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),