GPU gateway operations (post-BRI-439 push-only)
GPU gateway operations (post-BRI-439 push-only)
Supersedes the BRI-420 canary/weight-ramp checklist — weights, canary
rules, and ramp stages no longer exist. Dispatch order IS the policy:
fallbackChain in the resolved gateway config, walked top to bottom by
dispatchViaPushChain. This doc is the operator’s reference for how a job
moves and where to look when it doesn’t.
How a job dispatches
POST /jobscreates the row (state: new,runner:its assigned
runtime id or the default) and firesdispatchJob— the event-driven
path. The 60 sdispatch-sweeploop picks up anything leftnew.dispatchViaPushChainwalksdispatchCandidates(job.runner)— the
job’s runner first, thenfallbackChain, dedup’d — skipping runtimes
that are disabled, model-mismatched, missing an impl,unreachable,
orover_budget.- For each candidate:
promoteRunnerthe row, take a 600 s dispatcher
lease (claimForRunner(id, "<slug>-dispatcher")),impl.submit. A
retryable failure (429 busy, unreachable, timeout) retries once in
place, then walks to the next candidate; a non-retryable failure
releases the queue row and runsfailOrFallback. - Success stamps
leased_by = "<runtimeId>:<callId>". Then:kind: home/local— the push daemon (BRI-437) drives state
transitions itself via the worker-secret routes (PATCH /jobs/:slug,
/lease,/fail,/claimed). The reaper pollsGET push.url/<callId>as a stale-call backstop.kind: modal— the spawnedgenerateFunctionCall writes to R2 via
the payload’s presigned URLs and returns the result to the
status(callId)poll; nothing on Modal calls the backend. The row
goescomposingon submit and the reaper’s poll is the only
completion path.
failOrFallbackpromotes the row to the nextfallbackChainentry on
exhausted attempts; after the last entry the job fails terminally.
Config changes (chain order, enable/disable, budgets) go through the admin
console (GPU Gateway) or the import/export routes — import is
replace-semantics, so export first.
Cost guardrails (always on)
budget.dailyCents = 1000per Modal runtime ($10/day soft cap; the
runtime readsover_budgetat 2x and is skipped in the chain walk).maxConcurrency: 2on beta.idleExitSeconds: 60.- A Modal dashboard budget alert at $10.
Stop conditions — disable + drain immediately
- The same slug submitted more than 3 times.
- Stuck count > 3.
- A 401/403 on
submit(proxy token wrong/expired).
Drain verb: modal app stop brioso-worker -e <env> (proxy-auth) or
disabling the runtime in the admin console — a disabled runtime is skipped
in the walk and modal:app-stop releases in-flight calls on next poll.
Rollback layers: disable in the UI → modal app stop → restore the
exported gateway file via import.
Observability — where a stuck job is stuck
| Hop | Look at | Symptom → cause |
|---|---|---|
1 POST /jobs |
web journal; jobs row |
no row → auth/validation; unverified → OTP |
| 2 Dispatch | job_events + sweep log (dispatch-sweep) |
new with no submit → sweep down or all candidates skipped (disabled/unreachable/over_budget) |
| 3 Submit | gateway.submit span; job_events.routed |
401/403 → proxy token/env names; 404 → modal: block URL or push.url; timeout → Modal control plane / daemon down → walks to next candidate |
| 4 Container/daemon start | Modal dashboard generate calls, or daemon log |
spawn queued → max_containers/capacity; image pull; 429 busy → daemon saturated |
| 5 Generate + upload | worker/daemon spans; R2 keys | heartbeat stops → crash/OOM → lease expiry → reaper promotes |
| 6 Completion | status(callId) poll (modal) or PATCH writebacks (daemon); completed event |
stale call → reaper failOrFallback |
| 7 UI | job JSON, browser network | paid without audio → delivery/presign, don’t rerun |
The admin console’s runtime detail shows hops 2–4 live per runtime: queue
depth, last submit, health state, spend ledger.