Module: Henitai::SlotScheduler::Draining
- Included in:
- Henitai::SlotScheduler
- Defined in:
- lib/henitai/slot_scheduler/draining.rb
Overview
Drain/timeout state machine for in-flight slots.
A slot enters the draining state either when it exceeds its timeout (#check_timeouts) or when a shutdown is requested (#interrupt_active_slots). #drain_draining_slots then performs the two-phase SIGTERM/SIGKILL broadcast and the final blocking reap.
Mixed into Henitai::SlotScheduler; relies on its slot table, integration, progress_reporter, wakeup and the ProcessControl primitives.
Instance Method Summary collapse
-
#check_timeouts ⇒ Object
Per-slot timeout check.
-
#drain_draining_slots ⇒ Object
Two-phase broadcast cleanup for all slots that are in draining state.
- #draining_slots? ⇒ Boolean
- #interrupt_active_slots ⇒ Object
Instance Method Details
#check_timeouts ⇒ Object
Per-slot timeout check. Must be called after reap_all_completed_children so that naturally-exited processes are already removed from slots.
17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/henitai/slot_scheduler/draining.rb', line 17 def check_timeouts now = monotonic_time slots.each_value do |slot| next if slot.draining next unless now >= slot.started_at_monotonic + slot.timeout # Final targeted reap: if the child already exited, classify it normally. pid, status = wnohang_reap(slot.pid) if pid complete_slot(pid, status) else slot.forced_outcome = :timeout slot.draining = true end end end |
#drain_draining_slots ⇒ Object
Two-phase broadcast cleanup for all slots that are in draining state.
Precision rule: before signalling, do one final WNOHANG pass to catch processes that exited naturally in the window between check_timeouts and now. If SIGTERM gets ESRCH, the process is already gone — we must not force-label those as :timeout.
44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/henitai/slot_scheduler/draining.rb', line 44 def drain_draining_slots draining = draining_slots return if draining.empty? prune_raced_draining_slots(draining) return if draining.empty? broadcast_term(draining) wait_for_drain_window signal_draining_slots(draining) reap_and_remove_draining(draining) end |
#draining_slots? ⇒ Boolean
34 35 36 |
# File 'lib/henitai/slot_scheduler/draining.rb', line 34 def draining_slots? slots.any? { |_, slot| slot.draining } end |
#interrupt_active_slots ⇒ Object
58 59 60 61 62 63 64 65 |
# File 'lib/henitai/slot_scheduler/draining.rb', line 58 def interrupt_active_slots slots.each_value do |slot| next if slot.draining slot.forced_outcome = :interrupted slot.draining = true end end |