Class: Wurk::Swarm::OrphanGuard
- Inherits:
-
Object
- Object
- Wurk::Swarm::OrphanGuard
- Defined in:
- lib/wurk/swarm/orphan_guard.rb
Overview
Orphan protection for a swarm child. A SIGKILL'd (or crashed, or OOM'd) supervisor leaves its children with no parent — they keep fetching forever, and the next redeploy that boots a fresh supervisor then runs doubled concurrency against the same queues. This arms two independent mechanisms so a child self-terminates the moment it is orphaned:
* Linux: PR_SET_PDEATHSIG delivers SIGTERM the instant the forking
thread dies (kernel-level, zero latency). It is set right after the
fork; the fork race — the parent can die in the window before the
syscall lands, so the signal is never queued — is closed by the
watchdog's immediate getppid check below.
* Everywhere: a watchdog thread compares getppid against the PID the
parent captured *before* forking (race-free — never the reaper PID).
On its first tick, and every `interval` seconds after, a mismatch
means the parent is gone. This is the only mechanism on non-Linux
(JRuby, macOS dev) and a backstop for pdeathsig's per-thread caveat.
Both funnel through the child's own SIGTERM handler (Process.kill self), so orphan death is an ordinary graceful drain — in-flight jobs finish and the private list is requeued — not a hard kill.
Constant Summary collapse
- WATCHDOG_INTERVAL =
5- PR_SET_PDEATHSIG =
<linux/prctl.h>: PR_SET_PDEATHSIG is option 1.
1
Class Method Summary collapse
-
.pdeathsig_supported? ⇒ Boolean
pdeathsig via libc/prctl is Linux-only.
Instance Method Summary collapse
-
#arm ⇒ Object
Arm both mechanisms.
-
#initialize(parent_pid, logger:, interval: WATCHDOG_INTERVAL, pdeathsig: pdeathsig_supported?, , on_orphan: nil) ⇒ OrphanGuard
constructor
on_orphanis injectable so unit tests can observe the trip without actually signalling the test process. - #orphaned? ⇒ Boolean
Constructor Details
#initialize(parent_pid, logger:, interval: WATCHDOG_INTERVAL, pdeathsig: pdeathsig_supported?, , on_orphan: nil) ⇒ OrphanGuard
on_orphan is injectable so unit tests can observe the trip without
actually signalling the test process. Production leaves it nil and
self-TERMs, routing through the child's normal graceful-drain handler.
39 40 41 42 43 44 45 46 |
# File 'lib/wurk/swarm/orphan_guard.rb', line 39 def initialize(parent_pid, logger:, interval: WATCHDOG_INTERVAL, pdeathsig: pdeathsig_supported?, on_orphan: nil) @parent_pid = parent_pid @logger = logger @interval = interval @pdeathsig = pdeathsig @on_orphan = on_orphan || -> { ::Process.kill('TERM', ::Process.pid) } end |
Class Method Details
.pdeathsig_supported? ⇒ Boolean
pdeathsig via libc/prctl is Linux-only. Off elsewhere; the watchdog thread covers those platforms on its own.
32 33 34 |
# File 'lib/wurk/swarm/orphan_guard.rb', line 32 def self.pdeathsig_supported? ::RbConfig::CONFIG['host_os'].include?('linux') end |
Instance Method Details
#arm ⇒ Object
Arm both mechanisms. Returns the watchdog thread so the caller can retain it (otherwise GC could reap it).
50 51 52 53 |
# File 'lib/wurk/swarm/orphan_guard.rb', line 50 def arm set_pdeathsig start_watchdog end |
#orphaned? ⇒ Boolean
55 56 57 |
# File 'lib/wurk/swarm/orphan_guard.rb', line 55 def orphaned? ::Process.ppid != @parent_pid end |