TessariDBDocs
  • 0.34.2-betalatest
  • 0.34.1-beta
  • 0.34.0-beta
  • 0.33.4-beta
  • 0.33.3-beta
  • 0.33.2-beta
  • 0.33.1-beta
  • 0.33.0-beta
  • 0.32.0-beta

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' };
levelthe caller is answered oncea leader lost before a follower collected the write
LEADERthe leader holds the write durablytakes the write with it
MAJORITYa majority of the range's voters — its coordinating members, the leader among them — hold it durablyloses nothing that was acknowledged: whichever majority elects the next leader holds the write
client leader · holds it follower · holds it follower · behind 1 write 2 copy 3 ok leader + one follower = 2 of 3 voters: a majority
At 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:

LEADERMAJORITY
commit p50 / p99, 1 writer0.8 / 2.3–3.2 ms1.5 / 3.1–3.3 ms
commit p50 / p99, 16 writers1.9–2.4 / 4.3–5.0 ms4.1–4.9 / 6.3–7.1 ms
acknowledged writes lost on a leader kill0–2 of about 1 4000 of about 860
a new leader elected after the kill0.9–1.9 s0.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), ms0 ms1 ms10 ms50 ms
commit, ACKNOWLEDGE LEADER0.85 (2.48)0.86 (2.97)0.60 (2.29)0.60 (2.00)
commit, ACKNOWLEDGE LOCAL MAJORITY0.85 (2.29)0.76 (2.67)0.57 (2.31)0.56 (2.12)
commit, ACKNOWLEDGE MAJORITY1.43 (3.13)2.97 (4.37)16.87 (34.40)60.79 (71.11)
one write on a shard leader, MAJORITY1.60 (3.08)3.04 (4.82)14.30 (21.99)59.97 (73.37)
a commit across two leaders4.45 (6.32)6.42 (9.52)24.47 (57.81)115.40 (291.02)
a follower holding a commit3.46 (4.02)3.33 (3.67)6.73 (8.24)27.68 (29.82)
a leader killed, until a survivor writes1034.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 STALENESS and ANSWERED 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 FACTOR says how many nodes keep the namespace; ACKNOWLEDGE says how many of the voters must hold a write before its caller hears back.
When a write is acknowledged — TessariDB