Skip to content

fix checks never being delivered on installations with few checks - #193

Closed
nook24 wants to merge 3 commits into
sni:masterfrom
nook24:fix-lowvolume-flush
Closed

nook24 wants to merge 3 commits into
sni:masterfrom
nook24:fix-lowvolume-flush

Conversation

@nook24

@nook24 nook24 commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Follow-up to #192.
The problem

move_results_to_core() drains the async submit pipeline once per second with a non-blocking flush. That never gets a job out. gearman_client_run_tasks() on a client with GEARMAN_CLIENT_NON_BLOCKING returns GEARMAN_IO_WAIT and makes no progress unless the caller waits for the socket to become ready, so calling it again a second later just returns GEARMAN_IO_WAIT again. The pending count grows and nothing is ever delivered.

Where checks come in fast enough this is invisible, because the next submit drives the pipeline within microseconds. Where they do not, there is no next submit for minutes, and the periodic flush was supposed to be the thing that delivers them.

The 64-job GM_MAX_PENDING_SUBMITS buffer is not involved. It is only a ceiling that forces a blocking flush, never a threshold that has to be reached before anything is sent.

nook24 and others added 3 commits September 17, 2026 11:38
The periodic move_results_to_core() event drained the async submit pipeline
with a non-blocking flush. That never gets a job out: run_tasks() on a client
with GEARMAN_CLIENT_NON_BLOCKING returns GEARMAN_IO_WAIT and makes no progress
unless the caller waits for the socket to become ready, so calling it again a
second later just returns GEARMAN_IO_WAIT again. The pending count grows and
nothing is ever delivered.

Where checks come in fast enough this is invisible, because the next submit
drives the pipeline within microseconds. Where they do not, there is no next
submit for minutes and the periodic flush is the only thing that could deliver
them -- so the checks are simply never executed.

Reproduced with 10 services on a 60s interval against an unpatched naemon
1.5.2: over a 90 second window not one of them had last_check advance, and it
stayed 0 for the whole run. The same setup on v5.2.5 advances all ten. With
this change all ten advance again, at a check_latency of 0.5-0.9s, which is
what v5.2.5 shows as well.

Add gm_drain_submits() for that one job and call it from the event. It is a
blocking flush, bounded by gearman_connection_timeout (5s by default) -- the
same bound the per-job gearman_client_do_background() had before the pipeline
existed, and now once per second rather than once per check.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WY8mbGLNkt5eQfc57cTnZ5
Signed-off-by: nook24 <info@nook24.eu>
…g_data

Two defects found while chasing the delivery bug above.

gm_flush_submits() mapped everything but GEARMAN_SUCCESS and GEARMAN_IO_WAIT
to GM_ERROR. A blocking flush that has already handed its tasks over then
polls with nothing left to drive until the client timeout expires and returns
GEARMAN_TIMEOUT, so a submit that was in fact delivered got reported as
failed. In add_job_to_queue() that turns into rc = GEARMAN_ERRNO and the
caller cancels the check; handle_svc_check() unsets is_executing and returns
NEBERROR_CALLBACKCANCEL for a check that is already on its way.

The payload slot was taken with gm_pending_data[gm_pending_submits++] before
the flush that is supposed to free the slots. When a flush does not release
them -- which the GEARMAN_TIMEOUT case above makes reachable -- the next
submit writes one past the end of the array. Check the capacity before
queuing the task instead, drain synchronously if it is exhausted, and give up
on the job rather than corrupting memory.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WY8mbGLNkt5eQfc57cTnZ5
Signed-off-by: nook24 <info@nook24.eu>
The async tests force a blocking gm_flush_submits() right after submitting,
so they never exercise leaving a job queued for something else to deliver --
which is exactly what the periodic event in the NEB module does, and exactly
where the delivery bug was. Nothing in t/ would have caught it.

Add test_async_drain_only(): submit one job and let nothing but
gm_drain_submits() -- the same call the event makes -- deliver it, then
require the queue to drain. Switching that function back to a non-blocking
flush fails this test.

The existing blocking flush assertion was ok(TRUE, ...) with the real return
value only printed as a diagnostic, so it could not fail. It was in fact
returning GM_ERROR the whole time, which is what put the GEARMAN_TIMEOUT
handling in the previous commit on the trail. Assert the return value.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WY8mbGLNkt5eQfc57cTnZ5
Signed-off-by: nook24 <info@nook24.eu>
@sni

sni commented Sep 17, 2026

Copy link
Copy Markdown
Owner

thanks, i manually merged parts of this PR (b238340)

@sni sni closed this Sep 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants