◉CLAUDE LABJP
●2.1.302 — Fork subagents are now on by default in claude -p, the Agent SDK and the desktop app. Turn them off with CLAUDE_CODE_FORK_SUBAGENT=0 (10/16)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 43 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…●2.1.302 — Fork subagents are now on by default in claude -p, the Agent SDK and the desktop app. Turn them off with CLAUDE_CODE_FORK_SUBAGENT=0 (10/16)●2.1.299 — Conversations over 200K tokens left idle about 54 minutes get compacted; MCP allow rules now match only the server they name●SONNET 4.5 — Retires on the Claude API on 11/30, 43 days left. Migrate to Sonnet 5.5●WINDOWS — Reports say hooks break silently only on Japanese-locale Windows; check the character encoding first●FAST MODE — Opus 5.5 fast mode is out of research preview and no longer needs the beta header (10/15)●NEW — I Put a Ten-Line Promise File at the To…
Articles/API & SDK
⬡ API & SDK/2026-05-23Advanced

Adding Priority and Fairness to a Claude API Job Queue — Backpressure Patterns from a 50M-Download Indie App

A practical design for adding three-tier priority queues and Deficit Round Robin fairness to a Claude API worker. Drawn from running review-automation pipelines across an indie app catalog with 50M cumulative downloads, with full Python code and production metrics from a year of operation.

claude-api83priority-queuefair-schedulingbackpressureproduction111

✦ Premium Article

About a year after I started shipping a Claude-API-backed feature in production, I noticed something specific: most of my outages had nothing to do with "slow Claude days." They were about the order in which requests stacked up. The trigger was a small incident in an App Store review auto-reply worker I run for the indie apps I built — one morning a flood of long English reviews arrived, and quietly behind them Japanese reviews sat untouched for ninety minutes.

When I counted the morning's traffic, I had three English reviews of 18,000+ characters each — about 9,000 input tokens — and each Claude API call took 12–18 seconds. The Japanese reviews were closer to 600 characters and should have returned in about 1.5 seconds. But they arrived after the three long ones in a FIFO queue, and four parallel workers were tied up on the long jobs for the full 22 seconds. A naive FIFO worker design was quietly sacrificing short-input users at peak.

This article is the design I evolved over the six months after that. The official Anthropic SDK tells you to "cap your parallelism" and "design for retries," but once you run a multi-tenant Claude pipeline in production for a year, that turns out to be table stakes. Today I'll share the three-tier priority queue I now run, plus the Deficit Round Robin scheduler that keeps tenants from starving each other — with the actual code and metrics.

Why "request arrival order" is the wrong default

My first design was painfully simple: enqueue jobs onto a Redis LIST, four workers BRPOP'ing in parallel. The Claude messages.create calls fit comfortably inside my tier's parallelism budget, and on average days nothing felt off.

But when input-token sizes get lopsided, FIFO falls apart. Concretely:

  • Long jobs (18,000 chars ≈ 9,000 input tokens): median latency 12.4 seconds, p99 21.8 seconds
  • Short jobs (600 chars ≈ 300 input tokens): median latency 1.5 seconds, p99 3.2 seconds
  • When all four workers grab long jobs, every short job that arrives after waits up to 22 seconds

If you compare "throughput on 100 short jobs" vs "throughput on 3 long jobs," the long jobs dominate. This is classic Head-of-Line Blocking — networking and database folks have had answers for it for decades — and it shows up identically in a Claude worker pool.

The way I first noticed it was that a user in my Discord asked me why my reply was so slow. I had told myself review replies were "async, no SLA," but in practice users get anxious after just a few minutes. FIFO looks fair on paper, but it systematically penalizes the majority of small-input users while the few large ones consume the worker pool.

Three design goals — the latency / throughput / fairness tradeoff

To fix this, I sat down and rewrote my goals in priority order. The order matters; the upper ones trump the lower.

  1. Instant response on the critical path: any request triggered by a direct user action (e.g. an in-app "ask AI" button) should return within p99 ≤ 2 seconds
  2. Fairness between tenants: if one tenant (in my case, one app ID across my portfolio) dumps in a flood of jobs, other tenants' average wait should not rise more than 1.5×
  3. Maximize batch throughput: overnight bulk jobs should burn through the rails as hard as possible — but only when they don't violate (1) and (2)

These three pull against each other. Maximize (1) and (3) stalls. Maximize (2) and the tail of (1) grows (long jobs delay shorter ones). After a year of tuning, I ended up assigning each goal to a different mechanism:

  • (1) is handled by the three-tier priority queue with critical taking precedence
  • (2) is handled by DRR (Deficit Round Robin) topping up per-tenant quota on every round
  • (3) is handled by giving the batch tier a minimum guaranteed share plus delegating to the Anthropic message_batches API for very large jobs

What follows is the implementation. The code is Python asyncio, but the structure transfers cleanly to Node.js or Go.

✦

Thank you for reading this far.

Continue Reading

What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.

WHAT YOU'LL LEARN
✦Working Python code (~180 lines) for a three-tier priority queue (critical/standard/batch) combined with Deficit Round Robin fairness scheduling
✦Measured p99 latency reduction from 11.2s to 1.8s and the starvation-avoidance design decisions behind those numbers
✦Auto-demotion of budget-exceeding tenants while respecting 429 / Retry-After, plus four production gotchas surfaced after a year of running this in front of real users
Secure payment via Stripe · Cancel anytime
✦

Unlock This Article

Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.

or
Unlock all articles with Membership →
Share

Thank You for Reading

Claude Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

⬡ API & SDK2026-05-26
Stabilizing Claude API Structured Responses in Production — Notes on tool_use, JSON Schema, and Layered Validation
Getting Claude to return JSON takes a few lines. Keeping that JSON usable in production is a different problem. Here is the layered design I landed on after running a wallpaper classification pipeline through Claude API, built around tool_use, JSON Schema, and domain validation.
⬡ API & SDK2026-05-22
Why tool_result could not be submitted Keeps Coming Back, and How to Build a Recovery Handler That Actually Holds
Run a Claude agent long enough and one day it starts: 'tool_result could not be submitted', back to back, and retries change nothing. The error message hides four completely different root causes. Here is what I learned debugging it on the always-on agent jobs I run as an indie developer, with the TypeScript recovery handler I now ship in production.
⬡ API & SDK2026-05-19
Hierarchical Chat History Summarization with Claude API: A 3-Tier Design That Cut Tokens by 70%
Working TypeScript design for compressing long in-app chat histories into three tiers — recent turns kept verbatim, mid-range episodes summarized with Haiku 4.5, and long-range memory distilled to JSON by Sonnet 4.6. Includes seven weeks of production data showing input tokens down 70% and monthly API cost down from $480 to $145.