Skip to content

fix: batch-processor infinite timer loop prevents graceful shutdown#13288

Merged
nic-6443 merged 2 commits into
apache:masterfrom
nic-6443:fix/batch-processor-shutdown-infinite-loop
Apr 27, 2026
Merged

fix: batch-processor infinite timer loop prevents graceful shutdown#13288
nic-6443 merged 2 commits into
apache:masterfrom
nic-6443:fix/batch-processor-shutdown-infinite-loop

Conversation

@nic-6443

@nic-6443 nic-6443 commented Apr 24, 2026

Copy link
Copy Markdown
Member

Problem

During nginx worker shutdown, flush_buffer enters an infinite timer loop:

  1. flush_buffer(premature=true) — premature flag ignored, time condition not met
  2. create_buffer_timer()timer_at(inactive_timeout) fails with "process exiting"
  3. Fallback timer_at(0, flush_buffer) succeeds → goto 1 (infinite loop)

This prevents graceful shutdown and causes [alert] logs flooding.

Additionally, schedule_func_exec has a bug where the fallback timer_at(0, execute_func, self) is missing the batch argument, causing a nil crash when retry_delay > 0 during shutdown.

Fix

1. flush_buffer: Add premature or exiting() to the existing time condition

if premature or exiting() or
   now() - self.last_entry_t >= self.inactive_timeout or ...
  • premature catches the case where the original pending timer is aborted during shutdown
  • exiting() (ngx.worker.exiting()) catches ALL shutdown cases, including zero-delay fallback timers from create_buffer_timer where premature=falsetimer_at(0, ...) during shutdown always delivers premature=false because the timer expires immediately and is never found by abort_pending_timers

2. schedule_func_exec: Pass batch in the fallback timer

local ok, err = timer_at(0, execute_func, self, batch)

Test changes

  • t/utils/batch-processor.t: Added TEST 13 — regression test that hangs if the infinite loop exists (3600s timeouts ensure the time condition is never met normally)
  • t/plugin/prometheus2.t: Added --- error_log assertions on TEST 25, 28, 31, 37 for specific batch-processor shutdown errors (batch-processor now correctly flushes during shutdown, producing expected connection-refused errors when upstream test servers are already stopped)
  • t/plugin/prometheus3.t: Added --- error_log assertion on TEST 5 for batch-processor shutdown errors (error-log-logger flush during HUP worker shutdown)

Copilot AI review requested due to automatic review settings April 24, 2026 02:48
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. bug Something isn't working labels Apr 24, 2026
@nic-6443
nic-6443 force-pushed the fix/batch-processor-shutdown-infinite-loop branch from 6c9dd9a to 622e955 Compare April 24, 2026 02:51

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes a shutdown regression in the batch processor where timer fallback behavior could create a zero-delay timer loop, preventing Nginx workers from exiting gracefully.

Changes:

  • Pass batch into the execute_func timer fallback when ngx.timer.at returns "process exiting".
  • Update flush_buffer to flush immediately when called with premature=true to avoid extending timers during shutdown.
  • Add Test::Nginx coverage to verify buffered entries are flushed during worker shutdown and the suite continues (no hang).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
apisix/utils/batch-processor.lua Adjusts shutdown behavior for buffer flushing and fixes missing batch arg in timer fallback.
t/utils/batch-processor.t Adds regression tests covering shutdown flush and ensuring the worker exits cleanly.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread t/utils/batch-processor.t Outdated
Comment thread apisix/utils/batch-processor.lua Outdated
@nic-6443
nic-6443 force-pushed the fix/batch-processor-shutdown-infinite-loop branch 2 times, most recently from fe3fa56 to ee40382 Compare April 24, 2026 03:18
@dosubot dosubot Bot added size:M This PR changes 30-99 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Apr 24, 2026
@nic-6443
nic-6443 force-pushed the fix/batch-processor-shutdown-infinite-loop branch 6 times, most recently from 1af640f to 8a06608 Compare April 25, 2026 08:59
During worker shutdown, flush_buffer is called with premature=true but
never checked it, causing an infinite zero-delay timer loop that prevents
the worker from exiting.

Fix by adding premature to the flush condition so shutdown triggers the
same process_buffer() path. Also fix schedule_func_exec to pass the
batch arg in its 'process exiting' fallback (previously nil, would crash
if a retry timer fires during shutdown with retry_delay > 0).
@nic-6443
nic-6443 force-pushed the fix/batch-processor-shutdown-infinite-loop branch from 8a06608 to 1b74a41 Compare April 25, 2026 11:32
…shutdown

Use short inactive_timeout (1s) with continuous entry pushing instead of
long timeouts (3600s). This creates the race condition where timers
naturally expire during shutdown with premature=false, which is the
actual bug path that ngx.worker.exiting() guards against.
@nic-6443
nic-6443 merged commit 4d81068 into apache:master Apr 27, 2026
20 of 21 checks passed
@nic-6443
nic-6443 deleted the fix/batch-processor-shutdown-infinite-loop branch April 27, 2026 02:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working size:M This PR changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants