Priority & Rate Limiting Cheatsheet

One-screen lookup. Cite: Prioritized, Rate limiting, Global Rate Limit, rate-limiter-options.ts.

Priority — order

await queue.add('x', data, { priority: 10 });
await queue.add('x', data, { priority: 5  }); // runs FIRST (lower = higher)
await job.changePriority({ priority: 1 });
const counts = await queue.getCountsPerPriority(0, 1, 5, 10);
RuleDetail
Lower = higherRange 1–2 097 152.
No priorityTreated as highest — runs before any prioritized job.
TiesFIFO within the same value.
CostInsert is O(log n) — don't tag every job.
No preemptionOnly reorders waiting; active jobs finish.

Rate limiting — throughput

1. Worker limiter (global across workers)

new Worker('q', fn, {
  limiter: { max: 10, duration: 1000 },  // 10 jobs/1s, SHARED by all workers
});
Global, not per-worker. 10 workers × {max:10} = still 10/s total. Throttled jobs stay waiting.

2. Queue global limit (queue-side cap)

await queue.setGlobalRateLimit(100, 60_000);
const { max, duration } = await queue.getGlobalRateLimit();
const ttl = await queue.getRateLimitTtl();   // >0 = throttled now
await queue.removeGlobalRateLimit();
Coexists with worker limiter — stricter wins. Neither overrides.

3. Manual / dynamic (HTTP 429)

const worker = new Worker('q', async (job) => {
  const [ok, retryAfter] = await api(job.data);
  if (!ok) {
    await worker.rateLimit(retryAfter ?? 5000);
    throw Worker.RateLimitError();   // MUST be this, not a normal Error
  }
}, { limiter: { max: 5, duration: 1000 } });

await queue.removeRateLimitKey();            // clear manual hold
CallEffect
worker.rateLimit(ms)Holds the queue for ms.
throw Worker.RateLimitError()Returns job to waiting without consuming a retry.
queue.getRateLimitTtl()ms left on hold; 0 = free.
queue.removeRateLimitKey()Force-clear manual hold.

The groupKey trap

limiter: { groupKey: 'tenant' } was removed in BullMQ 3.0. RateLimiterOptions = { max, duration } only. Stale docs/tutorials still show it. Per-tenant throttling now → one queue per tenant, or manual worker.rateLimit() keyed on tenant.

Mental model

QuestionAnswer
Who runs first?priority (lower = first)
How fast overall?worker limiter {max,duration}
Ceiling across all producers?setGlobalRateLimit
React to a live 429?rateLimit + RateLimitError