What happened
On 2026-09-01 ~16:16–16:31 UTC, the production Cloud Run entrypoint (policyengine-simulation-entry, project policyengine-simulation-entry) returned an instant 429 Rate exceeded. (Google Frontend, ~0.1s) for every request on every path (/health, /docs, /versions/*). App-v2 UK society-wide reports failed with a 500 wrapping the upstream 429 from GET /versions/policyengine (v1 API SimulationEntrypointClient.resolve_app_name).
Evidence gathered during the incident:
https://simulation.api.policyengine.org/health → 429 Rate exceeded. (via the LB)
https://policyengine-simulation-entry-4xy55an4yq-uc.a.run.app/health → 429 (direct stable URL, bypasses the LB/Cloud Armor) — so this was Cloud Run itself refusing admission, not an edge policy
- Tagged revision URL
prod-10---… → 429 as well
policyengine-simulation-entry-staging (same project/region) → 200 (5.5s cold start) — rules out project-wide quota/billing
- Modal gateway
…-web-app.modal.run/health → 200 — backend healthy
- No deploy in the window (last: run 33270913487, 2026-08-29, same SHA as main)
An instant service-scoped 429 with that body is Cloud Run's admission-control refusal: all instances busy at max capacity with a full request queue (prod: --concurrency 80 --max-instances 20), i.e. either a large request burst or wedged/slow instances collapsing effective capacity. The service recovered on its own at ~16:31 UTC before any intervention reached prod (a re-deploy of the same SHA was dispatched as an instance refresh — run 33531984855 — and the recovery preceded its prod phase). A UK society-wide calculation (/uk/economy/82209/over/1?region=uk&time_period=2026) completed end-to-end right after.
Follow-ups this issue asks for
- Forensics (needs GCP access): Cloud Run metrics/logs for
policyengine-simulation-entry (us-central1) around 16:00–16:35 UTC — request rate, container instance count, request latencies, 429 count, and the request log by client identity/path just before onset. Was this a burst (from which caller?) or instances wedged behind a slow Modal upstream (25s proxy timeout × 80 concurrency can hold a lot of slots)?
- Uptime monitoring: there is no Better Stack (or equivalent) monitor on
https://simulation.api.policyengine.org/health — the v1 API host has one, this host does not; nobody was paged and the outage was found by a user (report page failure). Add a monitor.
- Capacity review: whether
--max-instances 20 × --concurrency 80 and the 25s upstream timeout compose safely when the Modal gateway is cold/slow; consider a lower per-request admission ceiling or faster shed for /versions/* (cacheable, tiny) so version resolution doesn't die with job traffic.
Related context: the July bot-wave incidents on the v1 API LB (docs/migration in policyengine-api, lb-cloud-armor-runbook.md) — but note Cloud Armor could not have caused this one (direct URL reproduced it), and the sim LB policy state should be checked separately if forensics point to a flood.
What happened
On 2026-09-01 ~16:16–16:31 UTC, the production Cloud Run entrypoint (
policyengine-simulation-entry, projectpolicyengine-simulation-entry) returned an instant429 Rate exceeded.(Google Frontend, ~0.1s) for every request on every path (/health,/docs,/versions/*). App-v2 UK society-wide reports failed with a 500 wrapping the upstream 429 fromGET /versions/policyengine(v1 APISimulationEntrypointClient.resolve_app_name).Evidence gathered during the incident:
https://simulation.api.policyengine.org/health→ 429Rate exceeded.(via the LB)https://policyengine-simulation-entry-4xy55an4yq-uc.a.run.app/health→ 429 (direct stable URL, bypasses the LB/Cloud Armor) — so this was Cloud Run itself refusing admission, not an edge policyprod-10---…→ 429 as wellpolicyengine-simulation-entry-staging(same project/region) → 200 (5.5s cold start) — rules out project-wide quota/billing…-web-app.modal.run/health→ 200 — backend healthyAn instant service-scoped 429 with that body is Cloud Run's admission-control refusal: all instances busy at max capacity with a full request queue (prod:
--concurrency 80 --max-instances 20), i.e. either a large request burst or wedged/slow instances collapsing effective capacity. The service recovered on its own at ~16:31 UTC before any intervention reached prod (a re-deploy of the same SHA was dispatched as an instance refresh — run 33531984855 — and the recovery preceded its prod phase). A UK society-wide calculation (/uk/economy/82209/over/1?region=uk&time_period=2026) completed end-to-end right after.Follow-ups this issue asks for
policyengine-simulation-entry(us-central1) around 16:00–16:35 UTC — request rate, container instance count, request latencies, 429 count, and the request log by client identity/path just before onset. Was this a burst (from which caller?) or instances wedged behind a slow Modal upstream (25s proxy timeout × 80 concurrency can hold a lot of slots)?https://simulation.api.policyengine.org/health— the v1 API host has one, this host does not; nobody was paged and the outage was found by a user (report page failure). Add a monitor.--max-instances 20×--concurrency 80and the 25s upstream timeout compose safely when the Modal gateway is cold/slow; consider a lower per-request admission ceiling or faster shed for/versions/*(cacheable, tiny) so version resolution doesn't die with job traffic.Related context: the July bot-wave incidents on the v1 API LB (docs/migration in policyengine-api,
lb-cloud-armor-runbook.md) — but note Cloud Armor could not have caused this one (direct URL reproduced it), and the sim LB policy state should be checked separately if forensics point to a flood.