You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2019/04/11 21:32:07 UTC
[couchdb] branch prototype/rfc-001-revision-metadata-model updated:
First pass at replicated updates
This is an automated email from the ASF dual-hosted git repository.
davisp pushed a commit to branch prototype/rfc-001-revision-metadata-model
in repository https://gitbox.apache.org/repos/asf/couchdb.git
The following commit(s) were added to refs/heads/prototype/rfc-001-revision-metadata-model by this push:
new fbaa234 First pass at replicated updates
fbaa234 is described below
commit fbaa234d2a4b2b518c68cc54e06b388b00bd7de3
Author: Paul J. Davis <pa...@gmail.com>
AuthorDate: Thu Apr 11 16:34:22 2019 -0500
First pass at replicated updates
---
src/fabric/src/fabric2_db.erl | 92 +++++++++++++++++++++++++++++++++---------
src/fabric/src/fabric2_fdb.erl | 21 +++++++++-
2 files changed, 94 insertions(+), 19 deletions(-)
diff --git a/src/fabric/src/fabric2_db.erl b/src/fabric/src/fabric2_db.erl
index 0383a8e..0c164f8 100644
--- a/src/fabric/src/fabric2_db.erl
+++ b/src/fabric/src/fabric2_db.erl
@@ -737,9 +737,67 @@ update_doc_interactive(Db, Doc0, _Options) ->
{ok, {NewRevPos, NewRev}}.
-update_doc_replicated(_Db, _Doc, _Options) ->
- erlang:error(not_implemented).
+update_doc_replicated(Db, Doc0, Options) ->
+ #doc{
+ id = DocId,
+ deleted = Deleted,
+ revs = {RevPos, [Rev | RevPath]}
+ } = Doc0,
+
+ DocRevInfo = #{
+ winner => undefined,
+ deleted => Deleted,
+ rev_id => {RevPos, Rev},
+ rev_path => RevPath,
+ sequence => undefined,
+ branch_count => undefined
+ },
+
+ AllRevs = fabric2_fdb:get_all_revs(Db, DocId),
+
+ RevTree = lists:foldl(fun(RI, TreeAcc) ->
+ RIPath = fabric2_util:revinfo_to_path(RI),
+ {Merged, _} = couch_key_tree:merge(TreeAcc, RevTree),
+ Merged
+ end, [], AllRevs),
+
+ DocRevPath = fabric2_util:revinfo_to_path(RI),
+ {NewTree, Status} = couch_key_tree:merge(RevTree, DocRevPath),
+ if Status /= internal_node -> ok; true ->
+ % We already know this revision so nothing
+ % left to do.
+ ?RETURN({ok, []})
+ end,
+
+ AllLeafsFull = couch_key_tree:get_all_leafs_full(NewTree),
+ LeafPath = get_leaf_path(RevPos, Rev, AllLeafsFull),
+ PrevRevInfo = find_prev_revinfo(LeafPath),
+ Doc1 = prep_and_validate(Db, Doc0, PrevRevInfo),
+
+ % Possible winners are the previous winner and
+ % the new DocRevInfo
+ [#{winner := true} = Winner | _] = fabric2_util:sort_revinfos(AllRevInfos),
+ {NewWinner, NonWinner} = case PrevRevInfo == Winner of
+ true ->
+ {DocRevInfo, not_found};
+ false ->
+ [W, NW] = fabric2_util:sort_revinfos([Winner, DocRevInfo]),
+ {N, NW}
+ end,
+
+ ToUpdate = if NonWinner == not_found -> []; true -> [NonWinner] end,
+ ToRemove = if PrevRevInfo == not_found -> []; true -> [PrevRevInfo] end,
+
+ ok = fabric2_fdb:write_doc(
+ Db,
+ Doc3,
+ NewWinner,
+ Winner,
+ ToUpdate,
+ ToRemove
+ ),
+ {ok, {NewRevPos, NewRev}}.
prep_and_validate(Db, Doc, PrevRevInfo) ->
@@ -751,12 +809,12 @@ prep_and_validate(Db, Doc, PrevRevInfo) ->
end,
PrevDoc = case HasStubs orelse (HasVDUs and not IsDDoc) of
- true ->
+ true when PrevRevInfo /= not_found ->
case fabric2_fdb:get_doc_body(Db, Doc#doc.id, PrevRevInfo) of
#doc{} = Doc -> Doc;
{not_found, _} -> nil
end;
- false ->
+ _ ->
nil
end,
@@ -816,20 +874,18 @@ validate_ddoc(Db, DDoc) ->
end.
-%% find_prev_known_rev(_Pos, []) ->
-%% not_found;
-%% find_prev_known_rev(Pos, [{_Rev, #doc{}} | RestPath]) ->
-%% % doc records are skipped because these are the result
-%% % of replication sending us an update. We're only interested
-%% % in what we have locally since we're comparing attachment
-%% % stubs. The replicator should never do this because it
-%% % should only ever send leaves but the possibility exists
-%% % so we account for it.
-%% find_prev_known_rev(Pos - 1, RestPath);
-%% find_prev_known_rev(Pos, [{_Rev, ?REV_MISSING} | RestPath]) ->
-%% find_prev_known_rev(Pos - 1, RestPath);
-%% find_prev_known_rev(Pos, [{_Rev, #leaf{}} | _] = DocPath) ->
-%% {Pos, [Rev || {Rev, _Val} <- DocPath]}.
+get_leaf_path(Pos, Rev, [{Pos, [{Rev, _RevInfo} | LeafPath]} | _]) ->
+ LeafPath;
+get_doc_leaf(Pos, Rev, [_WrongLeaf | RestLeafs]) ->
+ get_doc_leaf(Pos, Rev, RestLeafs).
+
+
+find_prev_revinfo(_Pos, []) ->
+ not_found;
+find_prev_revinfo(Pos, [{_Rev, ?REV_MISSING} | RestPath]) ->
+ find_prev_known_rev(Pos - 1, RestPath);
+find_prev_revinfo(_Pos, [{_Rev, #{} = RevInfo} | _]) ->
+ RevInfo.
transactional(DbName, Options, Fun) ->
diff --git a/src/fabric/src/fabric2_fdb.erl b/src/fabric/src/fabric2_fdb.erl
index 13ea05b..71cbafe 100644
--- a/src/fabric/src/fabric2_fdb.erl
+++ b/src/fabric/src/fabric2_fdb.erl
@@ -31,6 +31,7 @@
get_stat/2,
incr_stat/3,
+ get_all_revs/2,
get_winning_revs/3,
get_non_deleted_rev/2,
@@ -355,7 +356,7 @@ incr_stat(#{} = Db, StatKey, Increment) when is_integer(Increment) ->
erlfdb:add(Tx, Key, Increment).
-get_winning_revs(#{} = Db, DocId, NumRevs) ->
+get_all_revs(#{} = Db, DocId) ->
?REQUIRE_CURRENT(Db),
#{
tx := Tx,
@@ -363,7 +364,25 @@ get_winning_revs(#{} = Db, DocId, NumRevs) ->
} = Db,
Prefix = erlfdb_tuple:pack({?DB_REVS, DocId}, DbPrefix),
+ Options = [{streaming_mode, want_all}],
+ Future = erlfdb:get_range_startswith(Tx, Prefix, Options)
+ lists:map(fun({K, V}) ->
+ Key = erlfdb_tuple:unpack(K, DbPrefix),
+ Val = erlfdb_tuple:unpack(V),
+ fdb_to_revinfo(Key, Val)
+ end, erlfdb:wait(Future)).
+
+
+get_winning_revs(#{} = Db, DocId, NumRevs) ->
+ ?REQUIRE_CURRENT(Db),
+ #{
+ tx := Tx,
+ db_prefix := DbPrefix
+ } = Db,
+
+ {StartKey, EndKey} = erlfdb_tuple:range({?DB_REVS, DocId}, DbPrefix),
Options = [{reverse, true}, {limit, NumRevs}],
+ Future = erlfdb:get_range(Tx, StartKey, EndKey, Options),
lists:map(fun({K, V}) ->
Key = erlfdb_tuple:unpack(K, DbPrefix),
Val = erlfdb_tuple:unpack(V),