The quota stopped bounding the disk. `soft_delete_in_event` stamps `deleted_at` and refunds `total_upload_bytes`, but nothing ever removed the bytes, and the hourly sweep reached only `compression_status = 'failed'`. Upload 500 MB, delete, quota back to zero, upload another 500 MB. Not an attack -- a guest curating their camera roll, which is what people do. The host then sees guests hitting "Du hast dein Upload-Limit erreicht" while the admin widget shows a disk full of files no upload row points at, and the quota message is actively misleading because the space really is gone, just not to anyone the accounting can name. Two retention windows, because the two deletes mean different things. A compression failure keeps its 14 days: the guest didn't ask for it and may not be able to retake the photo. A deliberate removal gets 24 hours -- 14 days outlives the whole event, so a deliberate delete would never reclaim anything while it mattered, and a day still covers a mis-tap. Wider than reported: ALL FOUR paths are reclaimed, not just the original. Preview, display and thumbnail are each a separate file, none counted in `original_size_bytes`, and nothing ever removed them either. That was invisible while the sweep only saw failed compressions (which produce no derivatives) and becomes three leaked files per upload the moment it reaches a successful one. A row is re-selected until every path is cleared, and the columns are cleared only once every file for that upload is gone -- clearing after a partial success would strand the survivors in exactly the unowned state this drains. `backfill_stale_derivatives` selects on `display_path IS NULL AND preview_path IS NOT NULL`, which is close enough to the post-sweep state to be worth pinning: it is guarded on `deleted_at IS NULL`, so it cannot re-decode an original that is no longer on disk. Covered. Residual, deliberately: within the 24h window the bytes are still spent and still unaccounted, so delete-and-re-upload through an eight-hour event can outrun the sweep. Bounding that means holding the quota until the file is reclaimed rather than refunding at `deleted_at`. The low-disk warning is the net under it. Tests: 6 DB-backed, replacing 3. The one asserting an owner-deleted upload IS reclaimed is the exact inverse of what this file used to assert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
12 KiB
12 KiB