feat(disk): KEEPSAKE_ENABLED trades the archive for ~3x the usable media
The upload gate refuses any photo that would leave too little room to build the keepsake, and the archive needs both halves at once — so it demands `2.2 x media + 10 GB reserve + 1.5 GB headroom`. Every gigabyte of photos therefore costs about 3.2 GB of disk budget, and usable media works out at `(disk - 11.5 GB) / 3.2`. On a 30 GB volume that is only ~5.8 GB of photos. Measured, not theorised: a 945-photo wedding (8.74 GB of real files) stopped after ~780 photos with 24.98 GB free — the gate wanted 25.91 GB on a disk that was 79% empty. Uploads ended for want of an archive nobody had asked for yet. `KEEPSAKE_ENABLED=false` drops the archive term, leaving only `DISK_RESERVE_BYTES` — the one question still live without an archive, which is whether Postgres can still write. That turns the same 30 GB volume into ~20 GB of usable media. Verified against the exact disk state that ended the run: the photo that returned 413 with the keepsake armed is accepted with it off. The headroom term goes with it, deliberately. It exists only to keep this gate strictly ahead of the EXPORT PREFLIGHT, and with no keepsake there is no preflight to stay ahead of. Boot-time immutable, like COMMENTS_ENABLED: flipping it mid-event would move the gate under uploads already accepted against the old one. Releasing the gallery and minting download tickets are refused while it is off, and the host dashboard hides the release control rather than offering a button that answers 403 — a new `keepsake_enabled` field on `GET /host/event` carries that. The gate and the host's low-disk banner now share `upload_gate_required_free`. They were already two expressions of one threshold, and the banner exists to fire BEFORE the gate closes; applying the switch to only one of them would warn the host about a limit that can no longer fire, or stay silent past one that can. Tests pin the lead in both modes. The cost is real and worth stating plainly: there is no downloadable gallery at the end, so guests keep only what they save from the feed. Prefer a bigger disk where you can — ~45 GB holds this library with the keepsake intact. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
24
.env.example
24
.env.example
@@ -207,6 +207,30 @@ COMPRESSION_WORKER_CONCURRENCY=2
|
||||
# production. Remove that line from the compose file first if you want comments back.
|
||||
COMMENTS_ENABLED=true
|
||||
|
||||
# ── Keepsake archive (and the disk ceiling it imposes) ────────────────────────
|
||||
# Master switch for the downloadable gallery. Boot-time only, like COMMENTS_ENABLED.
|
||||
#
|
||||
# This is really a DISK-SIZING knob. The keepsake needs room for both halves at once (a ZIP
|
||||
# and an HTML viewer, each gallery-sized), and the upload gate refuses any photo that would
|
||||
# leave too little space to build it. The gate demands:
|
||||
#
|
||||
# free >= 2.2 x media + DISK_RESERVE (10 GB) + UPLOAD_GATE_HEADROOM (1.5 GB)
|
||||
#
|
||||
# so usable media is roughly `(disk - 11.5 GB) / 3.2`. On a 30 GB volume that is only ~5.8 GB
|
||||
# of photos — uploads stop with most of the disk still free. Measured: a 945-photo wedding
|
||||
# (8.74 GB) hit the wall after ~780 photos with 25 GB free.
|
||||
#
|
||||
# KEEPSAKE_ENABLED=true (default) archive available; ~5.8 GB of photos on a 30 GB disk
|
||||
# KEEPSAKE_ENABLED=false no archive; ~20 GB of photos on a 30 GB disk
|
||||
#
|
||||
# Turning it OFF is irreversible for the event in the way that matters: there is no download
|
||||
# at the end, so guests keep only what they save from the feed themselves. Releasing the
|
||||
# gallery and minting download tickets are refused while it is off. The 10 GB database
|
||||
# reserve still applies — that one protects Postgres, not the archive.
|
||||
#
|
||||
# Prefer a bigger disk if you can: ~45 GB holds a 9.7 GB library WITH the keepsake.
|
||||
KEEPSAKE_ENABLED=true
|
||||
|
||||
# ── Logging ───────────────────────────────────────────────────────────────────
|
||||
# SET THIS IN PRODUCTION. Without it the app falls back to
|
||||
# `eventsnap_backend=debug,tower_http=debug` (see main.rs), and with TraceLayer that is a
|
||||
|
||||
Reference in New Issue
Block a user