mirror of
https://github.com/block/buzz.git
synced 2026-11-07 01:41:06 +00:00
## Summary Clients ask for an image's small `.thumb.webp` thumbnail first and fall back to `.thumb.jpg` on a 404. Images uploaded before [#9003](https://github.com/block/buzz/pull/9003) only have the JPEG, so every client pays an extra 404 for them and transparent avatars keep showing on a black background. The same thing happens when a thumbnail write failed at upload time. When a thumbnail variant is missing but its image original exists, the relay now regenerates the thumbnails on that request and serves them with 200. It uses the same generator and storage writes as upload, which are now one shared `store_image_thumbnails`. Later requests are ordinary hits. - GET (full and range) and HEAD both regenerate, so a HEAD-then-GET client sees the same result. - Regeneration runs only after the existing tenant, auth and sidecar checks for that thumbnail. - It is bounded. At most two regenerations run per relay process (`try_acquire`, no waiting), and originals larger than `max_image_bytes` are skipped. In both cases, and for non-images, missing originals or any decode or storage error, the response is the 404 it is today. Errors are logged at warn. ### Related issue Follow-up to [#9003](https://github.com/block/buzz/pull/9003) and [#9105](https://github.com/block/buzz/pull/9105), which made relays write WebP thumbnails and clients prefer them. ### Testing Two router-level tests against Postgres and MinIO upload a PNG through the real route, then delete its thumbnails: - `missing_thumbnail_is_regenerated_from_image_original`: GET and HEAD of `.thumb.webp` and `.thumb.jpg` return 200 with the right content type, and both objects are stored afterward. With regeneration disabled it fails with `GET webp: 404, expected 200`. - `missing_thumbnail_is_not_regenerated_when_unsafe`: a non-image blob, a missing original, an oversize original and a busy relay (no free permit) each return 404 and store nothing. Once the permits are free, the same request returns 200. Removing the size cap or the permit guard fails it. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: John Tennant <jtennant@squareup.com> Co-authored-by: Simone <0828be588d5c9c4d092c5b1cf01761d5aef63d6b104807fd8e8284d517dd1a99@buzz.block.builderlab.xyz> Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>