請求生命週期
1. 即時圖片請求
GET /img/:sig/:opts/*
實際流程重點:
- 驗證 HMAC 簽名與 options
- 先查記憶體 LRU,再查 media bucket 的 storage cache
- 若 miss,從 source bucket 讀原圖
- 以 singleflight 避免同一來源或同一處理參數被重複抓取/重複處理
- 交由 libvips 處理
- 同步回寫記憶體 LRU,非同步寫入 media bucket cache
- 回應
Cache-Control: public, max-age=31536000, immutable與ETag
這條路徑完全同步,不依賴 queue。
2. Job 提交
POST /api/jobs
實作上的關鍵不是只有 enqueue,而是先做 request fingerprint:
- server 驗證 API key 與 payload
- 針對
type + hash + source + canonicalized options做 idempotency 檢查 - 若有既有活躍 job 或已完成結果,直接返回
- 否則 enqueue 到 Redis / asynq
- 建立 PostgreSQL job row,初始狀態為
queued
來源 bucket 本身不是呼叫端可覆寫的欄位,而是由部署時的 runtime 設定決定。
對影片類工作,server 在 enqueue 前還會檢查 source store,確認物件存在、量測實際大小,並在需要時把大型任務路由到 video:large。
3. Worker 執行
Worker 執行可分成兩類:單階段任務,以及 video:full workflow。
單階段任務
image:thumbnailvideo:covervideo:previewvideo:transcode
共同模式:
- worker 從 queue 取出 task
- 把 task status 更新為
processing - 從 source bucket 下載或讀取來源檔
- 執行媒體工具鏈
- 上傳 artifacts 到 media bucket
- 更新 job results 與 progress
- 視需要送 webhook callback
video:full workflow
video:full 不是把 cover / preview / transcode 分裂成三個父子 job,而是在單一 worker task 內完成:
- 下載來源檔一次
- 並行執行 cover 與 preview
- 若任一失敗,產生 machine-readable
stages與retry_plan - 若兩者成功,再進入 transcode
- 聚合所有 artifacts 後回寫單一結果 JSON
這讓外部看到的是一個 job,但結果仍保有每個 stage 的可觀測性。
4. HLS 播放
GET /stream/{hash}/*
播放時,server 不在本地存 segment,而是依 {hash} 與後綴路徑映射到 media bucket:
master.m3u8audio/{track_id}/...video/{variant}/...
對於未加密內容,播放器只需走 /stream/...。
對於加密內容,播放器還會額外呼叫 /api/key/{hash},並帶 Authorization: Bearer {token}。
5. 清理
DELETE /api/media/{hash} 的生命週期比較短,但對資料一致性很重要:
- 依
hash找出相關 media bucket objects - 清除 image cache tracking 與相關資料列
- 取消 queue 中的 active / retry / scheduled tasks
- 刪除 encryption key 與 job 紀錄
這個流程設計為 best-effort 與 idempotent,適合上游補償流程重複呼叫。