fix(deploy): give Postgres a CPU floor that Docker actually honours
`deploy.resources.reservations.cpus` was doing nothing. Outside Swarm, `docker compose up` silently drops it — verified by inspecting a running container, where CpuShares, CpuQuota and CpusetCpus were all unset while `limits.cpus` and `reservations.memory` came through as NanoCpus and MemoryReservation. So the comment calling it "the piece that actually protects the database" described a guarantee the box never had. It matters on the CX22 the runbook targets: the ceilings sum to 1.2 + 0.6 + 0.5 = 2.3 on 2 vCPU, so the other services can oversubscribe the machine, and with every container on the default weight Postgres competed on equal footing with two image resizes and an ffmpeg poster. Replaced with `cpu_shares`, which does survive the translation — db 2048, caddy 1024, app 512, frontend 256 — so the weighting only binds when the CPU is actually saturated, which is the moment the database must not lose. The Caddyfile gains a 10s header-read timeout: there was no read timeout anywhere, so a client could hold a connection, a tokio task and a `.tmp` file open indefinitely by sending one byte a minute, and the upload sweeper is keyed on mtime precisely so a live upload never ages out. Body reads stay unbounded — a 500 MB video over cellular legitimately takes minutes, and a body timeout would fail exactly the uploads this product exists to collect. .env.example documents that estimated_guest_count is a live input to the quota divisor rather than the inert setting both it and the runbook previously implied. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
16
.env.example
16
.env.example
@@ -12,6 +12,15 @@ DOMAIN=my-event.example.com
|
||||
# prebuilt images from the registry and never compiles — see DEPLOYMENT_RUNBOOK.md.
|
||||
# Always an immutable tag, never `latest`: rollback is `EVENTSNAP_VERSION=<previous>`
|
||||
# + `docker compose up -d`, which works offline if that image is still resident locally.
|
||||
#
|
||||
# ⚠ THIS TAG DOES NOT EXIST YET. The newest git tag is v0.12.0; v0.13.0 is the release you
|
||||
# cut for the event. Build and push it (plus its identical rollback twin v0.13.0-a) BEFORE
|
||||
# the first `docker compose up -d` — see DEPLOYMENT_RUNBOOK.md §6 (build) and §9 (rollback).
|
||||
# Copying this file and starting the stack without that step fails with `manifest unknown`.
|
||||
#
|
||||
# Do NOT "fix" this by dropping back to v0.12.0: no image was ever built for it, and a
|
||||
# 6-migration tree booting against a 22-migration database returns VersionMissing and
|
||||
# crash-loops forever behind a live Caddy. §9 covers this in full.
|
||||
EVENTSNAP_VERSION=v0.13.0
|
||||
|
||||
# ── App server ────────────────────────────────────────────────────────────────
|
||||
@@ -100,7 +109,12 @@ EXPORT_PATH=/exports
|
||||
# quota_tolerance is the MULTIPLIER IN THE PER-USER QUOTA FORMULA, not the point at
|
||||
# which anything warns you:
|
||||
#
|
||||
# per_user_limit = floor(free_disk * quota_tolerance / active_uploaders)
|
||||
# divisor = max(active_uploaders, estimated_guest_count, 1)
|
||||
# per_user_limit = max(floor(free_disk * quota_tolerance / divisor), 500 MiB)
|
||||
#
|
||||
# estimated_guest_count is a FLOOR ON THE DIVISOR, not decoration — it is a live knob
|
||||
# (upload::quota_limit_bytes). Earlier drafts of this file and the runbook both omitted
|
||||
# it and told operators it was inert; it is not.
|
||||
#
|
||||
# It is recomputed against LIVE free space on every upload, so it self-throttles: guests
|
||||
# converge on a fixed point at tolerance/(1+tolerance) of the free space you started
|
||||
|
||||
Reference in New Issue
Block a user