diff --git a/src/leveled_tictac.erl b/src/leveled_tictac.erl index b14642a..70fce1d 100644 --- a/src/leveled_tictac.erl +++ b/src/leveled_tictac.erl @@ -164,7 +164,7 @@ add_kv(TicTacTree, Key, Value, HashFun) -> -spec find_dirtyleaves(tictactree(), tictactree()) -> list(integer()). %% @doc -%% Returns a list of segment IDs that which hold differences between the state +%% Returns a list of segment IDs which hold differences between the state %% represented by the two trees. find_dirtyleaves(SrcTree, SnkTree) -> _Size = SrcTree#tictactree.size, @@ -226,14 +226,14 @@ merge_trees(TreeA, TreeB) -> MergeFun = fun(SQN, MergeL2) -> L2A = array:get(SQN, TreeA#tictactree.level2), - L2B = array:get(SQN, TreeA#tictactree.level2), + L2B = array:get(SQN, TreeB#tictactree.level2), NewLevel2 = merge_binaries(L2A, L2B), array:set(SQN, NewLevel2, MergeL2) end, NewLevel2 = lists:foldl(MergeFun, MergedTree#tictactree.level2, lists:seq(0, MergedTree#tictactree.width - 1)), - + MergedTree#tictactree{level1 = NewLevel1, level2 = NewLevel2}. get_segment(Key, SegmentCount) -> @@ -259,6 +259,25 @@ segmentcompare(SrcBin, SnkBin, Acc, Counter) -> segmentcompare(SrcTail, SnkTail, [Counter|Acc], Counter + 1) end. +checktree(TicTacTree) -> + checktree(TicTacTree#tictactree.level1, TicTacTree, 0). + +checktree(<<>>, TicTacTree, Counter) -> + true = TicTacTree#tictactree.width == Counter; +checktree(Level1Bin, TicTacTree, Counter) -> + BitSize = ?HASH_SIZE * 8, + <> = Level1Bin, + L2Bin = array:get(Counter, TicTacTree#tictactree.level2), + true = TopHash == segmentsummarise(L2Bin, 0), + checktree(Tail, TicTacTree, Counter + 1). + +segmentsummarise(<<>>, L1Acc) -> + L1Acc; +segmentsummarise(L2Bin, L1Acc) -> + BitSize = ?HASH_SIZE * 8, + <> = L2Bin, + segmentsummarise(Tail, L1Acc bxor TopHash). + merge_binaries(BinA, BinB) -> BitSize = bit_size(BinA), BitSize = bit_size(BinB), @@ -340,9 +359,11 @@ merge_test_withsize(Size) -> TreeZ4 = add_kv(TreeZ3, {o, "B1", "Y3", null}, {caine, 104}, HashFun), TreeM0 = merge_trees(TreeX4, TreeY4), + checktree(TreeM0), ?assertMatch(true, TreeM0#tictactree.level1 == TreeZ4#tictactree.level1), TreeM1 = merge_trees(TreeX3, TreeY4), + checktree(TreeM1), ?assertMatch(false, TreeM1#tictactree.level1 == TreeZ4#tictactree.level1). -endif. diff --git a/test/end_to_end/tictac_SUITE.erl b/test/end_to_end/tictac_SUITE.erl index 5544876..c0d66eb 100644 --- a/test/end_to_end/tictac_SUITE.erl +++ b/test/end_to_end/tictac_SUITE.erl @@ -8,7 +8,7 @@ ]). all() -> [ - many_put_compare, + % many_put_compare, index_compare ]. @@ -282,7 +282,7 @@ index_compare(_Config) -> TicTacTree1_P1, [TicTacTree1_P2, TicTacTree1_P3]), - % Go compare! Also heck we're not comparing empty trees + % Go compare! Also check we're not comparing empty trees DL1_0 = leveled_tictac:find_dirtyleaves(TicTacTree1_Full, TicTacTree1_Joined), EmptyTree = leveled_tictac:new_tree(empty, TreeSize), @@ -313,10 +313,57 @@ index_compare(_Config) -> TicTacTree2_P1, [TicTacTree2_P2, TicTacTree2_P3]), - % Go compare! Also heck we're not comparing empty trees + % Go compare! Also check we're not comparing empty trees DL2_0 = leveled_tictac:find_dirtyleaves(TicTacTree2_Full, TicTacTree2_Joined), EmptyTree = leveled_tictac:new_tree(empty, TreeSize), DL2_1 = leveled_tictac:find_dirtyleaves(TicTacTree2_Full, EmptyTree), true = DL2_0 == [], - true = length(DL2_1) > 100. + true = length(DL2_1) > 100, + + IdxSpc = {add, "idx2_bin", "zz999"}, + {TestObj, TestSpc} = testutil:generate_testobject(BucketBin, + term_to_binary("K9.Z"), + "Value1", + [IdxSpc], + [{"MDK1", "MDV1"}]), + ok = testutil:book_riakput(Book2C, TestObj, TestSpc), + testutil:check_forobject(Book2C, TestObj), + + TicTacTree3_Full = GetTicTacTreeFun(2, Book2A), + TicTacTree3_P1 = GetTicTacTreeFun(2, Book2B), + TicTacTree3_P2 = GetTicTacTreeFun(2, Book2C), + TicTacTree3_P3 = GetTicTacTreeFun(2, Book2D), + + % Merge the tree across the partitions + TicTacTree3_Joined = lists:foldl(fun leveled_tictac:merge_trees/2, + TicTacTree3_P1, + [TicTacTree3_P2, TicTacTree3_P3]), + + % Find all keys index, and then just the last key + IdxQ1 = {index_query, + BucketBin, + {fun testutil:foldkeysfun/3, []}, + {"idx2_bin", "zz", "zz|"}, + {true, undefined}}, + {async, IdxFolder1} = leveled_bookie:book_returnfolder(Book2C, IdxQ1), + true = IdxFolder1() >= 1, + + DL_3to2B = leveled_tictac:find_dirtyleaves(TicTacTree2_P1, + TicTacTree3_P1), + DL_3to2C = leveled_tictac:find_dirtyleaves(TicTacTree2_P2, + TicTacTree3_P2), + DL_3to2D = leveled_tictac:find_dirtyleaves(TicTacTree2_P3, + TicTacTree3_P3), + io:format("Individual tree comparison found dirty leaves of ~w ~w ~w~n", + [DL_3to2B, DL_3to2C, DL_3to2D]), + + true = length(DL_3to2B) == 0, + true = length(DL_3to2C) == 1, + true = length(DL_3to2D) == 0, + + % Go compare! Should find a difference in one leaf + DL3_0 = leveled_tictac:find_dirtyleaves(TicTacTree3_Full, + TicTacTree3_Joined), + io:format("Different leaves count ~w~n", [length(DL3_0)]), + true = length(DL3_0) == 1.