-
Notifications
You must be signed in to change notification settings - Fork 0
05 Troubleshoot
Preserve the control state before changing a router or engine. Create a separate ignored evidence directory for each router and incident, and collect from its private address:
mkdir -p runs/diagnostics
incident_dir=$(mktemp -d runs/diagnostics/router.XXXXXX)
curl -sS http://router:8000/health > "$incident_dir/health.json"
curl -sS -o "$incident_dir/ready.json" -w '%{http_code}\n' http://router:8000/ready
curl -sS http://router:8000/narwhal/state > "$incident_dir/state.json"
curl -sS http://router:8000/narwhal/lifecycle > "$incident_dir/lifecycle.json"
curl -sS http://router:8000/metrics > "$incident_dir/metrics.txt"Retain the router journal, ingress status, supervisor status, engine boot logs, fleet config, profiles and deployment load results with those files. Follow the lifecycle contract or the unplanned-failure procedure below before restarting an engine.
| Symptom | Meaning | Action |
|---|---|---|
/health is unreachable |
The router or host is down | Check the other router's /ready and see who holds the lease |
/health is standby
|
The router is up as a standby | Keep traffic and lifecycle actions off it |
/health is fenced
|
Lease ownership moved to another router | Find the current lease holder and leave this router out of the load balancer |
/health is maintenance
|
An engine-wide maintenance wave is running | Follow /narwhal/lifecycle and wait for readiness to return |
/ready is 503 on both routers |
Both routers are refusing traffic | Check the refusal reasons first, then backend health, lifecycle holds, monitoring, lease ownership, and handoff freshness |
| HTTP 429 rises | More requests are being refused or shed | Break down rejected, refused, and queue-shed reasons before adding capacity |
| HTTP 502 or 504 rises | An engine failed or timed out | Check engine health, ejection, quarantine, and work still in flight |
| Stream ends with an error frame | The stream failed after the 200 response had started | Check the failed attempts, final outcome, and engines involved |
Lifecycle state is blocked
|
Drain identity capture or readmission validation failed | Fix the failed check, then retry the action that failed: drain or readmission |
- Read
admission,serving,resident, and pool load from/narwhal/state. - Separate immediate concurrency rejection, queue-full shed, queue expiry, and predictive refusal.
- Compare both attainment denominators: the router journal divides completions by admitted requests, while the client divides success by every offered request, including predictive refusals.
- Reduce offered traffic at ingress or add a separately validated fleet.
- Change
serving.max_connections, queue depth, or timeouts after a controlled two-point measurement confirms the direction.
A longer queue or timeout can turn an immediate refusal into a late SLO miss, so shed offered traffic at the current settings until the two-point measurement confirms a new direction.
- Confirm the engine is ejected or quarantined and that other engines still receive work.
- Preserve its boot log and supervisor exit reason.
- Check
recovery.engine_restart_policy. Withindividual, let the external supervisor restart the engine and its attestation sidecar. Withwhole_wave, use the complete-wave procedure below. - Watch
/narwhal/lifecycle. A contracted fleet automatically runs health, attestation, model, generation, role-permitted KV, and final-health gates. - If validation passes, confirm the engine returns to
accepts_new: trueand the ejection clears. - If individual validation fails, repair the
blockedengine and call/narwhal/lifecycle/readmit; readmission clears its ejection after every recovery gate passes. Awhole_wavefleet requires complete-wave readmission.
Use the drain sequence, and let the external supervisor stop the process after ready_to_stop: true confirms that placement has stopped and resident work has drained; readmission then proves a newer process identity alongside the recovery gates.
Treat a stale-peer assertion, transfer stall that kills a peer, or generation mismatch as a whole-wave event.
- Stop new traffic with a lifecycle whole-wave drain. If drain identity capture fails for an engine, follow the unplanned whole-wave procedure to restore the endpoint and retry the drain.
- Wait for
wave.ready_to_stop: trueand router/readyHTTP 503. - Stop the full engine process trees through the external supervisor.
- Verify accelerator memory belongs only to the current worker.
- Start every engine from one immutable image and launch contract.
- Start a new attestation sidecar for every engine process.
- Submit whole-wave readmission.
- Return ingress traffic only after the KV ring and final health checks pass and
/readyreturns HTTP 200.
- Check both private
/readyroutes. - Confirm one lifecycle document reports
router.controls_fleet: true. - Confirm the load balancer selects only that router.
- Verify its lease epoch is greater than the failed primary's epoch and that roles and cumulative counters match the last handoff.
- Restart the old primary as standby, pointing
--standby-ofat the current active router. - Confirm the recovered process returns HTTP 503 from
/readyand refuses a direct completion request.
If both routers return 503, inspect their refusal reasons before attempting takeover. Repair lease storage or clock bounds when those checks fail. Preserve the lease and fencing while restoring one active primary.
The standby raises no fresh handoff when the preceding lease epoch's handoff has expired or that router ended before persisting one. A contract-version mismatch or wrong epoch holds /ready at HTTP 503 until a matching handoff arrives.
Keep client traffic stopped. Restore the compatible router release and state set, or start one router from the configured opening roles during a maintenance window. Confirm the old process is stopped or fenced before admitting traffic.
- Withdraw the target router from the load balancer.
- Stop it cleanly when possible so its handoff is complete.
- Check the rollback build's contract manifest with
narwhal-check --print-contract-versions. - Restore the matching config, profiles and a handoff version that build reads.
- Confirm that fleet control belongs to this router or its fenced HA peer.
- Start the rollback build and verify health, readiness, roles, counters, and one completion.
- Restore its standby only after the active router is stable.
Older builds load handoffs that declare a supported schema version. To start from configured opening roles and reset cumulative counters, set recovery.resume: false.
Use the release and production drills to check the repaired deployment's lifecycle and failover paths.