Class ExecutionLimitsBuilder
- Namespace
- Quartz
- Assembly
- Quartz.dll
Builds the ExecutionLimits a scheduler applies when it acquires triggers.
public sealed class ExecutionLimitsBuilder
- Inheritance
-
ExecutionLimitsBuilder
- Inherited Members
Examples
ExecutionLimits limits = ExecutionLimitsBuilder.Create()
.ForGroup("high-cpu", 2) // two on this node
.ForGroup("tenant-acme", 8, ExecutionLimitScope.Cluster) // eight across the cluster
.ForGroupsWithPrefix("tenant:", 2, ExecutionLimitScope.Cluster) // two for each other tenant
.ForOtherGroups(5)
.Build();
Remarks
The builder is mutable and the ExecutionLimits that Build() returns is not, so a snapshot handed to SetExecutionLimits(ExecutionLimits?, CancellationToken) cannot change underneath the scheduler thread that reads it.
UseExecutionLimits(Action<ExecutionLimitsBuilder>) hands one of these to a callback, which is the usual way to configure limits; its UseExecutionLimits(IQuartzBuilder, Action<IServiceProvider, ExecutionLimitsBuilder>) sibling does the same once the container exists, for a limit that comes from a service.
Methods
Build()
Takes an immutable snapshot of what has been configured so far.
public ExecutionLimits Build()
Returns
Create()
Create an ExecutionLimitsBuilder with no limits configured.
public static ExecutionLimitsBuilder Create()
Returns
- ExecutionLimitsBuilder
the new ExecutionLimitsBuilder
ForDefaultGroup(int, ExecutionLimitScope)
Set the concurrency limit for triggers that have no execution group.
public ExecutionLimitsBuilder ForDefaultGroup(int maxConcurrent, ExecutionLimitScope scope = ExecutionLimitScope.Node)
Parameters
maxConcurrentintMaximum concurrent threads (must be >= 0), or
0to forbid execution.scopeExecutionLimitScopeWhether the limit counts what this node runs or what the whole cluster runs.
Returns
- ExecutionLimitsBuilder
This builder for fluent chaining.
Exceptions
- ArgumentOutOfRangeException
maxConcurrentis negative, orscopeis not one of the defined values.
ForGroup(string, int, ExecutionLimitScope)
Set the concurrency limit for a named execution group.
public ExecutionLimitsBuilder ForGroup(string group, int maxConcurrent, ExecutionLimitScope scope = ExecutionLimitScope.Node)
Parameters
groupstringThe execution group name.
maxConcurrentintMaximum concurrent threads (must be >= 0), or
0to forbid execution.scopeExecutionLimitScopeWhether the limit counts what this node runs or what the whole cluster runs. Node-scoped unless said otherwise, which is what execution limits have always meant.
Returns
- ExecutionLimitsBuilder
This builder for fluent chaining.
Exceptions
- ArgumentNullException
groupis null.- ArgumentException
groupis a reserved name, or ends with*, which is how a configuration key names a prefix: use ForGroupsWithPrefix(string, int, ExecutionLimitScope).- ArgumentOutOfRangeException
maxConcurrentis negative, orscopeis not one of the defined values.
ForGroupsWithPrefix(string, int, ExecutionLimitScope)
Set the concurrency limit for every execution group that starts with a prefix. Each such group gets
the limit on its own: ForGroupsWithPrefix("tenant:", 2) lets tenant:acme and
tenant:initech run two each, not two between them.
public ExecutionLimitsBuilder ForGroupsWithPrefix(string prefix, int maxConcurrent, ExecutionLimitScope scope = ExecutionLimitScope.Node)
Parameters
prefixstringThe start of the group names, compared ordinally. Configuration spells it
quartz.executionLimit.tenant:; theis not part of the prefix.maxConcurrentintMaximum concurrent threads for each group (must be >= 0), or
0to forbid execution.scopeExecutionLimitScopeWhether each group's limit counts what this node runs or what the whole cluster runs.
Returns
- ExecutionLimitsBuilder
This builder for fluent chaining.
Remarks
This is ForOtherGroups(int, ExecutionLimitScope) narrowed to a family of groups, and it is decided in the same place: a group's own ForGroup(string, int, ExecutionLimitScope) or Unlimited(string) wins, then the longest prefix the group starts with, then the catch-all. A prefix equal to a named group's whole name therefore governs the other groups that start with it and not that one. Triggers with no execution group are never matched.
Each group is counted on its own, and a group with nothing in flight holds no count, so a family of many tenants costs what those running at the moment cost.
Exceptions
- ArgumentNullException
prefixis null.- ArgumentException
prefixis blank, or ends with*.- ArgumentOutOfRangeException
maxConcurrentis negative, orscopeis not one of the defined values.
ForOtherGroups(int, ExecutionLimitScope)
Set the default concurrency limit applied to any execution group not explicitly configured.
public ExecutionLimitsBuilder ForOtherGroups(int maxConcurrent, ExecutionLimitScope scope = ExecutionLimitScope.Node)
Parameters
maxConcurrentintMaximum concurrent threads (must be >= 0), or
0to forbid execution.scopeExecutionLimitScopeWhether the limit counts what this node runs or what the whole cluster runs.
Returns
- ExecutionLimitsBuilder
This builder for fluent chaining.
Remarks
The catch-all hands each unlisted group an allowance of its own rather than one they share, and
that holds whichever scope it is declared in: ForOtherGroups(1, ExecutionLimitScope.Cluster)
lets three unlisted tenants run one job each across the cluster, not one job between them.
Exceptions
- ArgumentOutOfRangeException
maxConcurrentis negative, orscopeis not one of the defined values.
Unlimited(string)
Mark a group as having no concurrency limit (unlimited).
public ExecutionLimitsBuilder Unlimited(string group)
Parameters
groupstringThe execution group name.
Returns
- ExecutionLimitsBuilder
This builder for fluent chaining.
Remarks
This is not the same as leaving the group out: an unlisted group falls back to ForOtherGroups(int, ExecutionLimitScope), while an explicitly unlimited one does not. It takes no scope, because unlimited on one node and unlimited across the cluster are the same permission.
Exceptions
- ArgumentNullException
groupis null.- ArgumentException
groupis a reserved name.
UseTriggerGroupWhenUnset()
Treats a trigger that carries no execution group as belonging to a group named after its own Group, for the purpose of these limits.
public ExecutionLimitsBuilder UseTriggerGroupWhenUnset()
Returns
Remarks
For a schedule that already partitions work by trigger group — a tenant per group, a subsystem per group — this caps each partition without restating every group name as an execution group on every trigger. ForGroup(string, int, ExecutionLimitScope) then names trigger groups, and ForOtherGroups(int, ExecutionLimitScope) caps the ones not named.
The derivation is applied where a limit is evaluated and nowhere else: the trigger still carries
no execution group, and the store still persists none. A trigger that does carry one is limited
by that one. Two consequences worth knowing: ungrouped triggers stop falling under
ForDefaultGroup(int, ExecutionLimitScope) — with this on, nothing is ungrouped — and a trigger whose group
happens to be a name the limits reserve (*, _, null) is left ungrouped
rather than folded into the bucket that name means.