PATTERN Cited by 1 source
Two-phase tentative-then-complete¶
Intent¶
Separate receipt from materialisation in a consensus commit path so that:
- Abandoned requests can be deleted cleanly (cancellation path).
- Durable requests are never forgotten (completion path).
- Completion and cancellation are mutually exclusive — a request ends up exactly one way.
Shape¶
Client → Leader: write X
Leader → Followers: tentative(X)
Followers → Leader: ack(X)
[Leader tallies acks. Once durability threshold is met, X is durable.]
Leader → Client: ack (optional — see Early-ack-on-durability)
Leader → Followers: complete(X)
Followers: apply X
A request passes through three stages:
| Stage | Who knows | Follower marker | Cancelable? |
|---|---|---|---|
| Incomplete | leader + responsive followers | tentative | yes |
| Durable | leader (implicit) | tentative (no change) | no |
| Complete | leader + all followers | applied | n/a |
The transition from incomplete → durable is implicit (no new message); the transition from durable → complete is explicit (complete message to each follower).
Canonical formulation¶
Sugu Sougoumarane, consensus-algorithms-at-scale part 6: "The above requirements can be met by introducing a two-phase protocol. The leader first transmits the payload of the request as tentative to all the nodes. A tentative request is one that can later be completed or canceled. A follower that is responsible for a leader's durability should acknowledge receipt of tentative requests. Once the leader receives the necessary acknowledgements from its followers, the request has become durable and cannot be canceled. The leader can then issue messages to complete the tentative request."
(Source: sources/2026-04-21-planetscale-consensus-algorithms-at-scale-part-6-completing-requests)
The mutual-exclusion invariant¶
The load-bearing safety property:
"Completion and cancellation are mutually exclusive: A request that was completed will never be canceled, and a request that was canceled will never be completed."
Clients and higher-layer protocols (idempotency tokens, retry logic) can rely on this: a success ack is not going to be silently rolled back later.
Composition with other patterns¶
- patterns/skip-completion-for-late-followers — once the request is durable, the leader can fast-forward lagging followers from no-state straight to
complete, skipping thetentativestep. - patterns/early-ack-on-durability — the leader can ack the client as soon as durability is met (not waiting for completion), saving one round-trip at the cost of needing quorum reads or a leader lease for consistency.
- patterns/lock-based-leader-election — the election shape that enables a valid leader lease and therefore cheap leader-local reads after an early ack.
Not two-phase commit¶
Despite the name similarity, this is not two-phase commit (2PC). Both have a prepare-like phase and a commit-like phase, but the invariants are different: 2PC delivers cross-participant atomicity for distributed transactions; two-phase tentative-then-complete delivers single-write durability for consensus. A consensus system can of course run 2PC on top for cross-shard atomicity.
The canonical non-example: MySQL semi-sync¶
MySQL semi-sync replication lacks the tentative step — "a replica receives a request, it immediately applies it". It therefore also lacks a clean cancellation path, and a primary that restarts after a crash can complete writes that were never durable, producing concepts/mysql-semi-sync-split-brain. Vitess deployments on top of MySQL have to paper over the gap operationally through reparenting tooling, lameduck drain, and vtgate query buffering.
Applicability¶
Use when:
- Consensus safety requires "never forget an ack'd request" plus "never apply a non-durable request" as simultaneous invariants.
- Leadership changes are possible and the elector needs to reason about in-flight work cleanly.
- The system will later add a read-path optimisation (lease, quorum read) that benefits from knowing whether a request is durable vs complete.
Skip when:
- The system is single-node (no replication) — the tentative state buys nothing.
- The system is eventually consistent with CRDT merge semantics (concepts/no-distributed-consensus) — there is no "the leader decides" altitude where this protocol operates.
Seen in¶
- sources/2026-04-21-planetscale-consensus-algorithms-at-scale-part-6-completing-requests — canonical wiki introduction by Sugu Sougoumarane in the sixth instalment of his consensus-algorithms series.
Related¶
- concepts/tentative-request / concepts/durable-request / concepts/two-phase-completion-protocol / concepts/request-cancellation — the concepts that together define this pattern.
- patterns/skip-completion-for-late-followers — the lagging-follower optimisation.
- patterns/early-ack-on-durability — the latency optimisation at the client boundary.
- concepts/mysql-semi-sync-split-brain — the canonical non-example that motivates the pattern.