ARC 2Choose Consistency SemanticsChapters 5–9

Chapter 6 — Concurrent Register Writes and Deterministic Convergence

Chapter objective: After this chapter, a developer can explain deterministic convergence of concurrent candidates and verify whether a custom resolver is commutative.

Learning objectives

  1. Distinguish wall-clock time, HLC, and lineage total order.
  2. Explain the deterministic basis of the default last-write-wins policy.
  3. Write a commutativity check for a resolver.
  4. Identify external effects that equal final values cannot repair.

Prerequisites

Case progress

Node A points fulfillment-primary east while node B points it west. After recovery, both sides need the same visible hint or traffic remains split indefinitely.

Determinism matters more than speed

Deterministic resolution of concurrent candidates

The default ConflictResolver.lastWriteWins() compares metadata through RegisterLineageOrder. The order includes lineage/HLC information and uses a stable node identifier as a tie-breaker. Correctness depends on every node applying the same total order to the same pair of candidates; one machine's interpretation of local time cannot supply that guarantee.

At minimum, the following property holds:

resolve(A, B).winner == resolve(B, A).winner

A first-argument-wins resolver can leave A on east and B on west. Duplicate delivery and reversed message order do not heal the split.

The safe entry point for a custom resolver

ConflictResolver.verified(delegate) invokes the resolver with (local, remote) and (remote, local), then compares the winning entity and outcome family. A violation raises CONFLICT_RESOLVER_NOT_COMMUTATIVE, which is useful in tests, staging, or a controlled rollout.

The check covers only the observed input pair and executes the resolver twice. It is not a formal proof. A resolver remains pure, deterministic, and free of external side effects.

Counterexample and fault injection

Define resolve(local, remote) -> localWins(local). Each node treats its own candidate as local and the state remains split. A resolver that reads the current time or a random number can occasionally agree after argument reversal while still failing deterministic replay.

Even when two concurrent inventory deductions eventually choose one winner, both customers might already have received success. Conflict resolution converges replica state; it does not undo external commitments.

Experiment

Run the two-node custom-resolver test:

cd submodule/dsm
mvn -q -pl dsm-integration-test -am \
  -Dtest=TwoNodeIntegrationTest#customConflictResolversConvergeMergedRegisterValuesAcrossTwoNodes \
  -Dsurefire.failIfNoSpecifiedTests=false test

Then inspect this chapter's resolver-cases.json and classify each candidate policy for purity, commutativity, and determinism.

Experiment acceptance card

Field Content
Command The Maven command above; node --test tests/chapter-assets.test.mjs
Input or fault Concurrent candidates for one key; reversed input order
Observable result Both nodes reach the same merged result; the asset marks unsafe strategies
Evidence level E3: controlled two-node conflict test
This experiment does not prove Formal correctness over every input, cross-resource transactions, or reversal of external effects

Review

Next

Chapter 7 turns to time-bounded ownership: shard processing asks who can act within a lease window rather than which candidate is newer.