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

vatamane pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit c6c5ba465b8a95f17d62a3037b265beca90587d7
Author: Nick Vatamaniuc <[email protected]>
AuthorDate: Fri Sep 26 17:58:47 2025 -0400

    Reschedule scanner plugins if they return skip on start or resume
    
    Previously, if a plugin returned `skip` on `start/2` or `resume/2`, then the
    plugin would stop and it wouldn't be scheduler to run again, even if it has 
a
    "repeat" time interval set.
    
    The new behavior on `skip` is to schedule the plugin to run again if plugin 
is
    configured to repeat it's execution. This way, a plugin may react to a
    temporary condition (feature disabled, some missing or crashed dependency) 
with
    a `skip`, and then it will get a chance to retry later hoping the conditions
    will change and it will continue executing.
---
 src/couch_scanner/src/couch_scanner_plugin.erl     | 29 ++++++++++++++++------
 .../test/eunit/couch_scanner_test.erl              | 20 +++++++++++++++
 2 files changed, 41 insertions(+), 8 deletions(-)

diff --git a/src/couch_scanner/src/couch_scanner_plugin.erl 
b/src/couch_scanner/src/couch_scanner_plugin.erl
index 1ea6d57e4..0454a6d59 100644
--- a/src/couch_scanner/src/couch_scanner_plugin.erl
+++ b/src/couch_scanner/src/couch_scanner_plugin.erl
@@ -246,7 +246,7 @@ init_from_checkpoint(#st{} = St) ->
             <<"start_sec">> := StartSec
         } ->
             Now = tsec(),
-            PSt = resume_callback(Cbks, SId, EJsonPSt),
+            PSt = resume_callback(Mod, Cbks, SId, EJsonPSt),
             St#st{
                 pst = PSt,
                 cursor = Cur,
@@ -502,20 +502,33 @@ start_callback(Mod, Cbks, Now, ScanId, LastStartSec, #{} 
= EJson) when
         TSec when is_integer(TSec), TSec =< Now ->
             #{start := StartCbk} = Cbks,
             case StartCbk(ScanId, EJson) of
-                {ok, PSt} -> PSt;
-                skip -> exit_resched(infinity);
-                reset -> exit_resched(reset)
+                {ok, PSt} ->
+                    PSt;
+                skip ->
+                    % If plugin skipped start, count this as an attempt and
+                    % reschedule to possibly retry in the future.
+                    SkipReschedTSec = schedule_time(Mod, Now, Now),
+                    exit_resched(SkipReschedTSec);
+                reset ->
+                    exit_resched(reset)
             end;
         TSec when is_integer(TSec), TSec > Now ->
             exit_resched(TSec)
     end.
 
-resume_callback(#{} = Cbks, SId, #{} = EJsonPSt) when is_binary(SId) ->
+resume_callback(Mod, #{} = Cbks, SId, #{} = EJsonPSt) when is_binary(SId) ->
     #{resume := ResumeCbk} = Cbks,
     case ResumeCbk(SId, EJsonPSt) of
-        {ok, PSt} -> PSt;
-        skip -> exit_resched(infinity);
-        reset -> exit_resched(reset)
+        {ok, PSt} ->
+            PSt;
+        skip ->
+            % If plugin skipped resume, count this as an attempt and
+            % reschedule to possibly retry in the future
+            Now = tsec(),
+            SkipReschedTSec = schedule_time(Mod, Now, Now),
+            exit_resched(SkipReschedTSec);
+        reset ->
+            exit_resched(reset)
     end.
 
 db_opened_callback(#st{pst = PSt, callbacks = Cbks, db = Db} = St) ->
diff --git a/src/couch_scanner/test/eunit/couch_scanner_test.erl 
b/src/couch_scanner/test/eunit/couch_scanner_test.erl
index 1fe2e157d..d288dc50a 100644
--- a/src/couch_scanner/test/eunit/couch_scanner_test.erl
+++ b/src/couch_scanner/test/eunit/couch_scanner_test.erl
@@ -29,6 +29,7 @@ couch_scanner_test_() ->
             ?TDEF_FE(t_conflict_finder_works, 30),
             ?TDEF_FE(t_config_skips, 10),
             ?TDEF_FE(t_resume_after_error, 10),
+            ?TDEF_FE(t_resume_after_skip, 10),
             ?TDEF_FE(t_reset, 10),
             ?TDEF_FE(t_schedule_repeat, 10),
             ?TDEF_FE(t_schedule_after, 15)
@@ -272,6 +273,25 @@ t_resume_after_error(_) ->
     config:set("couch_scanner_plugins", Plugin, "true", false),
     meck:wait(?FIND_PLUGIN, resume, 2, 10000).
 
+t_resume_after_skip(_) ->
+    meck:reset(?FIND_PLUGIN),
+    meck:expect(
+        ?FIND_PLUGIN,
+        start,
+        2,
+        meck:seq([
+            skip,
+            meck:passthrough()
+        ])
+    ),
+    Plugin = atom_to_list(?FIND_PLUGIN),
+    config:set("couch_scanner", "min_penalty_sec", "1", false),
+    config:set("couch_scanner", "interval_sec", "1", false),
+    config:set(Plugin, "repeat", "2_sec", false),
+    couch_scanner:resume(),
+    config:set("couch_scanner_plugins", Plugin, "true", false),
+    meck:wait(?FIND_PLUGIN, complete, 1, 10000).
+
 t_reset(_) ->
     meck:reset(?FIND_PLUGIN),
     meck:expect(

Reply via email to