You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/queue-concurrency.mdx
+96Lines changed: 96 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -157,6 +157,86 @@ export async function POST(request: Request) {
157
157
}
158
158
```
159
159
160
+
## Combined concurrency across keys
161
+
162
+
`concurrencyKey` gives every key value its own copy of the queue, each with the queue's full `concurrencyLimit`. That means the queue's total concurrency grows with the number of active keys: ten active users on a queue with `concurrencyLimit: 5` can run 50 at once.
163
+
164
+
To bound the whole queue, set `combinedConcurrencyLimit`. Each key still gets at most `concurrencyLimit`, and the queue as a whole never exceeds the combined limit across all keys:
165
+
166
+
```ts /trigger/per-user.ts
167
+
exportconst perUserQueue =queue({
168
+
name: "per-user-queue",
169
+
//each user runs at most 1 at a time...
170
+
concurrencyLimit: 1,
171
+
//...and at most 10 users can be running at once
172
+
combinedConcurrencyLimit: 10,
173
+
});
174
+
```
175
+
176
+
The combined limit only applies to runs triggered with a `concurrencyKey`; runs without a key are governed by `concurrencyLimit` alone. On the Queues page in the dashboard, a queue with a combined limit shows it in brackets next to the per-key limit, e.g. `1 (10)`.
177
+
178
+
<Note>
179
+
If you self-host, combined limits are enforced by default and can be disabled with
180
+
`RUN_ENGINE_TOTAL_CONCURRENCY_LIMITS_ENABLED=0`. When enforcement is disabled the limit is
181
+
still accepted, stored, and shown, but runs are not held back by it.
182
+
</Note>
183
+
184
+
## Holding slots in more than one queue (queue gates)
185
+
186
+
Sometimes one limit isn't enough: a webhook processor should be capped as a task, but each tenant should also have a global cap across every task they run. Queue gates let a run hold a concurrency slot in more than one queue at once.
187
+
188
+
Pass an array as `queue`: the first entry is the run's home queue (where it waits), and up to two more entries name gates — other queues the run must also have capacity in before it starts, and occupies while it executes:
A gate without a `concurrencyKey` uses the run's own key, so the shared `tenant` queue above caps each tenant across every task that names it as a gate. Give the gate a literal key to pin it to a single slot pool instead, for example capping all traffic to one external provider across your whole environment:
208
+
209
+
```ts /trigger/sync.ts
210
+
exportconst syncToProvider =task({
211
+
id: "sync-to-provider",
212
+
queue: [
213
+
{ name: "sync-home", concurrencyLimit: 20 },
214
+
//every run shares one "provider-api" pool regardless of its own key
The same array form works when you trigger, replacing the task's gates for that run:
224
+
225
+
```ts
226
+
awaitprocessWebhook.trigger(payload, {
227
+
queue: ["webhooks", "tenant"],
228
+
concurrencyKey: tenantId,
229
+
});
230
+
```
231
+
232
+
A run starts only when its home queue and every gate all have capacity, and it releases all of its slots together when it finishes or suspends.
233
+
234
+
<Note>
235
+
Queue gates are enforced when the server has them enabled. If you self-host, set
236
+
`RUN_ENGINE_QUEUE_GATES_ENABLED=1`; servers without gates enabled accept the option but run
237
+
without it.
238
+
</Note>
239
+
160
240
## Concurrency and subtasks
161
241
162
242
When you trigger a task that has subtasks, the subtasks will not inherit the queue from the parent task. Unless otherwise specified, subtasks will run on their own queue
description: When the concurrency limit was overridden
4450
+
combined:
4451
+
type: object
4452
+
description: |
4453
+
The combined concurrency cap across all `concurrencyKey` values of the queue.
4454
+
Present when the queue has a `combinedConcurrencyLimit`.
4455
+
properties:
4456
+
current:
4457
+
type: integer
4458
+
nullable: true
4459
+
description: The current combined concurrency limit as declared or overridden (null = no cap). Enforcement clamps it to the environment concurrency limit at admit time.
4460
+
example: 10
4461
+
base:
4462
+
type: integer
4463
+
nullable: true
4464
+
description: The declared combined limit an override reverts to on reset
4465
+
example: 10
4466
+
override:
4467
+
type: integer
4468
+
nullable: true
4469
+
description: The overridden combined limit, when an override is active
4470
+
example: null
4471
+
overriddenAt:
4472
+
type: string
4473
+
format: date-time
4474
+
nullable: true
4475
+
description: When the combined override was applied
4476
+
running:
4477
+
type: integer
4478
+
nullable: true
4479
+
description: Runs currently in flight across all concurrencyKey values
0 commit comments