98d25601b4
invalidateAll() invalidates every event holding a cached zip, and each invalidate() arms its own debounce timer in the same tick — so they all fired together and every one of them started building at once. Each build opens its own storage reads, so flipping a global setting across 25 events was enough to exhaust the S3 agent pool and stall uploads, thumbnails and gallery reads until the burst drained. Nothing capped it. Background rebuilds now run two at a time. The cap is deliberately only on that path: a foreground generateZip — a guest actually waiting on a download — is never queued behind a settings-change burst, which would trade one stall for another. stop() drains anything parked for a slot, so shutdown cannot hang on a queue that will never move. This is the second half of the problem. The first half — an individual build opening one unbounded read per photo — is the storage-read guard that already landed for the guest download routes and the job builder; the cached-zip builder's own copy is still in an open PR. Relates to issue 1399 Co-authored-by: Paul Nothaft <paul@MacStudio-von-Paul.local>