Ticket queue design
A bounded FIFO queue with priority lanes, expiry, and circuit breakers — the backbone of every licensing backend.
Core shape
A ticket queue is a fixed-capacity FIFO with three priority lanes — high, normal, low — drained by a pool of workers. Each ticket carries a TTL; expired tickets are silently dropped on dequeue. A circuit breaker halts enqueue when the queue reaches 80% capacity, returning HTTP 503.
Data model
ticket {
id: uuid v7
lane: high | normal | low
payload: jsonb
created: timestamp
expires: timestamp
attempts: uint8 (max 3)
}Worker loop
- Poll high lane → normal → low (strict priority).
- Skip expired tickets; increment dead-letter counter.
- Process payload; on failure, increment attempts.
- If attempts < 3, re-enqueue with exponential backoff.
- If attempts ≥ 3, move to dead-letter store.
Circuit breaker
A sliding-window counter tracks enqueue rate. When the window exceeds 80% of capacity for 3 consecutive sampling intervals, the breaker opens. Enqueue returns 503 with a Retry-After header. The breaker half-opens after 30s, allowing one probe request. Success closes it; failure re-opens.
Storage backends
Redis
Sorted sets per lane, ZPOPMIN for dequeue, TTL via score. Lua scripts for atomic dequeue + re-enqueue.
PostgreSQL
SKIP LOCKED queries, partial indexes on lane + expiry, advisory locks for worker coordination.
Observability
- queue.depth — gauge per lane
- queue.enqueue.rate — counter
- queue.expired — counter
- queue.dead_letter — counter
- circuit.breaker.state — gauge (0 closed, 1 open, 2 half-open)