Lightweight transactions (compare-and-set)

Apache Cassandra · Advanced · 12 min

Uniqueness and check-then-act operations (usernames, inventory) need more than last-write-wins.

Plain writes are blind upserts. When you need to check before writing, Cassandra offers lightweight transactions (LWT): conditional writes using a Paxos consensus round among the replicas, which makes them linearizable for that partition.

  • INSERT ... IF NOT EXISTS claims a key only if it's free, e.g. a unique username,
  • UPDATE ... SET qty = 4 WHERE ... IF qty = 5 changes a value only if it still has the expected one,
  • DELETE ... IF EXISTS and UPDATE ... IF EXISTS act only on existing rows.

The result contains an [applied] column; when false, it also returns the current values so you can decide what to do.

The Paxos phase uses the serial consistency level: SERIAL (across data centers) or LOCAL_SERIAL; the commit uses the normal consistency level. Read with SERIAL if you must see the result of in-progress LWTs.

Costs and limits:

  • several round trips, so roughly 4x the latency of a normal write, and contention on the same partition causes timeouts,
  • conditions are limited to one partition; there are no multi-partition transactions (general ACID transactions via the Accord protocol are planned for a later major version),
  • mixing LWT and non-LWT writes on the same data breaks the guarantees.

Use LWTs for the rare operations that truly need them, not for every write.

Example

-- unique username claim
INSERT INTO shop.usernames (username, user_id)
VALUES ('ana', 5b6962dd-3f90-4c93-8f61-eabfa4a803e2) IF NOT EXISTS;
--  [applied]
--       True
INSERT INTO shop.usernames (username, user_id)
VALUES ('ana', 0d1d8cd6-31ab-4a4b-a6d3-6c0e2b1b9a10) IF NOT EXISTS;
--  [applied] | username | user_id
--      False |      ana | 5b6962dd-3f90-4c93-8f61-eabfa4a803e2

-- optimistic stock decrement
SERIAL CONSISTENCY LOCAL_SERIAL;
UPDATE shop.stock SET qty = 4 WHERE sku = 'PEN' IF qty = 5;
--  [applied] | qty
--      False |   3      -> re-read and retry with the current value

Remember

  • IF NOT EXISTS / IF col = value give compare-and-set via Paxos
  • Check the [applied] column; failures return current values
  • Serial CL governs the Paxos phase (SERIAL or LOCAL_SERIAL)
  • Single-partition only and several times slower than plain writes

Watch out for

  • Mixing conditional and unconditional writes to the same column, which can silently break linearizability.

Check yourself

  1. Two users try to register the username "ana" at the same time with INSERT ... IF NOT EXISTS. What happens?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: cassandra.apache.org/doc/latest/cassandra/developing/cql/dml.html

Builds on

Unlocks

Nothing builds on this yet.