Default concurrency
By default, all tasks have an unbounded concurrency limit, limited only by the overall concurrency limits of your environment. This means that each task could possibly “fill up” the entire concurrency limit of your environment.Your environment has a maximum concurrency limit which depends on your plan. If you’re a paying
customer you can request a higher limit by contacting us.
Setting task concurrency
You can set the concurrency limit for a task by setting theconcurrencyLimit property on the task’s queue. This limits the number of runs that can be executing at any one time:
/trigger/one-at-a-time.ts
Sharing concurrency between tasks
As well as putting queue settings directly on a task, you can define a queue and reuse it across multiple tasks. This allows you to share the same concurrency limit:/trigger/queue.ts
task1 and task2 share the same queue, so only one of them can run at a time.
Setting the concurrency when you trigger a run
When you trigger a task you can override the concurrency limit. This is really useful if you sometimes have high priority runs. The task:/trigger/override-concurrency.ts
app/api/push/route.ts
Concurrency keys and per-tenant queuing
If you’re building an application where you want to run tasks for your users, you might want a separate queue for each of your users (or orgs, projects, etc.). You can do this by usingconcurrencyKey. It creates a separate queue for each value of the key.
Your backend code:
app/api/pr/route.ts
Concurrency and subtasks
When you trigger a task that has subtasks, the subtasks will not inherit the concurrency settings of the parent task. Unless otherwise specified, subtasks will run on their own queue/trigger/subtasks.ts
Waits and concurrency
With our task checkpoint system, a parent task can trigger and wait for a subtask to complete. The way this system interacts with the concurrency system is a little complicated but important to understand. There are two main scenarios that we handle slightly differently:- When a parent task waits for a subtask on a different queue.
- When a parent task waits for a subtask on the same queue.
We sometimes refer to the parent task as the “parent” and the subtask as the “child”. Subtask and
child task are used interchangeably. We apologize for the confusion.
Waiting for a subtask on a different queue
During the time when a parent task is waiting on a subtask, the “concurrency” slot of the parent task is still considered occupied on the parent task queue, but is temporarily “released” to the environment. An example will help illustrate this:/trigger/waiting.ts
Waiting for a subtask on the same queue
Because tasks can trigger and wait recursively, or share the same queue, we’ve added special handling for when a parent task waits for a subtask on the same queue. Recall above that when waiting for a subtask on a different queue, the parent task slot is temporarily released to the environment. When the parent task and the subtask share a queue, we also release the parent task slot to the queue. Again, an example will help illustrate this:/trigger/waiting-same-queue.ts
RECURSIVE_WAIT_DEADLOCK error. The following example will result in a deadlock:
/trigger/deadlock.ts
RECURSIVE_WAIT_DEADLOCK error because the parent task is waiting for the subtask, and the subtask is waiting for the subsubtask, but there is no more concurrency available in the queue. It will look a bit like this in the logs:

