Task Queue Priority and Fairness
Task Queue Priority and Task Queue Fairness are two ways to manage the distribution of work within Task Queues. Priority focuses on how Tasks are prioritized within a single Task Queue. Fairness aims to prevent one set of Tasks from blocking others within the same priority level.
You can use Priority and Fairness individually within your Task Queues or you can use them together for more complex scenarios. We recommend using them individually to keep your Task Queues easier to manage.
Priority and Fairness is currently in Public Preview. Please contact your AE or Support to enable this feature.
Task Queue Priority
Task Queue Priority lets you control the execution order of Workflows, Activities, and Child Workflows based on assigned priority values within a single Task Queue. Each priority level acts as a "virtual" queue that prioritizes Tasks within a single Task Queue.

Detailed view of how Priority works
When to use Priority
If you need a way to specify the order your Tasks execute in, you can use Priority to manage that. Priority lets you differentiate between your Tasks, like batch and real-time Tasks, so that you can allocate resources more effectively. You can also use this as a way to run urgent Tasks immediately and override others. For example, if you are running an e-commerce platform, you may want to process payment related Tasks before less time-sensitive Tasks like internal inventory management.
How to use Priority
Priority is avaliable for both self-hosted Temporal instances and Temporal Cloud and it will be automatically enabled. If you apply priority keys, the feature will be in use.
You can select a priority level by setting the priority key to a value within the integer range [1,5]. A lower value
implies higher priority, so 1 is the highest priority level. If you don't specify a Priority, a Task defaults to a
Priority of 3. Activities will inherit the priority level of their Workflow if a separate Activity priority level
isn't set.
When you set a priority level within your Task Queues, this means that they will all be processed in priority order.
For example, all of your priority level 1 Tasks will execute before your priority level 2 Tasks and so on. So your
lower priority Tasks will be blocked until the higher priority Tasks finish running. Tasks with the same priority level
are scheduled to run in first-in-first-out (FIFO) order. If you need more flexibility to allocate resources to Tasks of
the same type, like processing payments for multiple e-commerce platforms, check out
the fairness section.
When you update priority levels, the Task Queues will only reflect these changes for Tasks that haven't dispatched yet, not Tasks that are already queued.
Choose your SDK below to see an example of setting priority for your Workflows:
workflowOptions := client.StartWorkflowOptions{
ID: "my-workflow-id",
TaskQueue: "my-task-queue",
Priority: temporal.Priority{PriorityKey: 5},
}
we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, MyWorkflow)
WorkflowOptions options = WorkflowOptions.newBuilder()
.setTaskQueue("my-task-queue")
.setPriority(Priority.newBuilder().setPriorityKey(5).build())
.build();
WorkflowClient client = WorkflowClient.newInstance(service); MyWorkflow workflow =
client.newWorkflowStub(MyWorkflow.class, options); workflow.run();
await client.start_workflow(
MyWorkflow.run,
args="hello",
id="my-workflow-id",
task_queue="my-task-queue",
priority=Priority(priority_key=1),
)
var handle = await Client.StartWorkflowAsync(
(MyWorkflow wf) => wf.RunAsync("hello"),
new StartWorkflowOptions(
id: "my-workflow-id",
taskQueue: "my-task-queue"
)
{
Priority = new Priority(1),
}
);
Choose your SDK below to see an example of setting priority for your Activities:
ao := workflow.ActivityOptions{
StartToCloseTimeout: time.Minute,
Priority: temporal.Priority{PriorityKey: 3},
}
ctx := workflow.WithActivityOptions(ctx, ao)
err := workflow.ExecuteActivity(ctx, MyActivity).Get(ctx, nil)
ActivityOptions options = ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofMinutes(1))
.setPriority(Priority.newBuilder().setPriorityKey(3).build())
.build();
MyActivity activity = Workflow.newActivityStub(MyActivity.class, options); activity.perform();
await workflow.execute_activity(
say_hello,
"hi",
priority=Priority(priority_key=3),
start_to_close_timeout=timedelta(seconds=5),
)
await Workflow.ExecuteActivityAsync(
() => SayHello("hi"),
new()
{
StartToCloseTimeout = TimeSpan.FromSeconds(5),
Priority = new(3),
}
);
Choose your SDK below to see an example of setting priority for your Child Workflows:
cwo := workflow.ChildWorkflowOptions{
WorkflowID: "child-workflow-id",
TaskQueue: "child-task-queue",
Priority: temporal.Priority{PriorityKey: 1},
}
ctx := workflow.WithChildOptions(ctx, cwo)
err := workflow.ExecuteChildWorkflow(ctx, MyChildWorkflow).Get(ctx, nil)
ChildWorkflowOptions childOptions = ChildWorkflowOptions.newBuilder()
.setTaskQueue("child-task-queue")
.setWorkflowId("child-workflow-id")
.setPriority(Priority.newBuilder().setPriorityKey(1).build())
.build();
MyChildWorkflow child = Workflow.newChildWorkflowStub(MyChildWorkflow.class, childOptions); child.run();
await workflow.execute_child_workflow(
MyChildWorkflow.run,
args="hello child",
priority=Priority(priority_key=1),
)
await Workflow.ExecuteChildWorkflowAsync(
(MyChildWorkflow wf) => wf.RunAsync("hello child"),
new() { Priority = new(1) }
);
Task Queue Fairness
Task Queue Fairness lets you distribute Tasks based on fairness keys and their fairness weight within a Task Queue. The fairness keys are used to describe your Task structure. A fairness key can correspond to different tier levels, tenants, or other groupings useful to your workloads.
The Tasks associated with each fairness key are dispatched based on the fairness weight that has been assigned to the key. There should only be one fairness weight assigned to each fairness key within a single Task Queue. Having multiple fairness weights on a fairness key will result in unspecific behavior.
Using the fairness keys and their corresponding fairness weights lets you define levels with weighted capacities. For example, you can have free, basic, and premium levels and Fairness makes sure that an influx of premium Tasks don't overwhelm your resources and block free and basic Tasks.
When you start using fairness keys, it switches your active Task Queues to fairness mode. That means new Tasks are created with Fairness and the existing queued Tasks get processed before any of the new ones.
When to use Fairness
As an example, imagine a workload with three tenants, tenant-big, tenant-mid, tenant-small, that have varying numbers of Tasks at all times. Your tenant-big has a large number of Tasks that can overwhelm your Task Queue and prevent tenant-mid and tenant-small from running their Tasks. With Fairness, you can give each tenant a different fairness weight to make sure tenant-big doesn't use all of the Task Queue resources and block the others.
In this case, tenant-mid and tenant-small will have Tasks run in between tenant-big Tasks so that they are executed "fairly". Although, if all your Tasks can be dispatched immediately, then you don't need to use fairness.
This same scenario can apply to batch jobs where certain jobs run more often than others or processing orders from multiple vendors where several vendors have the majority of the orders.
Fairness applies at Task dispatch time based on information about the Tasks passing through the Task Queue and considers each Task as having equal cost until dispatch. It doesn't consider any Task execution that is currently being done by Workers. So if you look at Tasks being processed by Workers, you might not see "fairness" across tenants. For example, if you already have Tasks from tenant-big being processed, when Tasks for tenant-small are dispatched it may still look like tenant-big is using the most resources.
There are two ways to use Task Queue Fairness: without Priority and with Priority.
How Fairness works
If you implement Fairness without Priority, Tasks with different fairness keys will use a weighted distribution based on the fairness weights to allocate resources in the Task Queue. For example, say you have three fairness keys to describe customer tiers: free-tier, basic-tier, and premium-tier. You give premium-tier a fairness weight of 5.0, basic-tier a fairness weight of 3.0, and free-tier a fairness weight of 2.0. With Fairness, that means 50% of the time premium-tier Tasks dispatch, 30% of the time basic-tier Tasks dispatch, and 20% of the time free-tier Tasks dispatch from the Task Queue backlog.
If there are Tasks in the Task Queue backlog that have the same fairness key, then they're dispatched in FIFO order.
This is how you are able to ensure that one tier doesn't use all the resources and block other Tasks in the Task Queue backlog from dispatching.

High-level of how priority and fairness work together
Using Fairness and Priority together
When you use Fairness and Priority together, Priority determines which "bucket" Tasks go into. A single Task Queue with Priority implemented will have different "buckets" based on priority levels. Fairness will apply to the Tasks within each priority level. It sequences Tasks in the Task Queue probabilistically using a weighted distribution, like explained above, based on:
- Fairness weights you set
- The current backlog of Tasks
- A data structure that tracks how you've distributed Tasks for different fairness keys
It’s intended to address common situations like:
- Multi-tenant applications with big and small tenants where small tenants shouldn't be blocked by big ones.
- Assigning Tasks to different capacity bands and then, for example, dispatching 80% from one band and 20% from another without limiting overall capacity when one band is empty.

High-level of how priority and fairness work together
When you update fairness keys or fairness weights, the Task Queues will only reflect these changes for Tasks that haven't dispatched yet, not Tasks that are already queued.
How to use Fairness
Fairness is avaliable for both self-hosted Temporal instances and Temporal Cloud.
If you're self-hosting Temporal, use the latest pre-release development server and set matching.useNewMatcher and
matching.enableFairness to true in the
dynamic config
on the relevant Task Queues or Namespaces.
Enabling matching.useNewMatcher and matching.enableFairness is only applicable for self-hosted Temporal instances.
There is a toggle coming to Temporal Cloud soon to enable Priority and Fairness at the Namespace level.
Fairness will be a paid feature in Temporal Cloud and billing will be enabled in the near future. You will be notified before billing is enabled for your Namespaces. You will be able to enable or disable Fairness at the Namespace level and billing will be disabled at the next calendar hour after it is disabled.
Choose your SDK below to see an example of setting fairness for your Workflows:
workflowOptions := client.StartWorkflowOptions{
ID: "my-workflow-id",
TaskQueue: "my-task-queue",
Priority: temporal.Priority{
PriorityKey: 1,
FairnessKey: "a-key",
FairnessWeight: 3.14,
},
}
we, err := c.ExecuteWorkflow(context.Background(), workflowOptions, MyWorkflow)
WorkflowOptions options = WorkflowOptions.newBuilder()
.setTaskQueue("my-task-queue")
.setPriority(Priority.newBuilder().setPriorityKey(5).setFairnessKey("a-key").setFairnessWeight(3.14).build())
.build();
WorkflowClient client = WorkflowClient.newInstance(service);
MyWorkflow workflow = client.newWorkflowStub(MyWorkflow.class, options);
workflow.run();
await client.start_workflow(
MyWorkflow.run,
args="hello",
id="my-workflow-id",
task_queue="my-task-queue",
priority=Priority(priority_key=3, fairness_key="a-key", fairness_weight=3.14),
)
const handle = await startWorkflow(workflows.priorityWorkflow, {
args: [false, 1],
priority: { priorityKey: 3, fairnessKey: 'a-key', fairnessWeight: 3.14 },
});
var handle = await Client.StartWorkflowAsync(
(MyWorkflow wf) => wf.RunAsync("hello"),
new StartWorkflowOptions(
id: "my-workflow-id",
taskQueue: "my-task-queue"
)
{
Priority = new Priority(
priorityKey: 3,
fairnessKey: "a-key",
fairnessWeight: 3.14
)
}
);
client.start_workflow(
MyWorkflow, "input-arg",
id: "my-workflow-id",
task_queue: "my-task-queue",
priority: Temporalio::Priority.new(
priority_key: 3,
fairness_key: "a-key",
fairness_weight: 3.14
)
)
Choose your SDK below to see an example of setting fairness for your Activities:
ao := workflow.ActivityOptions{
Stardd
}
ctx := workflow.WithActivityOptions(ctx, ao)
err := workflow.ExecuteActivity(ctx, MyActivity).Get(ctx, nil)
ActivityOptions options = ActivityOptions.newBuilder()
.setStartToCloseTimeout(Duration.ofMinutes(1))
.setPriority(Priority.newBuilder().setPriorityKey(3).setFairnessKey("a-key").setFairnessWeight(3.14).build())
.build();
MyActivity activity = Workflow.newActivityStub(MyActivity.class, options);
activity.perform();
await workflow.execute_activity(
say_hello,
"hi",
priority=Priority(priority_key=3, fairness_key="a-key", fairness_weight=3.14),
start_to_close_timeout=timedelta(seconds=5),
)
const handle = await startWorkflow(workflows.priorityWorkflow, {
args: [false, 1],
priority: { priorityKey: 3, fairnessKey: 'a-key', fairnessWeight: 3.14 },
});
var handle = await Client.StartWorkflowAsync(
(MyWorkflow wf) => wf.RunAsync("hello"),
new StartWorkflowOptions(
id: "my-workflow-id",
taskQueue: "my-task-queue"
)
{
Priority = new Priority(
priorityKey: 3,
fairnessKey: "a-key",
fairnessWeight: 3.14
)
}
);
client.start_activity(
MyActivity, "input-arg",
id: "my-workflow-id",
task_queue: "my-task-queue",
priority: Temporalio::Priority.new(
priority_key: 3,
fairness_key: "a-key",
fairness_weight: 3.14
)
)
Choose your SDK below to see an example of setting fairness for your Child Workflows:
cwo := workflow.ChildWorkflowOptions{
WorkflowID: "child-workflow-id",
TaskQueue: "child-task-queue",
Priority: temporal.Priority{
PriorityKey: 1,
FairnessKey: "a-key",
FairnessWeight: 3.14,
},
}
ctx := workflow.WithChildOptions(ctx, cwo)
err := workflow.ExecuteChildWorkflow(ctx, MyChildWorkflow).Get(ctx, nil)
ChildWorkflowOptions childOptions = ChildWorkflowOptions.newBuilder()
.setTaskQueue("child-task-queue")
.setWorkflowId("child-workflow-id")
.setPriority(Priority.newBuilder().setPriorityKey(1).setFairnessKey("a-key").setFairnessWeight(3.14).build())
.build();
MyChildWorkflow child = Workflow.newChildWorkflowStub(MyChildWorkflow.class, childOptions);
child.run();
await workflow.execute_child_workflow(
MyChildWorkflow.run,
args="hello child",
priority=Priority(priority_key=3, fairness_key="a-key", fairness_weight=3.14),
)
const handle = await startChildWorkflow(workflows.priorityWorkflow, {
args: [false, 1],
priority: { priorityKey: 3, fairnessKey: 'a-key', fairnessWeight: 3.14 },
});
var handle = await Client.StartWorkflowAsync(
(MyWorkflow wf) => wf.RunAsync("hello"),
new StartWorkflowOptions(
id: "my-workflow-id",
taskQueue: "my-task-queue"
)
{
Priority = new Priority(
priorityKey: 3,
fairnessKey: "a-key",
fairnessWeight: 3.14
)
}
);
client.start_child_workflow(
MyChildWorkflow, "input-arg",
id: "my-child-workflow-id",
task_queue: "my-task-queue",
priority: Temporalio::Priority.new(
priority_key: 3,
fairness_key: "a-key",
fairness_weight: 3.14
)
)
Limitations of Fairness
When you use Worker Versioning and you're moving Workflows from one version to another, the ordering of Workflow Tasks that are moved to the next version is undefined. Tasks redirected to a new Worker version may not be treated fairly with respect to each other or Tasks that aren't redirected.
There isn't a limit on the number of fairness keys you can use, but their accuracy can degrade as you add more.
Task Queues are internally partitioned and Tasks are distributed to partitions randomly. This could interfer with fairnesss. Depending on your use case, you can reach out to Temporal Support to get your Task Queues set to a single partition.