When a write is acknowledged
ACKNOWLEDGE LEADER, LOCAL MAJORITY or MAJORITY: how many copies hold a write before the caller is told it succeeded, and the two refusals that mean opposite things.
A write on a cluster is applied and readable on the leader the moment it
commits. What ACKNOWLEDGE decides is when the caller is told it succeeded —
and therefore what a lost leader can take with it (from 0.21.0-beta).
DEFINE NAMESPACE prod REPLICATION FACTOR 3 ACKNOWLEDGE MAJORITY;
USE NAMESPACE prod;
DEFINE DATABASE app;
USE DATABASE app;
DEFINE COLLECTION event;
CREATE event:1 = { kind: 'signup' };
| level | the caller is answered once | a leader lost before a follower collected the write |
|---|---|---|
LEADER | the leader holds the write durably | takes the write with it |
MAJORITY | a majority of the range's voters — its coordinating members, the leader among them — hold it durably | loses nothing that was acknowledged: whichever majority elects the next leader holds the write |
MAJORITY the caller hears ok once a majority of the range's voters hold the write. Whichever majority elects the next leader includes one of them.MAJORITY is the default where there is more than one copy
A namespace kept on more than one node acknowledges at MAJORITY unless it says
otherwise. A namespace with REPLICATION NONE, or a store standing alone, is its
own majority and waits for nobody — which is why the block above runs on a single
store exactly as it would on three.
Writes there wait about one replication round trip. That is the price of a write surviving its leader, and it is paid only where a second copy exists to wait for.
What it costs, measured
Three processes on one Linux host (a container, release build, disk stores),
one writer and then sixteen, and the leader killed with kill -9 under a
writer:
LEADER | MAJORITY | |
|---|---|---|
| commit p50 / p99, 1 writer | 0.8 / 2.3–3.2 ms | 1.5 / 3.1–3.3 ms |
| commit p50 / p99, 16 writers | 1.9–2.4 / 4.3–5.0 ms | 4.1–4.9 / 6.3–7.1 ms |
| acknowledged writes lost on a leader kill | 0–2 of about 1 400 | 0 of about 860 |
| a new leader elected after the kill | 0.9–1.9 s | 0.85–0.9 s |
On macOS every commit pays a full flush of the device — about 5 ms, and three
processes on one machine queue on it — so the same test reads 14–15 ms at one
writer for both levels. Measure on the hardware you will run on; what these
numbers show is the shape: MAJORITY costs one round trip to the nearest
follower, and it is the level that loses nothing.
What distance costs
The same three nodes, with every packet between two of them delayed by half a
round trip (tc netem on Linux, 0.31.2-beta, release build, disk stores, one
container — emulated distance, not separate machines). The client sits beside
the node it asks. Machine-written by benchmarks/cluster-distance.sh into
benchmarks/2026-10-07-linux-aarch64-cluster-distance.md in the engine
repository, which this table is taken from:
| p50 (p99 / max), ms | 0 ms | 1 ms | 10 ms | 50 ms |
|---|---|---|---|---|
commit, ACKNOWLEDGE LEADER | 0.85 (2.48) | 0.86 (2.97) | 0.60 (2.29) | 0.60 (2.00) |
commit, ACKNOWLEDGE LOCAL MAJORITY | 0.85 (2.29) | 0.76 (2.67) | 0.57 (2.31) | 0.56 (2.12) |
commit, ACKNOWLEDGE MAJORITY | 1.43 (3.13) | 2.97 (4.37) | 16.87 (34.40) | 60.79 (71.11) |
one write on a shard leader, MAJORITY | 1.60 (3.08) | 3.04 (4.82) | 14.30 (21.99) | 59.97 (73.37) |
| a commit across two leaders | 4.45 (6.32) | 6.42 (9.52) | 24.47 (57.81) | 115.40 (291.02) |
| a follower holding a commit | 3.46 (4.02) | 3.33 (3.67) | 6.73 (8.24) | 27.68 (29.82) |
| a leader killed, until a survivor writes | 1034.64 (1038.13) | 899.60 (965.90) | 982.70 (1179.53) | 1100.85 (2212.13) |
What it shows is the shape. LEADER and LOCAL MAJORITY — with the leader
alone in its region — do not move with distance; MAJORITY costs a little more
than one round trip to the nearest follower, and a follower holds a commit
about half a round trip after it is acknowledged, because the leader sends
each commit as it lands. A commit across two leaders costs about two round
trips at distance, so keep related records under one leader where you can.
Replacing a leader is the lease, about a second, whatever the distance; one
failover in nine at 50 ms took 2.2 s.
A request may name its own level, and not a weaker one
A write, or a whole transaction at its COMMIT, may say how it is acknowledged:
BEGIN;
CREATE event:2 = { kind: 'login' };
COMMIT ACKNOWLEDGE MAJORITY;
Asking for less than the namespace's level is not allowed, because a default any caller could lower is not one anybody can rely on:
CREATE event:3 = { kind: 'heartbeat' } ACKNOWLEDGE LEADER;
Refused — AcknowledgeBelowNamespace, and the refusal says what would be accepted:
error: this write asks for `ACKNOWLEDGE LEADER`, and namespace `prod` waits for
`ACKNOWLEDGE MAJORITY`: write the namespace's level or a stronger one, or declare
`ALTER NAMESPACE prod ACKNOWLEDGE MAJORITY OR WEAKER` to let a request ask for less
The namespace decides whether its callers may trade durability for latency, and says so where everyone can read it:
ALTER NAMESPACE prod ACKNOWLEDGE MAJORITY OR WEAKER;
CREATE event:3 = { kind: 'heartbeat' } ACKNOWLEDGE LEADER;
INFO FOR NAMESPACE;
INFO FOR NAMESPACE reports the level under acknowledge —
'majority or weaker' here.
LOCAL MAJORITY: a majority of the leader's region
A member row can name the region its node runs in, and a write can then wait for a majority of the voters in the leader's own region instead of a majority of all of them:
ALTER REPLICA n0 REGION 'eu';
ALTER REPLICA n1 REGION 'eu';
ALTER REPLICA n2 REGION 'us';
CREATE event:4 = { kind: 'order' } ACKNOWLEDGE LOCAL MAJORITY;
DEFINE REPLICA … REGION 'eu' names it when the row is declared, ALTER REPLICA … REGION NONE takes it away, and INFO FOR NODE shows it. The level sits
between the other two — LEADER < LOCAL MAJORITY < MAJORITY — so a namespace
may default to it and a request may ask for more. The leader counts as one of
its region's voters.
What it promises, and what it does not. A write acknowledged at LOCAL MAJORITY survives losing every node outside the region, and a minority inside
it, while the leader lives. It is not promised across a failover: the
majority that elects the next leader is drawn from every voter and need not
include one of the region's copies, so a node without the write can be elected.
It is a latency choice with that limit written down, and MAJORITY stays the
default — the only level that loses nothing when the leader does.
A leader whose own row names no region refuses LocalMajorityWithoutRegion
before anything is written; too few voters in the region to make a majority is
MajorityUnreachable, as for MAJORITY.
Two refusals, and they mean opposite things
A client that treats every refusal as nothing happened is wrong about one of these, and it is the one that matters.
MajorityUnreachable — nothing was written. It comes before the commit: the
voters that subscribe to where the write lands cannot make up a majority, so the
write could never be held by one. Accepting it and timing it out would commit
something the caller is then told failed.
this write waits for 2 of 3 voter(s) to hold it, and only [] besides this
node subscribe to where it is written: nothing was written. Declare a subscription
that covers it on enough voters, or write it with `ACKNOWLEDGE LEADER` if the
namespace allows that
NotAcknowledgedInTime — the write is committed here. It comes after: the
write is in this node's log at the sequence the refusal names, and not enough
voters confirmed holding it within one failover ROUND. It may or may not survive
a failover.
this write is committed on this node at sequence 812, and only [] besides
it acknowledged holding it in time, where 2 copies were needed: it may not
survive a failover
Retry that one only with a write that says the same thing twice — an UPSERT,
or a write to a known identity — because a CREATE retried after it landed is
answered already exists. Neither name is on the wire; a client tells them apart
by the error class its library gives it, and the refusal reference
lists both.
What it does not change
- Reads. A write is readable on the leader the moment it commits, at either
level. Which node may answer a read is
STALENESSandANSWERED BY. - Who may write. That is the leadership; an acknowledgement is counted only by the node that leads the range.
- How many copies exist.
REPLICATION FACTORsays how many nodes keep the namespace;ACKNOWLEDGEsays how many of the voters must hold a write before its caller hears back.