allthingssecurity commented on code in PR #27340:
URL: https://github.com/apache/camel/pull/27340#discussion_r4181333212


##########
core/camel-core-processor/src/main/java/org/apache/camel/processor/errorhandler/RedeliveryErrorHandler.java:
##########
@@ -1214,10 +1214,46 @@ private void runAsynchronousRedelivery() {
                 LOG.trace("Scheduling redelivery task to run in {} millis for 
exchangeId: {}", redeliveryDelay,
                         exchange.getExchangeId());
             }
-            executorService.schedule(() -> 
reactiveExecutor.schedule(this::redeliver), redeliveryDelay,
+            if (currentRedeliveryPolicy.isAllowRedeliveryWhileStopping()) {
+                executorService.schedule(() -> 
reactiveExecutor.schedule(this::redeliver), redeliveryDelay,
+                        TimeUnit.MILLISECONDS);
+            } else {
+                // the redelivery is not allowed while stopping, so wake up 
every second to check whether we are
+                // preparing for shutdown, the same as the synchronous 
redelivery does (see sleep())
+                scheduleAsynchronousRedelivery(new StopWatch());
+            }
+        }
+
+        private void scheduleAsynchronousRedelivery(StopWatch watch) {
+            long delay = Math.max(0, Math.min(1000, redeliveryDelay - 
watch.taken()));

Review Comment:
   The extra cost applies only with `allowRedeliveryWhileStopping=false`; the 
default (`true`) still schedules one task
   for the full delay, as before. With `false`, each waiting asynchronous 
redelivery gets one scheduler task per second of
   its delay, and each task does one reactive-executor hop that checks 
`preparingShutdown` and the stopwatch. That is
   the same one-second cadence as the synchronous path's `sleep()`. The 
synchronous path also blocks a whole thread for
   the entire delay, which is much more expensive per waiting exchange. So with 
N exchanges waiting there are N short
   tasks per second on the error handler's scheduler. I think that is 
acceptable for an opt-in setting.
   
   I did consider tracking the scheduled futures and firing them from 
`prepareShutdown()`. I chose polling because:
   - it keeps the state inside the redelivery task: no shared registry of 
pending redeliveries, and no add/remove on a
     concurrent structure for every redelivery while nothing is shutting down;
   - firing from `prepareShutdown()` races with a task whose delay is just 
expiring. Each task would then need a
     compare-and-set so it either redelivers or is rejected exactly once, and 
the registry needs cleanup on every path
     (redelivered, rejected, scheduler rejected, error handler stopped);
   - polling gives the asynchronous path exactly the behaviour of the 
synchronous one (rejection within about one second
     of `prepareShutdown`), so the two paths stay identical.
   
   If you prefer the registry (rejection at once instead of within a second, 
and no wake-ups), I can switch to it.
   
   _Claude Code on behalf of allthingssecurity_
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to