diff options
author | Nick Vatamaniuc <vatamane@apache.org> | 2019-09-24 02:25:39 -0400 |
---|---|---|
committer | Nick Vatamaniuc <vatamane@apache.org> | 2019-09-24 02:25:39 -0400 |
commit | 8aa5798490edb34f1f35e05e9fd78eec39aac61c (patch) | |
tree | 303bff62cf9f8b8ae575d46c32ea96bbb4a3f454 | |
parent | 01a9228c91a42b5a99bd1478a9ad8c578b09b70f (diff) | |
download | couchdb-fix-doc-counts-for-replicated-deletions.tar.gz |
Fix doc counts for replicated deletionsfix-doc-counts-for-replicated-deletions
Do not decrement `doc_count` stat if document was previosuly missing, or if it
was already deleted.
Deleted documents could be brought in by replication. In that case, if there
were more replicated documents than the current `doc_count`, the `doc_count`
could even underflow the 64 bit unsigned integer range and end up somewhere in
the vicinity of 2^64. The counter, of course, would still be incorrect even if
it didn't underflow, the huge value would just make the issue more visible.
-rw-r--r-- | src/fabric/src/fabric2_fdb.erl | 8 | ||||
-rw-r--r-- | src/fabric/test/fabric2_doc_count_tests.erl | 25 |
2 files changed, 31 insertions, 2 deletions
diff --git a/src/fabric/src/fabric2_fdb.erl b/src/fabric/src/fabric2_fdb.erl index ccfeb3c06..ab8f32f94 100644 --- a/src/fabric/src/fabric2_fdb.erl +++ b/src/fabric/src/fabric2_fdb.erl @@ -556,7 +556,7 @@ write_doc(#{} = Db0, Doc, NewWinner0, OldWinner, ToUpdate, ToRemove) -> {not_found, #{deleted := false}} -> created; {not_found, #{deleted := true}} -> - deleted; + replicate_deleted; {#{deleted := true}, #{deleted := false}} -> recreated; {#{deleted := false}, #{deleted := false}} -> @@ -564,10 +564,12 @@ write_doc(#{} = Db0, Doc, NewWinner0, OldWinner, ToUpdate, ToRemove) -> {#{deleted := false}, #{deleted := true}} -> deleted; {#{deleted := true}, #{deleted := true}} -> - deleted + replicate_deleted end, case UpdateStatus of + replicate_deleted -> + ok; deleted -> ADKey = erlfdb_tuple:pack({?DB_ALL_DOCS, DocId}, DbPrefix), ok = erlfdb:clear(Tx, ADKey); @@ -614,6 +616,8 @@ write_doc(#{} = Db0, Doc, NewWinner0, OldWinner, ToUpdate, ToRemove) -> end, incr_stat(Db, <<"doc_count">>, 1), incr_stat(Db, <<"doc_del_count">>, -1); + replicate_deleted -> + incr_stat(Db, <<"doc_del_count">>, 1); deleted -> if not IsDDoc -> ok; true -> incr_stat(Db, <<"doc_design_count">>, -1) diff --git a/src/fabric/test/fabric2_doc_count_tests.erl b/src/fabric/test/fabric2_doc_count_tests.erl index 37d08404d..915144ab1 100644 --- a/src/fabric/test/fabric2_doc_count_tests.erl +++ b/src/fabric/test/fabric2_doc_count_tests.erl @@ -30,6 +30,7 @@ doc_count_test_() -> fun cleanup/1, {with, [ fun normal_docs/1, + fun replicated_docs/1, fun design_docs/1, fun local_docs/1 ]} @@ -109,6 +110,30 @@ normal_docs({Db, _}) -> ). +replicated_docs({Db, _}) -> + {DocCount, DelDocCount, DDocCount, LDocCount} = get_doc_counts(Db), + + Opts = [replicated_changes], + {R1, R2, R3} = {<<"r1">>, <<"r2">>, <<"r3">>}, + + % First case is a simple replicated update + Doc1 = #doc{id = <<"rd1">>, revs = {1, [R1]}}, + {ok, {1, R1}} = fabric2_db:update_doc(Db, Doc1, Opts), + check_doc_counts(Db, DocCount + 1, DelDocCount, DDocCount, LDocCount), + + % Here a deleted document is replicated into the db. Doc count should not + % change, only deleted doc count. + Doc2 = #doc{id = <<"rd2">>, revs = {1, [R2]}, deleted = true}, + {ok, {1, R2}} = fabric2_db:update_doc(Db, Doc2, Opts), + check_doc_counts(Db, DocCount + 1, DelDocCount + 1, DDocCount, LDocCount), + + % Here we extended the deleted document's rev path but keep it deleted. + % Again, only deleted doc count should be bumped. + Doc3 = #doc{id = <<"rd2">>, revs = {2, [R3, R2]}, deleted = true}, + {ok, {2, R3}} = fabric2_db:update_doc(Db, Doc3, Opts), + check_doc_counts(Db, DocCount + 1, DelDocCount + 2 , DDocCount, LDocCount). + + design_docs({Db, _}) -> {DocCount, DelDocCount, DDocCount, LDocCount} = get_doc_counts(Db), |