Skip to main content

Compare-and-Set (CAS)

Two clients read the same key, both compute a new value, and both write it back. The second write silently erases the first: a lost update.

A compare-and-set (CAS) write prevents this. It succeeds only if the key still has the value or revision you expect, and fails if another client changed the key first, so your code can read again and retry. It is the same idea as UPDATE ... WHERE version = @expected in SQL.

Because the check against the current version and the write happen as one step, CAS guarantees strong consistency for that key: no writer can overwrite a change it has not seen.

Use CaseHow CAS Helps
Leader ElectionEnsures only one node becomes leader.
Distributed LocksPrevents multiple nodes from acquiring the same lock.
Configuration UpdatesPrevents conflicting writes to shared config values.
Rate LimitingEnsures atomic updates to request counters.
Concurrent TransactionsAvoids lost updates when multiple clients modify the same key.

The leader election and lock uses above, and the queue consumer example below, rely on a key written with an expiry and nx as a simple lock. CAS does not stop a worker that paused past the expiry: it can still finish its work after another worker took over, and only its final CAS write fails. When work must not overlap, use distributed locks with fencing tokens.

Examples​

In Kahuna Script, add cmp <value> or cmprev <revision> to set. The .NET and TypeScript clients have dedicated methods for CAS on a value or on a revision. Their signatures are in the API reference: Compare Value and Set and Compare Revision and Set.

Distributed Queue Consumer Lock​

Use Case: Multiple workers process tasks from a queue but each task should only be handled once

Sets the initial value as usual if it hasn't been assigned previously:

set `locks/tasks/123` "node1" ex 10000 nx
r0 set 15ms

Mark the task as completed if this node still holds the key:

set `locks/tasks/123` "completed" cmp "node1"
r1 set 11ms

Feature Flag Update Control​

Use Case: Update a feature flag, but only if it hasn’t changed since you last read it.

Obtains the current value of the feature flag:

get `feature/new-landing`
r0 disabled 12ms

Change the flag if the revision is still 0:

set `feature/new-landing` "enabled" cmprev 0
r1 set 19ms

The flag won't change if the revision is not 0:

set `feature/new-landing` "disabled" cmprev 0
r1 not set 14ms