Control how many instances of a task or workflow run simultaneously.
Concurrency limits let you cap how many runs of a task or workflow execute at the same time. Common use cases include:
Per-user throttling — allow only one active job per user at a time
Per-tenant fairness — prevent any single tenant from monopolizing worker slots
Resource protection — limit concurrent calls to a downstream service
Hatchet evaluates concurrency rules at the workflow level. When a new run arrives and the limit is already reached, Hatchet applies the configured strategy to decide whether to cancel a running workflow, queue the new one, or fairly rotate across groups.
Pass an integer to concurrency to apply a simple cap across all runs of that workflow.
Python
TypeScript
Go
worker.py
from hatchet_sdk import Hatchethatchet = Hatchet(debug=True)# Allow at most 5 concurrent runs of this workflowsimple_workflow = hatchet.workflow( name="SimpleWorkflow", concurrency=5,)
Under the hood, passing an integer is shorthand for a ConcurrencyExpression with expression='constant' and limit_strategy=GROUP_ROUND_ROBIN.
Use a CEL expression to group runs dynamically. Each unique value of the expression forms its own concurrency group, and max_runs applies independently per group.
A CEL expression that determines the concurrency group for each run. Each unique result value forms an independent group with its own max_runs counter. For example, "input.user_id" creates a separate limit per user.
When a new run arrives and the group is full, Hatchet cancels the oldest running workflow in that group and starts the new one.Use this when the latest request supersedes earlier ones — for example, re-processing a document after it has been edited.
When the group is full, the incoming run is cancelled and the currently running workflows are left untouched. Use this when the work already in progress is more important than the new request.
Runs are distributed fairly across all active concurrency groups. When a slot opens up, Hatchet picks the group that has waited the longest and starts its next queued run.
GROUP_ROUND_ROBIN is a fairness strategy. In a multi-tenant system, it ensures no single tenant can starve others even if it submits many more jobs. This is also the strategy used when you pass a plain integer to concurrency.
Pass a list of ConcurrencyExpression objects to enforce several independent limits simultaneously. A run must satisfy all keys to proceed.For example, you can limit runs per tier (max 20 across the tier) and also per account (max 20 within the account) at the same time: