Conversation
|
Important Draft PR not reviewedDraft PRs are not automatically reviewed by default.
To automatically review draft PRs, update your CodeRabbit configuration: reviews:
auto_review:
drafts: trueThanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
All reported issues were addressed across 1 file
Architecture diagram
sequenceDiagram
participant CD as Collector Daemon
participant DS as Data Store
participant WS as Worker Spawner
participant PSR as Parser
loop Worker Lifecycle
CD->>WS: Spawn plugin process
WS-->>CD: PID (unsafe.pi)
CD->>PSR: Start parsing data
PSR->>CD: retry request (transient condition)
alt Retry requested
CD->>WS: Request graceful exit (QUIT/SIGTERM)
WS-->>CD: worker_ret_code
alt worker_ret_code == -1 (forced kill)
CD->>CD: Disable retry?<br/>No - keep retries enabled
CD->>CD: Log warning<br/>Restart with longer backoff
CD->>CD: Sleep update_every * 10
else worker_ret_code != -1 (clean exit)
CD->>CD: Sleep update_every
end
CD->>WS: Respawn plugin
else No retry requested
alt worker_ret_code == 0 (success)
CD->>CD: Handle success
else worker_ret_code != 0 (unexpected exit)
CD->>CD: Handle failure/crash
end
end
end
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 10 files (changes from recent commits).
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
There was a problem hiding this comment.
All reported issues were addressed across 6 files (changes from recent commits).
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
f895fa4 to
365d7a9
Compare
|
@cubic-dev-ai review PR |
@stelfrag I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 9 files
Architecture diagram
sequenceDiagram
participant WD as Worker Thread
participant POP as spawn_popen
participant SS as Spawn Server
participant BP as Backend (POSIX/UV/Nofork/Windows)
participant CFG as Config Parser
Note over WD,CFG: Collector lifecycle & kill outcome tracking
WD->>POP: spawn_popen_kill_ex(pi, 3s, &outcome)
POP->>POP: Close file descriptors
POP->>SS: spawn_server_exec_kill_ex(si, 3s, &outcome)
alt Backend = POSIX/UV
SS->>BP: Send SIGTERM
BP-->>SS: Signal sent
SS->>BP: spawn_server_exec_timedwait(grace_ms)
alt Child exited within grace
BP-->>SS: EXITED
SS-->>POP: outcome = SPAWN_KILL_EXITED
else Child still running
SS->>BP: Send SIGKILL
BP-->>SS: Signal sent
SS->>BP: spawn_server_exec_timedwait(grace_ms)
alt Child confirmed gone
BP-->>SS: EXITED
SS-->>POP: outcome = SPAWN_KILL_FORCED_EXITED
else Still not confirmed
BP-->>SS: TIMEDWAIT_TIMEOUT
alt Deferred cleanup available
SS->>BP: Defer waitpid to reaper
SS-->>POP: outcome = SPAWN_KILL_UNKNOWN
else No reaper slot
SS->>BP: spawn_server_exec_wait (unbounded)
BP-->>SS: Child reaped
SS-->>POP: outcome = SPAWN_KILL_FORCED_EXITED
end
end
end
else Backend = Windows
SS->>BP: WaitForSingleObject(3s)
alt Child exited within grace
BP-->>SS: WAIT_OBJECT_0
SS->>BP: TerminateChildProcesses
SS->>BP: WaitForSingleObject(INFINITE)
alt Handle valid
BP-->>SS: WAIT_OBJECT_0
SS-->>POP: outcome = SPAWN_KILL_EXITED
else Handle failed
BP-->>SS: WAIT_FAILED
SS-->>POP: outcome = SPAWN_KILL_UNKNOWN
end
else Still running
SS->>BP: kill(SIGTERM) + TerminateProcess
SS->>BP: TerminateChildProcesses
SS->>BP: WaitForSingleObject(INFINITE)
alt Handle valid
BP-->>SS: WAIT_OBJECT_0
SS-->>POP: outcome = SPAWN_KILL_FORCED_EXITED
else Handle failed
BP-->>SS: WAIT_FAILED
SS-->>POP: outcome = SPAWN_KILL_UNKNOWN
end
end
else Backend = Nofork
SS->>BP: Close fds, signal child
alt Child exited
BP-->>SS: EXITED
SS-->>POP: outcome = SPAWN_KILL_EXITED
else Need SIGKILL
SS->>BP: Send SIGKILL
BA-->>SS: Signal sent
BP-->>SS: Timed wait
alt Confirmed gone
SS-->>POP: outcome = SPAWN_KILL_FORCED_EXITED
else Not confirmed
SS-->>POP: outcome = SPAWN_KILL_UNKNOWN
end
end
end
POP-->>WD: return code + outcome
WD->>WD: Analyze kill_outcome
alt SPAWN_KILL_UNKNOWN
Note over WD: Process may still be running<br/>Refusing restart
WD->>WD: Log error, disable plugin, set pid=0
else SPAWN_KILL_EXITED or FORCED_EXITED
alt retry == true (parser requested)
WD->>WD: Log why (clean exit vs forced)
WD->>WD: pluginsd_sleep_backoff(cd)
WD->>WD: Restart collector
else retry == false
alt worker_ret_code == 0
WD->>WD: Handle success, backoff before restart
else error
WD->>WD: Handle error, disable if needed, backoff
end
end
end
Note over CFG,WD: Config load with clamping
CFG->>CFG: GET update_every from config
CFG->>CFG: Clamp to [UPDATE_EVERY_MIN, UPDATE_EVERY_MAX]
alt Out of range
CFG->>CFG: Log warning, use clamped value
end
CFG-->>WD: update_every (now safe for backoff math)
Note over WD: Backoff sleep uses usec_t<br/>avoids int overflow on large values
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
A caller that must decide whether to start a replacement cannot learn what it
needs from the return code. spawn_popen_status_rc() maps every signal but
SIGTERM/SIGPIPE to -1, so our own SIGKILL, a crash and an OOM kill are one
value; on Windows a forced TerminateProcess() reports 0, like a clean exit. Two
backends also return without ever confirming the child died.
spawn_server_exec_kill_ex() and spawn_popen_kill_ex() report
SPAWN_KILL_{EXITED,FORCED_EXITED,UNKNOWN} alongside the status. The plain
functions remain as wrappers, so the other callers are untouched.
UNKNOWN is the one a caller must act on: the child may still be running, so a
replacement would write the same charts from two processes. It covers the posix
deferred-cleanup path, the nofork "child is left running" path, a libuv
uv_process_kill() failure, and a Windows handle that cannot be waited on -
WaitForSingleObject() returns WAIT_FAILED at once there, and the exit code that
follows was never set.
EXITED and FORCED_EXITED are a reporting distinction only. Neither says why the
child died: an operator and the OOM killer send the same SIGKILL we do, so no
test here can name a signal's sender, and the header says so to keep the next
caller from building policy on it.
When the parser asks for a retry it has hit a transient condition - a host being deleted, a receiver it could not evict, a host it could not create - and wants a fresh connection. The worker killed the plugin, read the -1 exit as abnormal and disabled the collector permanently, which nothing undoes short of an agent restart. The retry is now honoured indefinitely, with no attrition. The plugin is not what failed: go.d.plugin runs many independent jobs over one connection and the retry conditions are per-vnode, so one device whose receiver cannot be evicted aborts the session for all of them. Giving up on the plugin after a streak would stop every healthy job in the process over one stuck device. The warning is throttled per plugin, since the path has no end. It does not ask why the child died, because that has no reliable answer - the exit code collapses our SIGKILL, a crash and an OOM kill on POSIX, and inverts on Windows where a forced termination looks like a clean exit. The one outcome it acts on is SPAWN_KILL_UNKNOWN: not "why did it die" but "is it actually gone", which is answerable, and respawning beside a live process would give two writers on the same charts. update_every is clamped where it is read and pluginsd_sleep() counts in usec_t: the backoff could be zero for a legal "update every = 0", or overflow negative for a large one, and all three backoff sites now share one helper.
365d7a9 to
6073267
Compare
|
There was a problem hiding this comment.
🟡 Changes recommended
The critical libuv cleanup issue must be resolved before approval.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
This PR improves pluginsd process-kill reporting, retry handling, and backoff behavior. The described ACLK teardown changes are not included in the reviewed files.
Changes:
- Adds kill outcome reporting across spawn backends.
- Preserves parser-requested retries with longer backoff.
- Clamps plugin intervals and retains compatibility wrappers.
File summaries
| File | Description |
|---|---|
src/plugins.d/plugins_d.c |
Retry, backoff, and interval handling |
src/libnetdata/spawn_server/spawn_server.h |
Kill outcome API |
src/libnetdata/spawn_server/spawn_server_windows.c |
Windows kill reporting |
src/libnetdata/spawn_server/spawn_server_posix.c |
POSIX kill reporting |
src/libnetdata/spawn_server/spawn_server_nofork.c |
Nofork kill reporting |
src/libnetdata/spawn_server/spawn_server_libuv.c |
Libuv kill outcomes |
src/libnetdata/spawn_server/spawn_popen.h |
Public popen kill API |
src/libnetdata/spawn_server/spawn_popen.c |
Popen wrapper implementation |
src/libnetdata/spawn_server/spawn_library.c |
Compatibility wrapper |
Review findings:
- Critical (3 votes): Libuv termination can return without reclaiming the spawn instance or handle, potentially orphaning a live child.
- Nit (1 vote each): The PR description claims ACLK teardown work absent from the reviewed changes.
Review details
Suppressed comments (2)
src/libnetdata/spawn_server/spawn_server_libuv.c:364
- The PR description claims ACLK command-pool draining and host-teardown changes, but the PR's modified-file set contains no ACLK source; this diff only delivers the spawn/pluginsd changes. If ACLK safety is part of the acceptance criteria, it is missing from this PR; otherwise the description should be narrowed so it does not claim behavior this branch does not deliver.
if (uv_process_kill(&si->process, SIGTERM)) {
// Most often the child is already gone (ESRCH) and the exit callback simply has not been
src/plugins.d/plugins_d.c:184
- The PR description claims that queued ACLK commands are drained and the ACLK command pool is closed during host teardown, but the supplied changes contain no ACLK command-pool or host-lifetime code; this hunk only adds spawn kill outcome handling. If those guarantees are part of this PR, that implementation is missing, otherwise the description should be corrected.
SPAWN_KILL_OUTCOME kill_outcome;
int worker_ret_code = spawn_popen_kill_ex(cd->unsafe.pi, 3 * MSEC_PER_SEC, &kill_outcome);
- Files reviewed: 9/9 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| nd_log(NDLS_COLLECTORS, NDLP_ERR, "SPAWN PARENT: uv_process_kill() failed"); | ||
| return -1; |
Summary