Skip to content

feat(cluster): CLUSTER FAILOVER state machine - #188

Merged
kacy merged 1 commit into
mainfrom
feat/cluster-failover
Feb 19, 2026
Merged

feat(cluster): CLUSTER FAILOVER state machine#188
kacy merged 1 commit into
mainfrom
feat/cluster-failover

Conversation

@kacy

@kacy kacy commented Feb 19, 2026

Copy link
Copy Markdown
Owner

summary

implements CLUSTER FAILOVER [FORCE|TAKEOVER] — the manual failover command that promotes a replica to primary.

three modes:

  • default: waits 500ms for the replication stream to deliver in-flight records, then proposes PromoteReplica + slot transfer via Raft for cluster-wide agreement. safe for planned maintenance failovers.
  • force: same as default but skips the 500ms grace period. use when the primary is unreachable and you accept possible data loss of in-flight writes.
  • takeover: bypasses Raft entirely. updates local state and propagates the promotion via gossip. use when Raft quorum is lost and you need an emergency promotion.

topology.rs — new ClusterState::promote_replica(replica_id):

  • transfers all slots from the old primary to the promoted node in the slot map
  • demotes the old primary to NodeRole::Replica (pointing back at the new primary)
  • updates both nodes' replica lists and slots cache
  • bumps the global config_epoch for conflict resolution

cluster.rsClusterCoordinator::cluster_failover(force, takeover):

  • validates this node is a replica with a configured primary
  • issues ClusterCommand::PromoteReplica + AssignSlots + RemoveSlots via Raft
  • applies the state locally immediately so the node starts accepting writes without waiting for Raft reconciliation
  • announces the role change via gossip
  • adds writes_paused: AtomicBool with pause_writes / resume_writes for future primary-side coordination during default failover

connection.rs:

  • replaces the stub with a call to cluster.cluster_failover(force, takeover)
  • write rejection now also checks is_writes_paused() so a primary can be paused during coordinated failover

what was tested

  • cargo test --workspace: 86 cluster unit tests, 68 server unit tests — all pass
  • cargo clippy --workspace -- -D warnings: clean
  • new tests: promote_replica_transfers_slots, promote_replica_rejects_non_replica, promote_replica_rejects_unknown_node (topology), failover_rejected_on_primary, failover_takeover_promotes_replica, writes_paused_blocks_and_resumes (cluster coordinator)

design considerations

the 500ms grace period in default mode is a heuristic — a proper implementation would track the exact replication offset per shard and wait for the replica to reach the primary's committed offset. that requires the replication client to expose its current offset back to the coordinator, which is a natural follow-up to pr 4 (automatic failover).

pause_writes and resume_writes on the primary side require a coordination channel (the primary needs to receive a pause request from the replica). the infrastructure is now in place; the protocol message is the remaining piece.

adds CLUSTER FAILOVER [FORCE|TAKEOVER] to promote a replica to primary.

topology.rs: new ClusterState::promote_replica(replica_id) method
  - transfers all slots from the old primary to the promoted node
  - demotes the old primary to replica status (replicates the new primary)
  - updates replica lists on both nodes
  - bumps the global config epoch for conflict resolution

cluster.rs: ClusterCoordinator::cluster_failover(force, takeover)
  - default: waits 500ms for replication to catch up, then proposes
    PromoteReplica + slot transfer via Raft for cluster-wide agreement
  - force: same as default but skips the grace period
  - takeover: bypasses Raft entirely; updates local state and announces
    the new role via gossip (for when Raft quorum is unavailable)
  - adds writes_paused AtomicBool + pause_writes/resume_writes for
    future primary-side coordination during default failover

connection.rs:
  - replaces the FAILOVER stub with a call to cluster_failover
  - adds is_writes_paused check to the write rejection gate
@kacy
kacy merged commit e34cc04 into main Feb 19, 2026
4 of 7 checks passed
@kacy
kacy deleted the feat/cluster-failover branch February 19, 2026 01:36
kacy added a commit that referenced this pull request Feb 19, 2026
adds CLUSTER FAILOVER [FORCE|TAKEOVER] to promote a replica to primary.

topology.rs: new ClusterState::promote_replica(replica_id) method
  - transfers all slots from the old primary to the promoted node
  - demotes the old primary to replica status (replicates the new primary)
  - updates replica lists on both nodes
  - bumps the global config epoch for conflict resolution

cluster.rs: ClusterCoordinator::cluster_failover(force, takeover)
  - default: waits 500ms for replication to catch up, then proposes
    PromoteReplica + slot transfer via Raft for cluster-wide agreement
  - force: same as default but skips the grace period
  - takeover: bypasses Raft entirely; updates local state and announces
    the new role via gossip (for when Raft quorum is unavailable)
  - adds writes_paused AtomicBool + pause_writes/resume_writes for
    future primary-side coordination during default failover

connection.rs:
  - replaces the FAILOVER stub with a call to cluster_failover
  - adds is_writes_paused check to the write rejection gate
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant