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 2024/11/07 05:25:43 UTC
(couchdb) 01/01: Prevent a broken mem3 app from permanently failing replication
This is an automated email from the ASF dual-hosted git repository.
vatamane pushed a commit to branch prevent-broken-mem3-from-failing-replication-jobs
in repository https://gitbox.apache.org/repos/asf/couchdb.git
commit 8f52f2db87b6a1fec82c9177dddec0d4ef2c9775
Author: Nick Vatamaniuc <va...@gmail.com>
AuthorDate: Thu Nov 7 00:16:10 2024 -0500
Prevent a broken mem3 app from permanently failing replication
Previously, when mem3 failed such that it's shards ets table disappeared, while
the replicator was processing doc updates, it was possible for the
`couch_replicator_utils:owner/2` to raise `error:badarg` (passed through from
the ets:* calls) to be registered as a malformed (failed) replicator doc
update. After that, the replication job would be effectively stuck in the
`failed` state until the user took action to re-create it.
To fix it, change the doc update callback to on only fail jobs on explicit
replication doc parse failures (they have `throw({the bad_rep_doc, _})
exception pattern). In any other case let the exception crash the db changes
feed and let the supervisor restart it.
---
.../src/couch_replicator_doc_processor.erl | 5 +--
.../couch_replicator_scheduler_docs_tests.erl | 43 ++++++++++++++++++++++
2 files changed, 44 insertions(+), 4 deletions(-)
diff --git a/src/couch_replicator/src/couch_replicator_doc_processor.erl b/src/couch_replicator/src/couch_replicator_doc_processor.erl
index f22d8b354..57806a158 100644
--- a/src/couch_replicator/src/couch_replicator_doc_processor.erl
+++ b/src/couch_replicator/src/couch_replicator_doc_processor.erl
@@ -95,10 +95,7 @@ db_change(DbName, {ChangeProps} = Change, Server) ->
try
ok = process_change(DbName, Change)
catch
- exit:{Error, {gen_server, call, [?MODULE, _, _]}} ->
- ErrMsg = "~p exited ~p while processing change from db ~p",
- couch_log:error(ErrMsg, [?MODULE, Error, DbName]);
- _Tag:Error ->
+ throw:{bad_rep_doc, Error} ->
{RepProps} = get_json_value(doc, ChangeProps),
DocId = get_json_value(<<"_id">>, RepProps),
couch_replicator_docs:update_failed(DbName, DocId, Error)
diff --git a/src/couch_replicator/test/eunit/couch_replicator_scheduler_docs_tests.erl b/src/couch_replicator/test/eunit/couch_replicator_scheduler_docs_tests.erl
index acc6abb3a..7cf79da77 100644
--- a/src/couch_replicator/test/eunit/couch_replicator_scheduler_docs_tests.erl
+++ b/src/couch_replicator/test/eunit/couch_replicator_scheduler_docs_tests.erl
@@ -31,6 +31,7 @@ setup_replicator_db(Prefix) ->
RepDb.
setup_main_replicator_db() ->
+ meck:new(couch_replicator_utils, [passthrough]),
{Ctx, {Source, Target}} = couch_replicator_test_helper:test_setup(),
RepDb = setup_replicator_db(<<>>),
{Ctx, {RepDb, Source, Target}}.
@@ -47,6 +48,7 @@ setup_prefixed_replicator_db_with_update_docs_true() ->
{Ctx, {RepDb, Source, Target}}.
teardown({Ctx, {RepDb, Source, Target}}) ->
+ meck:unload(),
ok = fabric:delete_db(RepDb, [?ADMIN_CTX]),
config:delete("replicator", "update_docs", _Persist = false),
couch_replicator_test_helper:test_teardown({Ctx, {Source, Target}}).
@@ -117,6 +119,16 @@ t_replicator_doc_state_fields_update_docs_true_test_() ->
])
}.
+t_replicator_doc_doesnt_fail_when_mem3_breaks_test_() ->
+ {
+ setup,
+ fun setup_main_replicator_db/0,
+ fun teardown/1,
+ with([
+ ?TDEF(t_doc_doesnt_fail_when_mem3_breaks, 30)
+ ])
+ }.
+
t_scheduler_docs_total_rows({_Ctx, {RepDb, Source, Target}}) ->
SourceUrl = couch_replicator_test_helper:cluster_db_url(Source),
TargetUrl = couch_replicator_test_helper:cluster_db_url(Target),
@@ -239,6 +251,37 @@ t_doc_fields_are_ignored({_Ctx, {RepDb, Source, Target}}) ->
StateDoc
).
+t_doc_doesnt_fail_when_mem3_breaks({_Ctx, {RepDb, Source, Target}}) ->
+ SourceUrl = couch_replicator_test_helper:cluster_db_url(Source),
+ TargetUrl = couch_replicator_test_helper:cluster_db_url(Target),
+ RepDoc = #{
+ <<"source">> => SourceUrl,
+ <<"target">> => TargetUrl
+ },
+ % Make owner function fail once
+ meck:expect(
+ couch_replicator_utils,
+ owner,
+ 2,
+ meck:seq([
+ meck:raise(error, badarg),
+ meck:passthrough()
+ ])
+ ),
+ RepDocUrl = rep_doc_url(RepDb, ?docid()),
+ {201, _} = req(put, RepDocUrl, RepDoc),
+ StateDoc = test_util:wait(
+ fun() ->
+ case req(get, RepDocUrl) of
+ {200, #{<<"_replication_state">> := <<"completed">>} = StDoc} -> StDoc;
+ {_, #{}} -> wait
+ end
+ end,
+ 14000,
+ 1000
+ ),
+ ?assertMatch(#{<<"_replication_state">> := <<"completed">>}, StateDoc).
+
rep_doc_url(RepDb, DocId) when is_binary(RepDb) ->
rep_doc_url(binary_to_list(RepDb), DocId);
rep_doc_url(RepDb, DocId) when is_binary(DocId) ->