Cassandra Lightweight Transactions

A Lightweight Transaction (LWT) in Cassandra is a conditional read-modify-write operation that provides linearizable consistency — meaning you can safely check a condition and act on it atomically, even in a distributed environment. LWTs use the Paxos consensus algorithm and are significantly slower than regular reads and writes. Use them sparingly for situations where correctness is critical.

The Bank Account Analogy

Withdrawing money from a bank account requires checking the balance first, then deducting the amount — but only if the balance is still sufficient at the moment of deduction. Two simultaneous withdrawals could both read a $100 balance, both decide "$100 is enough for my $80 withdrawal," and both proceed — leaving the account at -$60. An LWT prevents this by locking the check-and-update into a single atomic step.

Without LWT (race condition):
  Thread 1 reads balance: $100
  Thread 2 reads balance: $100
  Thread 1 deducts $80  → balance: $20
  Thread 2 deducts $80  → balance: -$60  ← wrong!

With LWT:
  Thread 1: UPDATE account SET balance = 20
            WHERE account_id = X IF balance = 100;  → applied ✓
  Thread 2: UPDATE account SET balance = 20
            WHERE account_id = X IF balance = 100;  → NOT applied ✗
                                                    (balance is now 20)

LWT Syntax

INSERT IF NOT EXISTS

Inserts a row only when no row with the same primary key exists. Use this to prevent duplicate registrations.

INSERT INTO users (user_id, username, email)
VALUES (uuid(), 'alice', 'alice@example.com')
IF NOT EXISTS;

UPDATE IF EXISTS

Updates a row only when the row exists.

UPDATE users SET email = 'new@example.com'
WHERE user_id = [uuid]
IF EXISTS;

UPDATE IF condition

Updates a row only when the specified column currently holds the given value. This is compare-and-set (CAS).

-- Change status from 'pending' to 'processing' only if still pending:
UPDATE orders SET status = 'processing'
WHERE order_id = [uuid]
IF status = 'pending';

DELETE IF EXISTS / IF condition

DELETE FROM sessions WHERE token = [uuid] IF EXISTS;

DELETE FROM orders WHERE order_id = [uuid] IF status = 'cancelled';

The [applied] Response Column

Every LWT returns a boolean column named [applied]. When true, the operation succeeded. When false, the condition was not met and the operation was skipped. The response also includes the current values of the checked columns so you know the actual state.

-- Example: INSERT IF NOT EXISTS

INSERT INTO usernames (username, user_id)
VALUES ('alice', uuid())
IF NOT EXISTS;

Success (row did not exist):
 [applied]
───────────
 True

Failure (username already taken):
 [applied] | user_id
───────────+──────────────────────
 False     | existing-user-uuid

How Paxos Works in Cassandra LWTs

LWTs use a four-phase Paxos protocol. This is why they cost roughly 4× the latency of a normal write.

Phase 1: Prepare
  Coordinator → Replica nodes: "I want to run a transaction on partition X"
  Replicas → Coordinator: "Granted (or denied if newer proposal exists)"

Phase 2: Read (Promise)
  Coordinator reads the current value from a quorum of replicas
  → Determines the actual current state

Phase 3: Propose
  Coordinator → Replicas: "Apply this conditional write"
  Replicas → Coordinator: "Accepted"

Phase 4: Commit
  Coordinator → Replicas: "Commit the write"
  Replicas apply the write → ACK

Result returned to client.

Serial Consistency Level

LWTs use a separate consistency level for the Paxos phases called serial consistency. The default is SERIAL (cross-datacenter). Use LOCAL_SERIAL to restrict Paxos rounds to the local data center, reducing latency in multi-DC setups.

-- In cqlsh:
SERIAL CONSISTENCY LOCAL_SERIAL;

INSERT INTO usernames (username, user_id)
VALUES ('bob', uuid())
IF NOT EXISTS;
// In Java driver:
SimpleStatement stmt = SimpleStatement.builder(
    "INSERT INTO usernames (username, user_id) VALUES (?, ?) IF NOT EXISTS")
  .setSerialConsistencyLevel(ConsistencyLevel.LOCAL_SERIAL)
  .build();

LWT Performance Cost

Operation       Approximate Cost
──────────────────────────────────────────────────────────────
Normal write    ~0.1–0.5 ms
LWT write       ~4–10 ms (4 Paxos phases + quorum reads)

LWTs require a quorum of replicas in every phase. In a high-availability setup, a node outage during Paxos causes LWT latency to spike dramatically. Never put LWTs in a high-throughput hot path.

Common LWT Use Cases

Use Case                         LWT Operation
──────────────────────────────────────────────────────────────
User registration (unique name)  INSERT IF NOT EXISTS
Order state machine              UPDATE SET status='X' IF status='Y'
Account creation                 INSERT IF NOT EXISTS
Seat reservation (tickets)       UPDATE SET reserved=true IF reserved=false
Token generation (unique)        INSERT IF NOT EXISTS
Idempotent job claiming          UPDATE SET claimed=true IF claimed=false

LWT Pitfalls

Pitfall                         Consequence
──────────────────────────────────────────────────────────────
Using LWT for high-volume ops   4× latency degrades throughput
                                severely
Not checking [applied] response Application applies the change
                                even if the condition failed
Using SERIAL in multi-DC        Cross-DC Paxos adds 100+ms latency
  instead of LOCAL_SERIAL       per operation
Batching LWTs                   Only one LWT per batch is allowed
                                on a single partition
Running LWT without quorum      Paxos requires quorum; node failures
  available                     cause LWT failures

Summary

Lightweight Transactions provide conditional, linearizable read-modify-write operations using Paxos consensus. Use INSERT IF NOT EXISTS for uniqueness constraints and UPDATE IF condition for compare-and-set state machines. Always check the [applied] response and handle the false case in your application. Use LOCAL_SERIAL consistency in multi-datacenter clusters. Reserve LWTs for low-volume, correctness-critical operations — their 4× cost makes them unsuitable for high-throughput workloads.

Leave a Comment

Your email address will not be published. Required fields are marked *