From 538848259b9d80ccfca5ddb515ebc491f0489c56 Mon Sep 17 00:00:00 2001 From: Adam Kocoloski Date: Wed, 29 Dec 2010 03:01:12 +0000 Subject: Stem revision trees after merging a path, COUCHDB-968 git-svn-id: https://svn.apache.org/repos/asf/couchdb/branches/1.1.x@1053513 13f79535-47bb-0310-9956-ffa450edef68 --- src/couchdb/couch_db.erl | 3 ++- src/couchdb/couch_db_updater.erl | 10 +++++----- src/couchdb/couch_key_tree.erl | 8 +++++++- test/etap/060-kt-merging.t | 33 +++++++++++++++------------------ 4 files changed, 29 insertions(+), 25 deletions(-) diff --git a/src/couchdb/couch_db.erl b/src/couchdb/couch_db.erl index 4e945ad4..7522a189 100644 --- a/src/couchdb/couch_db.erl +++ b/src/couchdb/couch_db.erl @@ -555,7 +555,8 @@ prep_and_validate_replicated_updates(Db, [Bucket|RestBuckets], [OldInfo|RestOldI {ok, #full_doc_info{rev_tree=OldTree}} -> NewRevTree = lists:foldl( fun(NewDoc, AccTree) -> - {NewTree, _} = couch_key_tree:merge(AccTree, couch_db:doc_to_tree(NewDoc)), + {NewTree, _} = couch_key_tree:merge(AccTree, + couch_db:doc_to_tree(NewDoc), Db#db.revs_limit), NewTree end, OldTree, Bucket), diff --git a/src/couchdb/couch_db_updater.erl b/src/couchdb/couch_db_updater.erl index 2cce4b69..eb1a3edc 100644 --- a/src/couchdb/couch_db_updater.erl +++ b/src/couchdb/couch_db_updater.erl @@ -495,10 +495,11 @@ merge_rev_trees(Limit, MergeConflicts, [NewDocs|RestDocsList], [OldDocInfo|RestOldInfo], AccNewInfos, AccRemoveSeqs, AccSeq) -> #full_doc_info{id=Id,rev_tree=OldTree,deleted=OldDeleted,update_seq=OldSeq} = OldDocInfo, - NewRevTree0 = lists:foldl( + NewRevTree = lists:foldl( fun({Client, #doc{revs={Pos,[_Rev|PrevRevs]}}=NewDoc}, AccTree) -> if not MergeConflicts -> - case couch_key_tree:merge(AccTree, couch_db:doc_to_tree(NewDoc)) of + case couch_key_tree:merge(AccTree, couch_db:doc_to_tree(NewDoc), + Limit) of {_NewTree, conflicts} when (not OldDeleted) -> send_result(Client, Id, {Pos-1,PrevRevs}, conflict), AccTree; @@ -529,7 +530,7 @@ merge_rev_trees(Limit, MergeConflicts, [NewDocs|RestDocsList], NewDoc#doc{revs={OldPos, [OldRev]}}), NewDoc2 = NewDoc#doc{revs={OldPos + 1, [NewRevId, OldRev]}}, {NewTree2, _} = couch_key_tree:merge(AccTree, - couch_db:doc_to_tree(NewDoc2)), + couch_db:doc_to_tree(NewDoc2), Limit), % we changed the rev id, this tells the caller we did send_result(Client, Id, {Pos-1,PrevRevs}, {ok, {OldPos + 1, NewRevId}}), @@ -543,12 +544,11 @@ merge_rev_trees(Limit, MergeConflicts, [NewDocs|RestDocsList], end; true -> {NewTree, _} = couch_key_tree:merge(AccTree, - couch_db:doc_to_tree(NewDoc)), + couch_db:doc_to_tree(NewDoc), Limit), NewTree end end, OldTree, NewDocs), - NewRevTree = couch_key_tree:stem(NewRevTree0, Limit), if NewRevTree == OldTree -> % nothing changed merge_rev_trees(Limit, MergeConflicts, RestDocsList, RestOldInfo, diff --git a/src/couchdb/couch_key_tree.erl b/src/couchdb/couch_key_tree.erl index 09712adf..bc723cc2 100644 --- a/src/couchdb/couch_key_tree.erl +++ b/src/couchdb/couch_key_tree.erl @@ -12,7 +12,7 @@ -module(couch_key_tree). --export([merge/2, find_missing/2, get_key_leafs/2, get_full_key_paths/2, get/2]). +-export([merge/3, find_missing/2, get_key_leafs/2, get_full_key_paths/2, get/2]). -export([map/2, get_all_leafs/1, count_leafs/1, remove_leafs/2, get_all_leafs_full/1,stem/2,map_leafs/2]). @@ -23,6 +23,12 @@ % partial trees arranged by how much they are cut off. +-spec merge([path()], path(), pos_integer()) -> {[path()], + conflicts | no_conflicts}. +merge(Paths, Path, Depth) -> + {Merged, Conflicts} = merge(Paths, Path), + {stem(Merged, Depth), Conflicts}. + -spec merge([path()], path()) -> {[path()], conflicts | no_conflicts}. merge(Paths, Path) -> {ok, Merged, HasConflicts} = merge_one(Paths, Path, [], false), diff --git a/test/etap/060-kt-merging.t b/test/etap/060-kt-merging.t index 971e49bf..0e481a52 100755 --- a/test/etap/060-kt-merging.t +++ b/test/etap/060-kt-merging.t @@ -15,7 +15,7 @@ main(_) -> test_util:init_code_path(), - etap:plan(14), + etap:plan(12), case (catch test()) of ok -> etap:end_tests(); @@ -42,77 +42,74 @@ test() -> etap:is( {[One], no_conflicts}, - couch_key_tree:merge([], One), + couch_key_tree:merge([], One, 10), "The empty tree is the identity for merge." ), etap:is( {TwoSibs, no_conflicts}, - couch_key_tree:merge(TwoSibs, One), + couch_key_tree:merge(TwoSibs, One, 10), "Merging a prefix of a tree with the tree yields the tree." ), etap:is( {[One], no_conflicts}, - couch_key_tree:merge([One], One), + couch_key_tree:merge([One], One, 10), "Merging is reflexive." ), etap:is( {[TwoChild], no_conflicts}, - couch_key_tree:merge([TwoChild], TwoChild), + couch_key_tree:merge([TwoChild], TwoChild, 10), "Merging two children is still reflexive." ), etap:is( {[TwoChildSibs], no_conflicts}, - couch_key_tree:merge([TwoChildSibs], TwoChildSibs), + couch_key_tree:merge([TwoChildSibs], TwoChildSibs, 10), "Merging a tree to itself is itself."), etap:is( {[TwoChildSibs], no_conflicts}, - couch_key_tree:merge([TwoChildSibs], Stemmed1b), + couch_key_tree:merge([TwoChildSibs], Stemmed1b, 10), "Merging a tree with a stem." ), etap:is( {[TwoChildSibs2], no_conflicts}, - couch_key_tree:merge([TwoChildSibs2], Stemmed1bb), + couch_key_tree:merge([TwoChildSibs2], Stemmed1bb, 10), "Merging a stem at a deeper level." ), etap:is( {[TwoChild], no_conflicts}, - couch_key_tree:merge([TwoChild], Stemmed1aa), + couch_key_tree:merge([TwoChild], Stemmed1aa, 10), "Merging a single tree with a deeper stem." ), etap:is( {[TwoChild], no_conflicts}, - couch_key_tree:merge([TwoChild], Stemmed1a), + couch_key_tree:merge([TwoChild], Stemmed1a, 10), "Merging a larger stem." ), etap:is( {[Stemmed1a], no_conflicts}, - couch_key_tree:merge([Stemmed1a], Stemmed1aa), + couch_key_tree:merge([Stemmed1a], Stemmed1aa, 10), "More merging." ), Expect1 = [OneChild, Stemmed1aa], etap:is( {Expect1, conflicts}, - couch_key_tree:merge([OneChild], Stemmed1aa), + couch_key_tree:merge([OneChild], Stemmed1aa, 10), "Merging should create conflicts." ), - {MultiPaths, NoConflicts} = couch_key_tree:merge(Expect1, TwoChild), - etap:is(NoConflicts, no_conflicts, "Merge should have no conflicts."), - etap:is(length(MultiPaths), 2, "Should have two paths before stemming."), etap:is( - couch_key_tree:stem(MultiPaths, 10), - [TwoChild], - "Stemming should collapse the paths." + {[TwoChild], no_conflicts}, + couch_key_tree:merge(Expect1, TwoChild, 10), + "Merge should have no conflicts." ), ok. -- cgit v1.2.3