Consistency Levels and Quorum Tuning in Cassandra 4.x and 5.x

Consistency level (CL) is the single per-query knob that decides how many replicas must acknowledge a read or a write before Cassandra returns to the client, and it is the one operators most often set on reflex and regret later. Choose it too weak and reads can miss the newest write or surface a value a concurrent write already superseded; choose it too strong and a single slow replica or a cross-region round trip drags every request into timeout territory. This guide is for DBAs, distributed-systems engineers, and SREs who need to reason about the trade-off deliberately — it sits beneath the broader Cassandra architecture and compaction fundamentals guide, which frames how the replication and reconciliation machinery fits together, and drills into how the consistency ladder, the R + W > RF inequality, and per-datacenter quorums actually behave on Cassandra 4.x and 5.x. Reach for it when you are picking a CL for a new keyspace, debugging an UnavailableException after a node loss, or watching multi-datacenter reads blow past their latency budget.

How Consistency Levels Map onto Replicas

Every keyspace declares a replication factor (RF) per datacenter through its replication strategy, and consistency level is layered on top at query time: RF says how many copies exist, CL says how many of them must answer this specific request. Cassandra tunes consistency per operation, so the same table can be written at LOCAL_QUORUM by a batch job and read at ONE by a dashboard. The coordinator that receives your query is responsible for fanning it out to the replicas that own the partition — determined by the token ring placement — waiting for the number of acknowledgements the CL demands, and only then answering the client.

The consistency ladder runs from weakest to strongest:

  • ANY (writes only) — the write is satisfied even if only a hinted handoff is stored on a non-replica coordinator. It guarantees durability of intent, not visibility; a subsequent read at any level may not see it until the hint replays. Rarely appropriate outside fire-and-forget ingestion.
  • ONE / LOCAL_ONE — one replica must respond. ONE accepts a replica in any datacenter; LOCAL_ONE forces that replica to be in the coordinator’s local datacenter, which keeps latency predictable and stops a read from silently crossing a WAN link.
  • QUORUM / LOCAL_QUORUM — a strict majority must respond. QUORUM counts replicas cluster-wide; LOCAL_QUORUM counts only the local datacenter’s replicas. LOCAL_QUORUM is the workhorse for multi-datacenter deployments because it delivers a majority guarantee without a cross-region round trip.
  • EACH_QUORUM (writes primarily) — a quorum must respond in every datacenter. It is the strongest multi-DC write guarantee and the most latency-exposed, because the slowest datacenter gates the whole write.
  • ALL — every replica must respond. Maximum consistency, zero fault tolerance: lose one replica and the operation fails.

The reason quorum sits at the centre of almost every well-tuned deployment is arithmetic, and that arithmetic is worth making explicit before touching any configuration.

The R + W > RF Rule

Strong consistency in Cassandra is not a mode you switch on; it is a property that emerges when your read consistency level and your write consistency level overlap. Write a value to W replicas and later read from R replicas out of RF total copies. If R + W > RF, then the set of replicas that acknowledged the write and the set that serve the read must share at least one replica — by the pigeonhole principle, two subsets of an RF-sized set whose sizes sum to more than RF cannot be disjoint. That shared replica holds the latest write, Cassandra reconciles by cell timestamp during the read, and the client is guaranteed to see it.

R plus W greater than RF quorum overlap at replication factor three Three replicas hold copies of a partition at replication factor three. The write set at QUORUM spans two replicas, the left and middle. The read set at QUORUM spans two replicas, the middle and right. Because two plus two is greater than three, the write set and the read set must share the middle replica, which holds the newest value, so the read is guaranteed to see the latest write. Write set · W = 2 Replica A acknowledged write Replica B write AND read Replica C served the read Read set · R = 2 RF = 3 · R + W = 2 + 2 = 4 > 3 → sets overlap on Replica B → newest write is visible
At RF=3, the QUORUM write set (2 replicas) and the QUORUM read set (2 replicas) always share at least one replica, so the read sees the latest write.

The quorum size for a given replication factor is floor(RF / 2) + 1: two for RF=3, three for RF=5, four for RF=7. Because QUORUM + QUORUM always exceeds RF for these odd factors, reading and writing at quorum is the canonical strong-consistency configuration — it tolerates the loss of floor(RF/2) replicas while still satisfying every request. The inequality also legitimises asymmetric tunings: a read-heavy service can write at QUORUM and read at ONE only if it accepts eventual consistency, because 2 + 1 = 3 is not greater than 3. To keep strong consistency while favouring fast reads, invert it — write at ALL (W=3) and read at ONE (R=1), giving 3 + 1 > 3 — but now every write fails if any replica is down. There is no free lunch; the inequality just tells you exactly what you are trading.

Configuration Reference

Consistency level is not stored in cassandra.yaml or the schema. It is set per statement, either explicitly in CQL or, far more commonly, as a driver-wide default that individual queries override. The table below summarises the ladder and where each rung fits.

Level Replicas required Multi-DC behaviour Best-fit use case
ANY 1 (a hint counts) Any DC; hint may sit on a non-replica Fire-and-forget writes where durability of intent beats visibility
ONE 1 replica Any DC — may cross a WAN link Lowest-latency reads that tolerate staleness
LOCAL_ONE 1 replica in local DC Never leaves local DC Fast local reads without accidental cross-region hops
QUORUM floor(RF_total/2)+1 Counts replicas across all DCs Single-DC strong consistency with R+W>RF
LOCAL_QUORUM floor(RF_localDC/2)+1 Local DC only; no cross-region round trip The default for multi-DC strong consistency
EACH_QUORUM A quorum in every DC Slowest DC gates the operation Strongest multi-DC write guarantee
ALL Every replica Every replica in every DC Read-repair-on-read or migrations; no fault tolerance

Set a default on the driver session and override per query only where it matters. With the DataStax Python driver:

#!/usr/bin/env python3
# requirements: Python 3.10+, cassandra-driver>=3.28
from cassandra.cluster import Cluster
from cassandra.policies import DCAwareRoundRobinPolicy
from cassandra.query import SimpleStatement
from cassandra import ConsistencyLevel

# Pin the driver to the local DC so LOCAL_* levels never cross a WAN link.
cluster = Cluster(
    ["10.0.0.11", "10.0.0.12"],
    load_balancing_policy=DCAwareRoundRobinPolicy(local_dc="dc-east"),
)
session = cluster.connect("app")

# Session-wide default: strong consistency without a cross-region hop.
session.default_consistency_level = ConsistencyLevel.LOCAL_QUORUM

# Per-query override for a latency-tolerant dashboard read.
fast = SimpleStatement(
    "SELECT * FROM app.metrics WHERE id = %s",
    consistency_level=ConsistencyLevel.LOCAL_ONE,
)
session.execute(fast, ("host-42",))

In cqlsh, the session-scoped CONSISTENCY command does the same thing for interactive work:

-- Inspect and set the consistency level for this cqlsh session.
CONSISTENCY;              -- prints the current level (default ONE)
CONSISTENCY LOCAL_QUORUM; -- every subsequent statement uses LOCAL_QUORUM
SELECT * FROM app.accounts WHERE id = 7;

Note the trap: cqlsh and most drivers default to ONE (or LOCAL_ONE) out of the box. A team that never sets a default is running the weakest read consistency in production by accident, which is exactly the configuration in which stale reads appear intermittently and defy reproduction.

Choosing and Setting a Consistency Level Safely

Work through these steps in order when adopting or changing a CL on a live keyspace. Each rests on knowing your replication factor per datacenter, which you confirm first.

  1. Confirm the replication factor per datacenter. Every CL decision is relative to RF, so read the schema before reasoning about quorums.

    SELECT keyspace_name, replication FROM system_schema.keyspaces
    WHERE keyspace_name = 'app';

    Expected output shows the strategy and per-DC factors, for example {'class': 'NetworkTopologyStrategy', 'dc-east': '3', 'dc-west': '3'}. Gate: proceed only if every DC that serves traffic has RF >= 3; a quorum on RF=2 tolerates zero failures and offers no availability benefit over ALL.

  2. Pick the level from the workload, not habit. For a strongly consistent multi-DC service, default reads and writes to LOCAL_QUORUM — it satisfies R + W > RF within each datacenter (2 + 2 > 3) while keeping every request in-region. Reserve EACH_QUORUM for writes that must be a majority in every region before acknowledging, and QUORUM for single-datacenter clusters only.

  3. Pin the driver to its local datacenter. LOCAL_* levels are only meaningful when the load-balancing policy knows which datacenter is local. Set DCAwareRoundRobinPolicy(local_dc=...) as shown above, or a LOCAL_QUORUM query can still route through a remote coordinator and inflate latency.

  4. Set the session default, override the exceptions. Configure default_consistency_level once, then drop specific queries to LOCAL_ONE for latency-tolerant reads or raise them to EACH_QUORUM for the rare cross-region-critical write. Keeping the default strong means a forgotten query fails safe, not stale.

  5. Load-test at the target CL before cutover. Run representative traffic with the new default and one replica deliberately down (nodetool stopdaemon on a spare) to confirm the CL still meets its availability requirement under a realistic single-node loss.

How CL Interacts with Repair and Read Repair

Consistency level determines what a single request guarantees; it does not by itself keep replicas converged over time. Two mechanisms close that gap, and both interact with your CL choice. The full mechanics are covered in read repair versus anti-entropy repair, but the CL-relevant points are these.

Blocking read repair fires during a read at any level above ONE/LOCAL_ONE. When the coordinator queries multiple replicas and their digests disagree, it reconciles the mismatch and writes the merged result back to the out-of-date replicas before returning to the client. Reading at QUORUM or LOCAL_QUORUM therefore does double duty: it satisfies R + W > RF and opportunistically repairs the replicas it touched. This is one reason quorum reads are self-healing in a way that ONE reads are not.

A critical version note: the probabilistic read_repair_chance and dclocal_read_repair_chance table options were removed in Cassandra 4.0. Older guidance told operators to tune those knobs to force background read repair; on 4.x and 5.x they no longer exist, and setting them in an ALTER TABLE is rejected. Blocking read repair on digest mismatch still happens automatically for any read above ONE — you do not configure it and cannot disable it except by dropping to LOCAL_ONE/ONE.

Anti-entropy repair (nodetool repair) is the durable backstop that read repair cannot replace, because read repair only ever touches partitions that are actually read. Cold partitions that no query has requested drift until a scheduled repair reconciles them. This is why even a strict LOCAL_QUORUM deployment must still run periodic repair: quorum guarantees per-request correctness for hot data, not eventual convergence of the whole dataset. The coupling to keep in mind is with gc_grace_seconds — repair cadence must stay below it so tombstones are not purged before every replica has seen the delete, a constraint detailed in the repair guide and the tombstone management material.

Verification and Observability

After settling on a CL, confirm it is actually in force and watch the signals that reveal when it is too strong or too weak for the current cluster state.

  • Confirm the effective level in a session. In cqlsh, run CONSISTENCY; to print the current level. From the driver, TRACING ON in cqlsh (or statement.is_idempotent plus tracing on the session) shows the replicas contacted and confirms a LOCAL_QUORUM read stayed in-datacenter.

  • Watch coordinator-side unavailables and timeouts. The client-request metrics expose them per level: org.apache.cassandra.metrics:type=ClientRequest,scope=Read,name=Unavailables and the matching Write and Timeouts MBeans. Scrape these through the Prometheus JMX exporter. A rising Unavailables count means live replicas fell below what the CL requires — the CL is too strong for the current membership.

  • Correlate with ring state. Cross-reference every unavailable spike with nodetool status: a datacenter showing nodes as DN while LOCAL_QUORUM reads fail is the textbook signature of a CL that cannot be met after node loss. On Cassandra 5.0, the system_views virtual tables let you query coordinator activity without an external JMX bridge.

  • Track cross-DC read latency. If p99 read latency jumps after a config change, confirm the driver is DC-aware; a LOCAL_ONE or LOCAL_QUORUM request that routes through a remote coordinator because the load-balancing policy was misconfigured shows up as a bimodal latency histogram, with the slow mode at roughly the inter-region round-trip time.

Failure Modes and Rollback

UnavailableException after node loss. The coordinator throws UnavailableException before attempting the operation when fewer live replicas exist than the CL requires — for LOCAL_QUORUM on RF=3, losing two replicas in a datacenter drops you below the required two. Detect it with the Unavailables MBean and nodetool status showing DN nodes. Rollback: the correct fix is to restore the down replica, not to weaken the CL, because dropping reads to LOCAL_ONE to paper over the outage silently abandons the R + W > RF guarantee. If you must shed load temporarily, lower CL on non-critical read paths only, and restore it the moment the ring is whole. Structurally, provisioning RF=3 per datacenter so a quorum survives a single node loss is what prevents this from happening in the first place.

Cross-datacenter latency blow-up. Using QUORUM (cluster-wide) instead of LOCAL_QUORUM in a multi-DC cluster forces the coordinator to wait for replicas across the WAN, so every request pays the inter-region round trip. EACH_QUORUM writes exhibit the same symptom because the slowest datacenter gates acknowledgement. Detect it as a step change in p99 latency that matches your inter-region RTT, with no change in Unavailables. Rollback: switch reads and most writes to LOCAL_QUORUM, and reserve EACH_QUORUM/QUORUM for the specific operations whose correctness genuinely requires every region to agree.

Intermittent stale reads when R + W ≤ RF. Writing at LOCAL_QUORUM but reading at LOCAL_ONE gives 2 + 1 = 3, which is not greater than RF=3; the read replica may be one that never received the latest write, so a value that was written and even read correctly moments ago intermittently reads stale. This is maddening to reproduce because blocking read repair eventually heals the touched partitions, so it self-corrects between test runs. Detect it by auditing the CL pair against RF — the arithmetic, not the logs, is the diagnostic. Rollback: raise the read to LOCAL_QUORUM (restoring 2 + 2 > 3) or, if reads must stay at LOCAL_ONE, raise writes to ALL for that table and accept the reduced write availability. This ties back to how node gossip and failure detection shape which replicas the coordinator believes are live when it assembles the quorum.

FAQ

Is QUORUM or LOCAL_QUORUM the right default in a multi-datacenter cluster?

LOCAL_QUORUM in almost every case. Both give a majority guarantee, but QUORUM counts replicas across all datacenters, so a coordinator in one region waits for acknowledgements from replicas in another and every request pays the inter-region round trip. LOCAL_QUORUM requires a majority of only the local datacenter’s replicas, satisfying R + W > RF within each region while staying in-region. Reserve cluster-wide QUORUM for single-datacenter clusters and EACH_QUORUM for the rare write that must be a majority in every region before it acknowledges.

Does reading and writing at QUORUM guarantee strong consistency?

Yes, on a single datacenter, because QUORUM + QUORUM always exceeds RF for the odd replication factors you should be using. At RF=3 the quorum is 2, and 2 + 2 = 4 > 3, so the write set and read set overlap on at least one replica that holds the newest value, which Cassandra returns after reconciling by timestamp. In a multi-DC cluster, use LOCAL_QUORUM for reads and writes to get the same overlap within each datacenter without the cross-region latency. What breaks the guarantee is mixing a quorum write with a ONE read, since 2 + 1 does not exceed 3.

Do I still need to run repair if I read and write at LOCAL_QUORUM?

Yes. Quorum guarantees per-request correctness for data that is actually read, and blocking read repair heals the replicas a quorum read touches, but neither reconciles partitions that no query requests. Cold data drifts until a scheduled anti-entropy repair reconciles it, and gc_grace_seconds will purge tombstones on the assumption that repair has run. Treat LOCAL_QUORUM and periodic nodetool repair as complementary: one gives request-time correctness, the other gives eventual whole-dataset convergence.

What happened to read_repair_chance in Cassandra 4.0?

The probabilistic read_repair_chance and dclocal_read_repair_chance table options were removed in Cassandra 4.0 and no longer exist on 4.x or 5.x; an ALTER TABLE that sets them is rejected. They previously triggered background read repair on a fraction of reads even when the client CL did not require cross-replica comparison. On modern Cassandra, blocking read repair still runs automatically whenever a read above ONE finds a digest mismatch, and durable convergence is the job of scheduled anti-entropy repair rather than a per-table probability knob.

Can I set a different consistency level for different queries?

Yes, and you should. Consistency level is a per-statement property, so one session can write a ledger at LOCAL_QUORUM, read a cache-like table at LOCAL_ONE, and push a compliance write at EACH_QUORUM. The practical pattern is to set a strong session default such as LOCAL_QUORUM so forgotten queries fail safe, then explicitly override the specific reads that can tolerate staleness for lower latency. Just keep the R + W > RF arithmetic in mind for any table where a read path and a write path must agree.