Users upload product images. Files must be scanned, resized, then published to the CDN. The scan service crashes mid-flow. How do you avoid serving unscanned files and avoid duplicate processing?
How I frame it
Treat upload as a state machine on an asset, not a synchronous HTTP chain. Public CDN URL is only set after SCAN_CLEAN. Crashes must resume from last durable state with idempotent workers.
Pipeline
- Direct-to-blob upload: Presigned PUT to private bucket
uploads/raw/{asset_id}. App createsassets(id, status=UPLOADED, checksum). - Enqueue
AssetUploadedvia outbox (idempotent on asset_id). - Scan worker: Claim row
SCANNING, call AV. ResultCLEAN | INFECTED. Never copy to public prefix before CLEAN. - Transform worker: Resize/WebP →
deriv/prefix; statusPROCESSED. - Publish: Copy/move to public bucket or attach CDN path; status
PUBLISHED. Product API only references PUBLISHED assets.
UPLOADED → SCANNING → CLEAN → PROCESSING → PUBLISHED
↘ INFECTED → QUARANTINED (no CDN)
Crash in SCANNING: lease expires → another worker resumes
Checksum + asset_id → skip duplicate scan if already CLEANSafety rules
- Private bucket by default; CDN origin only serves
public/. - Worker leases with TTL; fencing token so stale scanner cannot mark CLEAN late.
- Idempotent steps keyed by
asset_id + step. - Infected: delete/quarantine; never retry publish.
Weak answer
"Upload to CDN first, scan async." Malware is publicly reachable during the window. "Do scan in the API process." Timeouts, no resume, easy to double-process on client retry.
Follow-up questions
Very large video files?
Chunked upload, same state machine; scan may be async with longer SLA; streaming transforms.
Re-process with new resize profile?
New derivation job from CLEAN original; do not re-scan unless policy requires; versioned CDN paths (/v2/).