You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by va...@apache.org on 2026/10/04 17:00:51 UTC

(couchdb) 04/12: Fixup nouveau out-of-order purge PR

This is an automated email from the ASF dual-hosted git repository.

nickva pushed a commit to branch 3.5.x-a-few-more-fixes
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit 1d6fcc14a37d51ee2fefafa32f99b2815df194eb
Author: Nick Vatamaniuc <va...@gmail.com>
AuthorDate: Fri Oct 2 14:41:19 2026 -0400

    Fixup nouveau out-of-order purge PR
    
    Implement the fixups from https://github.com/apache/couchdb/pull/6138
    
    (cherry picked from commit dd14b1ade8d5fccecb074cc116391a6a47a5f953)
---
 src/nouveau/src/nouveau_index_updater.erl | 43 ++++++++++++++++++++-----------
 test/elixir/test/nouveau_test.exs         | 13 ++++++++--
 2 files changed, 39 insertions(+), 17 deletions(-)

diff --git a/src/nouveau/src/nouveau_index_updater.erl b/src/nouveau/src/nouveau_index_updater.erl
index 9f9de5844..5b03f35ab 100644
--- a/src/nouveau/src/nouveau_index_updater.erl
+++ b/src/nouveau/src/nouveau_index_updater.erl
@@ -244,29 +244,42 @@ purge_index(Db, Index, StartUpdateSeq, #purge_acc{} = PurgeAcc0) ->
             case couch_db:get_full_doc_info(Db, Id) of
                 not_found ->
                     queue_purge(Id, PurgeSeq, PurgeAcc1);
-                #full_doc_info{} = FDI ->
-                    #doc_info{high_seq = Seq} = couch_doc:to_doc_info(FDI),
+                #full_doc_info{update_seq = Seq} ->
+                    %% FDI still exists. If it changed after index last saw it
+                    %% (eg. doc was re-created, or a leaf was purged and all
+                    %% leafs were re-labeled with new sequences), remove it and
+                    %% let the changes phase index the winner (if there is a
+                    %% new one). If the FDI didn't change, then the index
+                    %% already has its current winner (this may be when purging
+                    %% a non-leaf or non-existent revision).
                     case Seq > StartUpdateSeq of
-                        true ->
-                            queue_purge(Id, PurgeSeq, PurgeAcc1);
-                        false ->
-                            PurgeAcc1
+                        true -> queue_purge(Id, PurgeSeq, PurgeAcc1);
+                        false -> PurgeAcc1
                     end
             end,
         update_task(1),
-        maybe_flush_batch(PurgeAcc2)
+        case maybe_flush_batch(PurgeAcc2) of
+            {ok, PurgeAcc3} ->
+                {ok, PurgeAcc3};
+            {error, Reason} ->
+                exit({error, Reason})
+        end
     end,
 
-    {ok, #purge_acc{} = PurgeAcc3} = couch_db:fold_purge_infos(
+    {ok, #purge_acc{} = PurgeAcc4} = couch_db:fold_purge_infos(
         Db, PurgeAcc0#purge_acc.index_purge_seq, FoldFun, PurgeAcc0, []
     ),
-    {ok, PurgeAcc4} = flush_batch(PurgeAcc3),
-    DbPurgeSeq = couch_db:get_purge_seq(Db),
-    ok = nouveau_api:set_purge_seq(
-        Index, PurgeAcc4#purge_acc.index_purge_seq, DbPurgeSeq
-    ),
-    update_local_doc(Db, Index, DbPurgeSeq),
-    ok.
+    case flush_batch(PurgeAcc4) of
+        {ok, PurgeAcc5} ->
+            DbPurgeSeq = couch_db:get_purge_seq(Db),
+            ok = nouveau_api:set_purge_seq(
+                Index, PurgeAcc5#purge_acc.index_purge_seq, DbPurgeSeq
+            ),
+            update_local_doc(Db, Index, DbPurgeSeq),
+            ok;
+        {error, Reason} ->
+            exit({error, Reason})
+    end.
 
 queue_purge(Id, PurgeSeq, #purge_acc{} = PurgeAcc) ->
     Item = nouveau_api:make_purge(
diff --git a/test/elixir/test/nouveau_test.exs b/test/elixir/test/nouveau_test.exs
index 9a589602b..8f52dba5f 100644
--- a/test/elixir/test/nouveau_test.exs
+++ b/test/elixir/test/nouveau_test.exs
@@ -721,14 +721,14 @@ defmodule NouveauTest do
     assert get_total_hits(resp) == 4
 
     # purge two docs in separate requests
-    doc_a = (Enum.at(create_resp.body, 0))
+    doc_a = Enum.at(create_resp.body, 0)
     resp =
       Couch.post("/#{db_name}/_purge",
         body: %{doc_a["id"] => [doc_a["rev"]]}
       )
     assert_status_code(resp, 201)
 
-    doc_b = (Enum.at(create_resp.body, 1))
+    doc_b = Enum.at(create_resp.body, 1)
     resp =
       Couch.post("/#{db_name}/_purge",
         body: %{doc_b["id"] => [doc_b["rev"]]}
@@ -751,6 +751,15 @@ defmodule NouveauTest do
     assert_status_code(resp, 200)
     assert get_total_hits(resp) == 5
 
+    # b (doc3) should have "foo" instead of "bar"
+    resp = Couch.get(search_url, query: %{q: "foo:bar"})
+    assert_status_code(resp, 200)
+    assert get_total_hits(resp) == 0
+
+    resp = Couch.get(search_url, query: %{q: "foo:foo"})
+    assert_status_code(resp, 200)
+    assert get_total_hits(resp) == 2
+
     # confirm nouveau responds and has correct sequences
     resp = Couch.get("/#{db_name}/_design/foo/_nouveau_info/bar")
     assert_status_code(resp, 200)