Skip to content

nodetool setconcurrency

Sets the maximum concurrent operations for read, write, or counter write thread pools.


Synopsis

nodetool [connection_options] setconcurrency <type> <value>

Description

nodetool setconcurrency modifies the maximum number of concurrent operations that can execute simultaneously for a specific operation type. This controls how Cassandra's thread pools handle incoming requests and directly impacts throughput, latency, and resource utilization.

Understanding Cassandra's Threading Model (SEDA)

Cassandra uses a Staged Event-Driven Architecture (SEDA), where different types of operations are handled by dedicated thread pools (stages). Each stage has:

  • A thread pool with a configurable number of worker threads
  • A queue for requests waiting to be processed
  • Metrics for monitoring active, pending, and completed tasks

uml diagram

Concurrency Types

Type Thread Pool Default Purpose
read ReadStage 32 Local read operations (single-partition and range queries)
write MutationStage 32 Local write operations (inserts, updates, deletes)
counter_write CounterMutationStage 32 Counter increment/decrement operations

Non-Persistent Setting

This setting is applied at runtime only and does not persist across node restarts. After a restart, concurrency reverts to the settings in cassandra.yaml.

To make changes permanent, update cassandra.yaml:

concurrent_reads: 32
concurrent_writes: 32
concurrent_counter_writes: 32

Arguments

Argument Description
type Concurrency type: read, write, or counter_write
value Maximum concurrent operations (number of threads)

Examples

View Current Concurrency

nodetool getconcurrency

Set Read Concurrency

nodetool setconcurrency read 64

Set Write Concurrency

nodetool setconcurrency write 64

Set Counter Write Concurrency

nodetool setconcurrency counter_write 32

When to Adjust Concurrency

Scenario 1: Read Latency High with Pending Reads

Symptoms: - nodetool tpstats shows pending tasks in ReadStage - Read latencies increasing - CPU not fully utilized

Diagnosis:

# Check for pending reads
nodetool tpstats | grep -E "Pool Name|ReadStage"

Example output showing a problem:

Pool Name                    Active   Pending      Completed   Blocked
ReadStage                        32       245        1523456         0

Action: Increase read concurrency:

nodetool setconcurrency read 64

# Verify
nodetool getconcurrency
nodetool tpstats | grep ReadStage

Scenario 2: Write Throughput Limited

Symptoms: - Write operations queuing (pending in MutationStage) - Disk I/O not saturated - Application seeing write timeouts

Diagnosis:

nodetool tpstats | grep -E "Pool Name|MutationStage"

Action: Increase write concurrency:

nodetool setconcurrency write 64

Scenario 3: CPU Saturation

Symptoms: - CPU at 100% utilization - High context switching - Latencies spiking under load

Diagnosis:

# Check CPU
top -H -p $(pgrep -f CassandraDaemon)

# Check thread activity
nodetool tpstats

Action: Consider reducing concurrency if over-threaded:

# Too many threads can cause contention
nodetool setconcurrency read 24
nodetool setconcurrency write 24

Scenario 4: High-Core-Count Servers

Symptoms: - Server has 64+ CPU cores - Default concurrency (32) underutilizes hardware - Throughput plateaus despite available resources

Action: Scale concurrency with core count:

# For a 64-core server
nodetool setconcurrency read 64
nodetool setconcurrency write 64

Scenario 5: Heavy Counter Workload

Symptoms: - Counter operations are slow - CounterMutationStage has pending tasks

Diagnosis:

nodetool tpstats | grep -E "Pool Name|CounterMutationStage"

Action:

nodetool setconcurrency counter_write 48

Metrics to Monitor

Thread Pool Statistics

The primary tool for monitoring concurrency is nodetool tpstats:

nodetool tpstats

Key columns to watch:

Column Meaning Healthy Value
Active Threads currently processing requests < max concurrency
Pending Requests waiting in queue Should be 0 or very low
Completed Total completed operations Increasing over time
Blocked Requests rejected due to full queue Must be 0

Interpreting tpstats Output

Pool Name                    Active   Pending      Completed   Blocked
ReadStage                        32       245        1523456         0
MutationStage                    28         0        2845123         0
CounterMutationStage              2         0          45123         0

Analysis: - ReadStage: Active=32 (at max), Pending=245 (queuing) → Consider increasing read concurrency - MutationStage: Active=28, Pending=0 → Healthy, no changes needed - CounterMutationStage: Active=2, Pending=0 → Healthy, low counter activity

Warning Signs

Observation Problem Recommendation
Pending > 0 consistently Thread pool undersized Increase concurrency
Blocked > 0 Queue overflow, requests dropped Increase concurrency urgently
Active = max, high latency May need more threads or disk is bottleneck Check disk I/O first
Active low, CPU high Too many context switches May need to decrease concurrency

Monitoring Script

#!/bin/bash
# monitor_concurrency.sh - Watch thread pool health

while true; do
    clear
    echo "=== $(date) ==="
    echo ""
    echo "--- Current Concurrency Settings ---"
    nodetool getconcurrency
    echo ""
    echo "--- Thread Pool Stats ---"
    nodetool tpstats | head -20
    echo ""
    echo "--- CPU Usage ---"
    top -bn1 | head -5
    echo ""
    echo "--- Latencies ---"
    nodetool proxyhistograms | head -15
    sleep 10
done

JMX Metrics

For detailed monitoring, key JMX metrics:

Metric Path Description
org.apache.cassandra.metrics:type=ThreadPools,path=request,scope=ReadStage,name=ActiveTasks Active read threads
org.apache.cassandra.metrics:type=ThreadPools,path=request,scope=ReadStage,name=PendingTasks Queued reads
org.apache.cassandra.metrics:type=ThreadPools,path=request,scope=MutationStage,name=ActiveTasks Active write threads
org.apache.cassandra.metrics:type=ThreadPools,path=request,scope=MutationStage,name=PendingTasks Queued writes

Impact on Cluster and Clients

Increasing Concurrency

Positive effects: - Higher throughput (more requests processed in parallel) - Lower latencies (less time waiting in queue) - Better utilization of multi-core CPUs

Potential negative effects: - Increased memory usage (more threads = more stack space) - Higher CPU contention if over-subscribed - More pressure on disk I/O - Potential for increased GC pressure

Decreasing Concurrency

Positive effects: - Lower memory footprint - Reduced CPU contention - More predictable latencies under overload

Potential negative effects: - Lower throughput - Requests queue up faster - Risk of blocked requests if queue fills

Client Impact

Concurrency Change Client Experience
Too low Timeouts, slow responses, connection pool exhaustion
Optimal Consistent low latencies, high throughput
Too high May see latency spikes if resources over-subscribed

General Guidelines

Server Type CPU Cores Recommended Read Recommended Write
Small (4-8 cores) 4-8 16-32 16-32
Medium (16-32 cores) 16-32 32-64 32-64
Large (64+ cores) 64+ 64-128 64-128

Storage-Based Considerations

Storage Type Read Concurrency Write Concurrency Notes
HDD 16-32 32-64 Reads limited by seek time
SATA SSD 32-64 32-64 Balanced I/O
NVMe SSD 64-128 64-128 Can handle high parallelism

Formula Approach

A common starting point:

concurrent_reads = 16 × number_of_drives
concurrent_writes = 8 × number_of_cpu_cores

For example, with 8 cores and 4 SSDs: - Read concurrency: 16 × 4 = 64 - Write concurrency: 8 × 8 = 64

Tuning Process

  1. Start with defaults (32/32/32)
  2. Monitor tpstats for pending tasks
  3. If pending consistently > 0, increase by 50%
  4. Monitor CPU and memory impact
  5. Repeat until balanced

Relationship to Other Settings

Native Transport Requests

# cassandra.yaml
native_transport_max_threads: 128  # Threads handling client connections

The native transport threads hand off work to the stage pools. If native_transport_max_threads is high but concurrency is low, requests will queue.

Concurrent Compactors

concurrent_compactors: 4  # Separate from read/write concurrency

Compaction has its own thread pool and doesn't compete with read/write concurrency settings.

Memory Implications

Each thread requires stack space:

Memory per thread ≈ 256KB (default stack size)
64 threads ≈ 16MB stack space
128 threads ≈ 32MB stack space

High concurrency values increase overall heap pressure indirectly.


Cluster-Wide Configuration

Apply to All Nodes

#!/bin/bash
# set_concurrency_cluster.sh

READ_CONCURRENCY="${1:-32}"
WRITE_CONCURRENCY="${2:-32}"# Get list of node IPs from local nodetool status


nodes=$(nodetool status | grep "^UN" | awk '{print $2}')

echo "Setting concurrency across cluster..."
echo "Read: $READ_CONCURRENCY, Write: $WRITE_CONCURRENCY"
echo ""

for node in $nodes; do
    echo "=== $node ==="
    ssh "$node" "nodetool setconcurrency read $READ_CONCURRENCY"
    ssh "$node" "nodetool setconcurrency write $WRITE_CONCURRENCY"
    ssh "$node" "nodetool getconcurrency"
    echo ""
done

Making Changes Permanent

# cassandra.yaml
concurrent_reads: 64
concurrent_writes: 64
concurrent_counter_writes: 32

Troubleshooting

Pending Tasks Not Decreasing After Increase

# Check if disk is the bottleneck
iostat -x 1 5

# If disk is at 100% util, concurrency won't help
# Consider:
# - Faster storage
# - Better data model (fewer reads/writes)
# - More nodes

High CPU After Increasing Concurrency

# Check for excessive context switching
vmstat 1 5

# If 'cs' (context switches) is very high, reduce concurrency
nodetool setconcurrency read 32

Blocked Tasks Appearing

# Blocked means queue overflow - serious issue
nodetool tpstats | grep -E "Pool Name|Blocked"

# Immediate actions:
# 1. Increase concurrency
nodetool setconcurrency read 128

# 2. Check for resource bottlenecks
iostat -x 1 3
top -H

# 3. Consider if cluster is undersized

Changes Not Taking Effect

# Verify change applied
nodetool getconcurrency

# Check logs for errors
tail -100 /var/log/cassandra/system.log | grep -i concurrency

# Try again
nodetool setconcurrency read 64

Best Practices

Concurrency Guidelines

  1. Start conservative - Begin with defaults, increase based on metrics
  2. Monitor continuously - Watch tpstats before and after changes
  3. Balance resources - Don't set concurrency higher than available CPU cores
  4. Consider storage - HDDs need lower concurrency than SSDs
  5. Test under load - Validate changes during realistic traffic
  6. Make permanent - Update cassandra.yaml after validating
  7. Apply cluster-wide - Keep settings consistent across nodes

Common Mistakes

  • Setting concurrency very high without monitoring impact
  • Ignoring disk I/O when tuning (disk is often the bottleneck)
  • Different settings on different nodes (causes imbalanced load)
  • Forgetting to persist changes (lost on restart)
  • Not monitoring blocked tasks (indicates dropped requests)

Command Relationship
getconcurrency View current concurrency settings
tpstats Monitor thread pool statistics
proxyhistograms View read/write latency distributions
info General node information