Graceful shutdown: Signal.trap and ConditionVariable
Before the code: what graceful shutdown actually means
Picture a restaurant at closing time. The good waiter does not yell stop and dump plates on the floor. They stop seating new guests, let the people already eating finish, then turn the lights off. That is graceful shutdown for a server. When the operating system sends a stop signal, you want the server to stop accepting new connections, let the writes already in flight finish, then quit. In Ruby you wire this with Signal.trap, a ConditionVariable that broadcasts the shutdown, and an in-flight counter that knows when every handler has finished.
Stop accepting, drain, exit
Quiz: Quiz
Loading practice…
Why this is Ruby's step 9: Threads already give us per-connection isolation. The remaining production gap is shutdown - drain in-flight handlers, snapshot state, exit cleanly.
%w[INT TERM].each do |sig|
Signal.trap(sig) do
$shutdown_mu.synchronize { $shutdown = true; $shutdown_cv.broadcast }
begin; server.close; rescue; end
end
end
$shutdown_mu.synchronize { $shutdown_cv.wait($shutdown_mu) until $shutdown }
puts " [shutdown] signal received, draining..."Trap signals + close the listener + broadcast on the ConditionVariable.
deadline = Time.now + 10
$in_flight_mu.synchronize do
while $in_flight > 0 && Time.now < deadline
remaining = deadline - Time.now
$in_flight_cv.wait($in_flight_mu, remaining)
end
end
# then snapshot RDB
n = snapshot_atomic
puts " [shutdown] snapshot wrote #{n} keys"In-flight counter via ConditionVariable. Wait until 0 or deadline.
ConditionVariable is event-driven. Threads waiting on cv.wait are woken instantly by cv.broadcast. Polling would either delay shutdown (long sleep) or burn CPU (short sleep). The CV is the cheap, correct primitive.
Quiz: Quiz
Loading practice…