Domain Isolation Configuration
Domain isolation features in Cadence help eliminate noisy neighbor problems by providing stronger isolation between domains at the history task processing level. This guide covers the configuration and deployment of History Queue V2 and the domain-aware Task Scheduler.
For the story behind these features and performance improvements observed at Uber, see the Eliminating Noisy Neighbors blog post.
Prerequisites
To use these features, upgrade Cadence Server to v1.4.1 or later.
History Queue V2 is available starting from v1.4.0, but some additional metrics like task_latency_per_domain_ns were added in v1.4.1.
Configuration
History Queue V2
History Queue V2 provides domain-level isolation through virtual queues and prevents one slow domain from blocking others.
Feature Flags:
-
history.enableTransferQueueV2- Description: Enables History Queue V2 for the transfer queue
- Type: Bool
- Default:
false - Requires restart: Yes
- Filters:
ShardID
-
history.enableTimerQueueV2- Description: Enables History Queue V2 for the timer queue
- Type: Bool
- Default:
false - Requires restart: Yes
- Filters:
ShardID
-
history.enableTransferQueueV2PendingTaskCountAlert- Description: Enables virtual queue splitting for the transfer queue when the pending task count exceeds the configured threshold
- Type: Bool
- Default:
false - Filters:
ShardID
-
history.enableTimerQueueV2PendingTaskCountAlert- Description: Enables virtual queue splitting for the timer queue when the pending task count exceeds the configured threshold
- Type: Bool
- Default:
false - Filters:
ShardID
-
history.queueMaxPendingTaskCount- Description: Defines the maximum number of pending tasks allowed in a history queue per shard. Once this limit is reached, the queue stops reading additional tasks from the database until the pending task count decreases.
- Type: Int
- Default:
10000
-
history.queueCriticalPendingTaskCount- Description: Defines the pending task count threshold that triggers a virtual queue split. Must be less than
history.queueMaxPendingTaskCount. - Type: Int
- Default:
9000
- Description: Defines the pending task count threshold that triggers a virtual queue split. Must be less than
History Task Scheduler
The Task Scheduler provides domain-level rate limiting and fair scheduling across domains.
Feature Flags:
-
history.taskSchedulerGlobalDomainRPS- Description: Defines the global task processing rate limit, in requests per second (RPS), for each domain
- Type: Int
- Default:
1000 - Filters:
DomainName
-
history.taskSchedulerEnableRateLimiter- Description: Enables rate limiting in the History Task Scheduler. When enabled, task processing is throttled according to the configured domain-level rate limits.
- Type: Bool
- Default:
false
-
history.taskSchedulerEnableRateLimiterShadowMode- Description: Enables shadow mode for the History Task Scheduler rate limiter. In shadow mode, rate-limit decisions are evaluated and recorded for observability, but tasks are not actually throttled.
- Type: Bool
- Default:
true - Filters:
DomainName
-
history.taskSchedulerDomainRoundRobinWeight- Description: Defines a per-domain map from numeric task priorityID to the weight used by the task scheduler's weighted round-robin algorithm. By default, tasks with the same priority but from different domains have the same weight.
- Type: Map
- Default:
{"1": 500, "9": 20, "17": 5} - Filters:
DomainName
Step-by-Step Enablement
1. Update Cadence Server
Deploy Cadence Server v1.4.1 or later to your cluster.
2. Enable History Queue V2
Set the following dynamic configuration values and restart the cadence-history service:
history.enableTransferQueueV2:
- value: true
constraints: {}
history.enableTimerQueueV2:
- value: true
constraints: {}
These flags require a service restart to take effect. Plan your deployment accordingly.
3. Enable Virtual Queue Splitting
Once you are confident that History Queue V2 is operating as expected, enable virtual queue splitting:
history.enableTransferQueueV2PendingTaskCountAlert:
- value: true
constraints: {}
history.enableTimerQueueV2PendingTaskCountAlert:
- value: true
constraints: {}
4. Enable Task Scheduler Rate Limiter (Shadow Mode)
Enable the task scheduler rate limiter in shadow mode to start collecting metrics:
history.taskSchedulerEnableRateLimiter:
- value: true
constraints: {}
With shadow mode enabled by default (history.taskSchedulerEnableRateLimiterShadowMode: true), rate-limit decisions are evaluated and logged, but tasks are not actually throttled.
5. Tune Rate Limits Per Domain (Optional)
Monitor the metrics and adjust the rate limits for specific domains as needed:
history.taskSchedulerGlobalDomainRPS:
- value: 500
constraints:
domainName: latency-sensitive-domain
- value: 2000
constraints:
domainName: high-throughput-domain
- value: 1000
constraints: {}
6. Disable Shadow Mode for Specific Domains (Optional)
Once you've validated the rate limits, you can disable shadow mode for specific domains to enforce actual throttling:
history.taskSchedulerEnableRateLimiterShadowMode:
- value: false
constraints:
domainName: noisy-domain
- value: true
constraints: {}
Disabling shadow mode is not recommended for latency-sensitive domains, as active rate limiting may increase task processing latency.
Monitoring and Observability
Key Metrics
Task Scheduler Metrics
These metrics become available after setting history.taskSchedulerEnableRateLimiter to true.
-
task_scheduler_allowed_counter_per_domain- Measures the number of tasks per domain that are allowed to proceed by the task scheduler's rate limiter
- Use: Monitor task scheduling QPS for each domain and tune
history.taskSchedulerGlobalDomainRPSaccordingly
-
task_scheduler_throttled_counter_per_domain- Measures the number of tasks per domain that are throttled by the task scheduler's rate limiter
- Use: Identify domains hitting rate limits
Task Processing Metrics
-
task_requests_per_domain- Measures the QPS of task processing broken down by domain
- Use: Monitor overall task processing load per domain
-
task_latency_per_domain- Measures task processing latency broken down by domain
- Use: Identify domains with high latency
-
task_latency_per_domain_ns- Histogram of task processing latency broken down by domain (available in v1.4.1+)
- Use: Detailed latency distribution analysis
-
task_latency- Aggregated task processing latency across all domains
- Use: Monitor overall cluster health
-
task_latency_ns- Aggregated histogram of task processing latency across all domains (available in v1.4.1+)
- Use: Monitor overall cluster health
A QPS spike in one domain accompanied by increased latency in other domains may indicate that a high-traffic domain is a noisy neighbor. Use the per-domain task latency metrics to identify such domains.
Log Messages
Queue Paused Due to Too Many Pending Tasks
{
"level": "warn",
"msg": "Too many pending tasks, pause loading tasks for a while",
"service": "cadence-history",
"shard-id": "<shard-id>",
...
}
This indicates that the queue is paused because the pending task count exceeded history.queueMaxPendingTaskCount for that shard. The queue will resume once the pending task count decreases.
Troubleshooting
High Pending Task Count
Symptom: Frequent "Too many pending tasks" warnings in logs
Possible Causes:
- A domain is generating tasks faster than they can be processed
- Downstream services are slow or unavailable
- Insufficient worker capacity
Solutions:
- Check if a specific domain is responsible using domain-level metrics
- Enable virtual queue splitting if not already enabled
- Consider adjusting
history.queueMaxPendingTaskCountandhistory.queueCriticalPendingTaskCountbased on your cluster's capacity - Scale up workers or optimize activity/workflow implementations
Increased Latency After Disabling Shadow Mode
Symptom: Task processing latency increased after enabling rate limiting
Possible Causes:
- Rate limits are too conservative
- Shadow mode was disabled prematurely
Solutions:
- Review and adjust
history.taskSchedulerGlobalDomainRPSbased on actual workload - Consider keeping shadow mode enabled for latency-sensitive domains
Best Practices
-
Gradual Rollout: Enable History Queue V2 on a subset of shards first (using
ShardIDfilter) to validate behavior before rolling out cluster-wide. -
Monitor Before Tuning: Collect metrics for at least a few days before adjusting rate limits to understand normal and peak traffic patterns.
-
Enable in Shadow Mode First: Always enable the task scheduler rate limiter in shadow mode first to collect baseline metrics before enforcing limits.
-
Keep Shadow Mode for Critical Domains: For latency-sensitive or business-critical domains, keep the rate limiter in shadow mode to maintain visibility without risking increased latency.
-
Set Conservative Limits: When disabling shadow mode, start with conservative rate limits and gradually increase based on observed metrics.
-
Document Domain-Specific Limits: Maintain documentation of which domains have custom rate limits and why, to help with troubleshooting and future tuning.
Related Documentation
- Monitoring Guide: General monitoring and metrics documentation
- Setup Guide: Dynamic configuration setup
- Eliminating Noisy Neighbors Blog Post: Background and performance improvements