Two writers on one range
A namespace can admit writes on more than one node. What that gives up is single-copy semantics — so the engine refuses the conflict and names it, rather than picking a winner.
Everything else in this section assumes one node writes a range at a time. A namespace can be declared otherwise, and this page is what that costs.
DEFINE NAMESPACE shared MULTI MASTER;
DEFINE NAMESPACE ledger SINGLE LEADER;
MULTI MASTER and SINGLE LEADER say how many nodes may WRITE a namespace,
which is a different question from how many copies of it exist — that is
REPLICATION, and it is on
What replicates where.
The clause is optional and silence means a single leader. But a namespace that
never said is still not a namespace that said SINGLE LEADER, kept apart for
the reason the replication clause keeps its two, and INFO FOR NAMESPACE reports
class alongside replication so the difference is readable rather than
inferred.
What multi-master gives up
It gives up single-copy semantics, and that is a loss rather than a feature.
On a single-leader range every write passes through one node, so the versions of a record form a line and the newest of them is the answer.
On a multi-master range two nodes can each accept a write to one record without having seen the other's, and the result is two versions of which neither is newer. There is no clock that settles it: the nodes' clocks are not comparable, and the order the versions happen to reach a third node is the order that node heard about them, not the order they were made. The engine cannot tell you which is right because nothing knows.
So before and after are not the only two answers a comparison can give.
concurrent is a third, and it is a fact about the two writes rather than a
tie that was broken quietly.
So it refuses, and names both
A write onto a record in that state is rejected:
the record person:1 holds versions 812 and 807 that neither supersedes:
they were written without seeing each other. Version 807 carries a write from
node c81b…, which version 812 has not seen. Read both and write what they mean.
The refusal carries the record's id, both surviving versions, and a node whose write one of them carries and the other has not seen — enough to go and read both and write what they mean. A refusal that said only contested would leave the caller unable to fetch what it has to choose between, which is the same dead end a redirect with no address gives.
The refusal is the point. Once a write is accepted on top of a contested record, no later read, dump or backup comparison can tell a record that was reconciled from one that was silently ranked. The moment of the refusal is the only moment at which the choice is visible.
Retrying the same write unchanged meets the same refusal, correctly. What clears it is a write that has seen both versions — at which point it supersedes them and there is nothing left to be concurrent with.
LAST WRITER WINS is the alternative, and its cost is named
DEFINE TABLE ledger SCHEMALESS LAST WRITER WINS;
DEFINE TABLE audit SCHEMALESS REFUSE CONFLICTS;
On a table that declares it, a write onto a contested record is taken instead of refused: the incoming write supersedes every version it did not see.
The last writer is the caller — the one whose write is being committed — and not a timestamp. No clock is read, which is what makes this a defensible rule at all rather than a comparison of two machines that disagree about the hour.
What is given up is the versions it did not see. They stay on disk, byte-intact, and they are gone from every answer; an operator auditing the store for data loss finds them and concludes nothing was lost. The engine therefore counts each one, and the count is readable, because a loss nobody records is a loss nobody can check.
REFUSE CONFLICTS writes the default down where a reader can see it. A table
that says neither refuses. The clause stands with the table's other flags in any
order, and it is declared per table on purpose: a counter can tolerate
last-writer-wins beside a ledger row that cannot.
Looking at a contested record
INFO FOR VERSIONS OF person:1;
This is the one INFO FOR that does not read the catalog — it reads one record's
versions. It answers with:
answered— the version an ordinary read resolves to, and the node that wrote it;versions— every surviving version, newest first;concurrent— whether more than one survives.
On a settled record, which is nearly all of them, it reports one version and
concurrent: false. It answers on a single-leader range too, because is this
contested? is a question worth being able to ask anywhere.
The node named is the node that WROTE the version, which is not always the node the version has seen the most writes from. Those two are the same for almost every record and come apart exactly in the case this page is about — a version written by a node that had already seen five of another node's writes names the other node more often, and the writer is still the one that wrote it.
What the declaration changes, and what it does not
It is the declaration that exempts a write from needing a lease — by the same
predicate, with no second setting. A clustered node holding no leadership still
refuses writes on a single-leader range; on a range declared MULTI MASTER it
does not, because the declaration is what says more than one node may write
there.
Each writer allocates positions in its own log for the shared range, and a third node replaying both applies them without a gap refusal. Two logs for one range is the normal arrangement here, not the divergence Leadership and failover describes — a divergence is two different records at one position in one log.
The campaign remains the only producer of an epoch with two writers on one range, which is checked structurally rather than assumed.
And the record of what a version has seen survives the wire, the log and a backup and restore unchanged. A conflict that was visible before a restore and invisible afterwards would make the restore itself a silent reconciliation.
Choosing
Declare SINGLE LEADER, or say nothing, unless you can answer what happens when
two nodes write one record without seeing each other — and the answer is not
"that will not happen". If you can answer it, MULTI MASTER with
REFUSE CONFLICTS puts the answer in your application, where the meaning of the
data is. LAST WRITER WINS puts it in the engine, cheaply, and the price is
recorded in a counter somebody has to actually read.