Cassandra Lightweight Transactions: Paxos-Based Compare-and-Set in Practice
Learn how Cassandra Lightweight Transactions use Paxos to provide linearizable compare-and-set on a single partition, with a concrete UPDATE example, latency numbers, common pitfalls, and verification steps you can run on a 3‑node cluster.
12 Aug 2026, 18:58 UTC

What you get from a Lightweight Transaction
A Lightweight Transaction (LWT) lets you execute a compare-and-set on a single partition with linearizable semantics. The statement either succeeds atomically — returning applied = true — or fails and returns the current row so you can decide what to do next. No external lock service is required; Cassandra runs a Paxos round internally.
How the Paxos round works
When you append an IF clause to an INSERT, UPDATE, or DELETE, the coordinator becomes a Paxos proposer for that partition:
- Prepare – coordinator sends a prepare request to a quorum of replicas.
- Propose – if a quorum promises, the coordinator proposes the new value.
- Commit – on acceptance, the coordinator sends a commit to all replicas.
- Ack – replicas acknowledge; the coordinator returns the result.
Each phase requires a network round‑trip, so an LWT typically costs 2‑4× the latency of a normal write.
Worked example: conditional balance decrement
UPDATE accounts
SET balance = balance - 100
WHERE id = 'user123'
IF balance >= 100;
Run this in cqlsh (or any driver) against a Cassandra 3.11+ / 4.x cluster. On success you receive:
[applied] | id | balance
-----------+---------+---------
True | user123 | 900
On contention (another client changed the balance first) you get:
[applied] | id | balance
-----------+---------+---------
False | user123 | 950
The returned row reflects the state at the moment the Paxos round read the partition; it is not guaranteed to be the latest committed value.
Performance characteristics you should expect
- Latency: 4 round‑trips across a quorum. In a 3‑node cluster with 1 ms intra‑DC latency, a single LWT often takes 4‑8 ms vs. 1‑2 ms for a normal write.
- Throughput under contention: Failed attempts still consume coordinator CPU and network. Hot partitions (e.g., a single inventory counter) can become bottlenecks; throughput drops sharply once contention exceeds ~10 % of requests.
- Consistency level: LWT always runs at
SERIALconsistency (quorum for prepare/propose, all for commit). You cannot lower it. - Cassandra 4.0+ tuning:
cas_contacts(default 2) andcas_retry_timeout(default 10 s) incassandra.yamllet you limit the number of replicas contacted during prepare and the maximum retry window.
Common mistakes and how to avoid them
1. Read‑then‑LWT pattern
Reading the balance first and then issuing the LWT creates a race window. The read may be stale, and the subsequent LWT will fail anyway. Always embed the condition inside the LWT itself as shown above.
2. Using LWT for high‑throughput idempotent writes
If your operation is naturally idempotent (e.g., setting a column to a fixed value), a normal write with client‑side retries is cheaper. Reserve LWT for true compare‑and‑set logic.
3. Collections or UDTs in the condition
LWT on a table that stores collections or user‑defined types rewrites the entire collection on each attempt, amplifying write amplification and latency. Model such data in separate partitions if you need conditional updates.
4. Ignoring read‑after‑write visibility
After a successful LWT, a subsequent SELECT ... WITH CONSISTENCY QUORUM may still return the old value because some replicas have not yet applied the commit. Use CONSISTENCY SERIAL for the read that must see the LWT result.
Monitoring and diagnostics
| Metric | Source | What to watch |
|---|---|---|
| LWT Latency (p50/p99) | nodetool tablestats or JMX org.apache.cassandra.metrics:type=Table,keyspace=*,table=*,scope=LWTLatency | Sudden increase signals contention or GC pauses. |
| LWT Contention (failed attempts / total) | Same sources, metric LWTContention | >10 % suggests schema redesign (bucketing, async compensation). |
| Paxos phase tracing | TRACING ON in cqlsh, then inspect system_traces.events for PAXOS entries | Confirms 4‑phase execution; missing phases indicate coordinator failures. |
Verification steps you can run today
- Start a 3‑node cluster (Docker or
ccm) with Cassandra 4.1. - Create the
accountstable and insert a row.CREATE TABLE accounts (id text PRIMARY KEY, balance int); INSERT INTO accounts (id, balance) VALUES ('user123', 1000); - Run the conditional UPDATE from the example; confirm
applied = trueand balance 900. - Run the same UPDATE concurrently from two terminals; one will return
applied = falsewith the current balance. - Execute
SELECT * FROM accounts WHERE id='user123' WITH CONSISTENCY SERIAL;— it must show 900. Repeat withQUORUMto observe possible stale read. - Enable tracing (
TRACING ON) and re‑run the LWT; querysystem_traces.eventsfor four PAXOS entries (prepare, propose, commit, ack). - Use
cassandra-stresswith thelwtprofile to generate load, then checknodetool proxyhistogramsfor latency percentiles.
Permissions: All commands require a user with MODIFY on the keyspace and SELECT on system_traces. nodetool needs JMX access (typically the cassandra OS user).
Limitations to keep in mind
- LWT is single‑partition only. No multi‑row or multi‑partition atomicity.
- Failed LWT returns a row that may already be stale; implement exponential back‑off with jitter before retrying.
- Repair operations (especially incremental repair) can spike LWT latency because Merkle tree calculations compete for CPU and disk I/O. Schedule repairs during low‑traffic windows.
- Cassandra 5.0 (early access) reworks the Paxos implementation; behavior may differ. Validate on your target version before production rollout.
Quick checklist before you ship
- Condition expressed inside the
IFclause — no separate read. - Partition key design avoids hot spots (consider bucketing or sharding).
- Client uses
SERIALconsistency for any read that must observe the LWT result. - Monitoring alerts on
LWT Contention > 10%andLWT Latency p99 > 50 ms. - Tested on the exact Cassandra version you will run in production (3.11, 4.0, 4.1, …).
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.