INITIALIZE · SERVE · DRAIN
Graceful shutdown: stop admitting, then drain
Follow worker lifecycles and the invocations they own. Test readiness, shutdown deadlines and the point at which results commit.
Scroll the worker lanes horizontally. The worker inspector, sums and event trace below provide the same states as text.
Worker and invocation inspector
Committed-output fixture
Completed sums remain visible after a worker exits. This represents an external result store; browser reload does not persist it. Killed invocations have no committed output.
Recent lifecycle decisions
Your experiment
The opening scenario kills unfinished work. Increase the grace period to twenty seconds and keep the handler enabled: accepted work can finish, but new work has nowhere to go after draining starts. Enable replacement startup and inspect the transition gap.
Exactly what runs
Requests arrive from t = 1 s through t ≤ 40 s, at the selected interval. Each carries [request number, 1, 2]. The host assigns a free slot and models three equal-duration stages: prepare, model, tool. No AI is called. At completion, actual JavaScript addition computes and commits the sum. Each invocation owns one slot until it completes or its process is killed.
Ready-only routing picks the least-loaded ready worker, with worker ID as a deterministic tie-break. Without that guard it also considers starting and draining workers; those reject the request. There is no admission queue, retry, autoscaler or state transfer. A replacement is a single scheduled boot, not automatic recovery based on load. Startup failure affects only worker 1.
At the signal time, an enabled handler withdraws readiness and drains existing work; an ignored signal leaves the application ready until forced stop. A drained process exits immediately. At a shared timestamp, completions happen before readiness changes, termination and new admission; completion exactly at a shutdown deadline therefore wins in this model. Time is evaluated in 0.1 s steps.
Fixture gate, not a certification
PASS requires zero killed or still-running invocations and a final rejection percentage no greater than the selected allowance. It says nothing about authentication, model quality, real provider SLOs, latency percentiles or production correctness.
Offline, deliberately bounded lifecycle model. It does not execute an ADK Runner, Java process, container, operating-system signal or cloud deployment. Model/tool stages illustrate ownership and timing, not provider computation. The configurable grace period and router are teaching controls, not promises about any cloud runtime. Cloud Run’s documented termination contract and ADK’s invocation event loop must be consulted separately for real implementations. Cloud Run container contract · ADK runtime event loop.
One worker, four slots, a signal at thirty seconds
A single worker boots in four seconds and then admits requests that arrive every two seconds from one second onward. Each invocation needs twenty seconds and occupies one of four slots, so most arrivals are rejected because all slots are busy or the worker is not ready yet. At thirty seconds the worker receives SIGTERM. With Handle SIGTERM and stop admission enabled, it withdraws readiness at once and begins to drain. The grace period then decides whether work in flight may finish.
A zero grace period kills committed-looking work
With the opening Grace period of zero, the forced stop happens at the signal. Of seven admitted invocations, three have completed, and four, request-6, request-13, request-14 and request-15, are killed before they commit. Raise the grace period to ten: the worker exits at forty seconds, request-6 completes, and three invocations are still killed. At twenty seconds all seven admitted invocations complete and the worker exits at forty-nine seconds with zero killed. Draining needs a grace period at least as long as the longest invocation still running.
Stopping admission is the other half
Keep grace at twenty and clear Handle SIGTERM and stop admission. The worker keeps accepting work after the signal, admits request-16, and the forced stop at fifty seconds kills it. Eight invocations are admitted and seven complete. Restore the handler and enable Boot one replacement at signal time: the replacement becomes ready four seconds after the signal and admits three more invocations, so ten complete. Route only to ready workers prevents sending work to a worker that is starting or draining.
Reach the fixture gate
The gate requires zero killed invocations, nothing still running and a rejected share at or below Fixture rejection allowance. The opening run fails with sixty-five percent rejected. Set Invocation duration to eight, grace to ten and enable the replacement: sixteen of twenty arrivals complete, four are rejected, nothing is killed and the gate passes at twenty percent. Initial workers, Invocation slots per worker, Startup duration, Request interval, Fail initialization of worker 1 and Signal worker 1 at all change the same accounting. Common baseline settings restores the shared reference configuration.
Inspection and limits
Play, Step 1 second, Restart, Speed and Simulation time replay sixty seconds; Choose worker shows a worker state and the invocations it owns. Commit happens at completion, so a killed invocation leaves no output. Not modeled: load-balancer deregistration delay, HTTP keep-alive connections, in-flight tool side effects, session persistence, Kubernetes preStop hooks or JVM shutdown hooks. Real platforms send SIGKILL after their own termination grace period, which this deadline represents.