Skip to content

Commit e2bc084

Browse files
anth-volkclaude
andcommitted
Document the qualified runtime shape in the operations reference
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 2fb195f commit e2bc084

2 files changed

Lines changed: 33 additions & 0 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Configure the production Cloud Run runtime from the Stage 2 capacity qualification: two gunicorn workers, request concurrency 4, production max-instances 4, staging pinned to scale-to-zero, and worker recycling to bound memory growth.

docs/migration/cloud-run-operations.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,40 @@ it**. Two mitigations are therefore built in:
4343
`--timeout 0` is required — a worker mid-import does not heartbeat, and gunicorn's
4444
default 30s watchdog would kill it. `--keep-alive 5` and `--forwarded-allow-ips '*'`
4545
preserve the previous uvicorn keep-alive and proxy-header behavior.
46+
`--max-requests 500` (+jitter 100) recycles workers to bound slow memory growth under
47+
sustained load; a recycling worker re-imports for ~3 minutes, halving instance
48+
capacity while it does.
4649
- Candidate deploys set `--cpu-boost` to shorten the import.
4750

51+
## Runtime shape and scaling (from the Stage 2 qualification)
52+
53+
Values measured and justified in
54+
[`history/pr4-stage2-runtime-timing.md`](history/pr4-stage2-runtime-timing.md):
55+
56+
- **`WEB_CONCURRENCY=2`** — the engine is GIL-bound; two worker processes materially
57+
outperform one on the 4-vCPU instance. Set via the deploy script's env vars.
58+
- **`--concurrency 4`** — admission control is what keeps the two-worker instance
59+
memory-safe in 16Gi; excess load queues at the platform and fails as client timeouts
60+
rather than dying in-instance. **Both knobs are pinned explicitly on every deploy**:
61+
gcloud inherits unspecified template fields (a mid-campaign CI deploy once shipped an
62+
inherited test concurrency), and `--concurrency default` resolves to the *platform*
63+
default (640), not the historical 80.
64+
- **Scaling pins live in `push.yml` per job**: production `max-instances 4` (peak real
65+
traffic ~11 RPS, mostly cached/light; ~1–2 concurrent uncached calculates per
66+
instance), staging `min 0 / max 1`.
67+
- **Warm capacity is a service-level setting made manually, once** — CI keeps
68+
revision-level `--min-instances 0`, because revision-level minimums keep a warm 16Gi
69+
instance alive per accumulated `stage3-*` tag:
70+
71+
```bash
72+
gcloud run services update policyengine-api \
73+
--project policyengine-api --region us-central1 \
74+
--min-instances 1
75+
```
76+
77+
Rationale: the user-facing scale-from-zero wake was measured at 282.8s — 17s under
78+
the 300s request timeout — so a cold start must never sit on a public request path.
79+
4880
Consequences to keep in mind:
4981

5082
- An instance is "started" (port bound) before the app can answer. Requests arriving in

0 commit comments

Comments
 (0)