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

Instance Method Details

#check_timeoutsObject

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_slotsObject

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

Returns:

  • (Boolean)


34
35
36
# File 'lib/henitai/slot_scheduler/draining.rb', line 34

def draining_slots?
  slots.any? { |_, slot| slot.draining }
end

#interrupt_active_slotsObject



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