Skip to main content

reddb_server/replication/
mod.rs

1//! Replication Module
2//!
3//! Implements single-primary, multi-replica replication via WAL streaming.
4//!
5//! # Architecture
6//!
7//! - Primary: accepts writes and streams WAL records to replicas
8//! - Replica: read-only, connects to primary for WAL streaming
9//! - Initial sync via snapshot transfer, then incremental WAL
10//!
11//! # Usage
12//!
13//! ```ignore
14//! // Primary
15//! let options = RedDBOptions::persistent("./primary-data")
16//!     .with_replication(ReplicationConfig::primary());
17//!
18//! // Replica
19//! let options = RedDBOptions::persistent("./replica-data")
20//!     .with_replication(ReplicationConfig::replica("http://primary:50051"));
21//! ```
22
23pub mod cdc;
24pub mod commit_policy;
25pub mod commit_waiter;
26pub mod lease;
27pub mod logical;
28pub mod primary;
29pub mod quorum;
30pub mod replica;
31pub mod scheduler;
32pub mod topology_advertiser;
33
34pub use commit_policy::CommitPolicy;
35pub use commit_waiter::{AwaitOutcome, CommitWaiter};
36pub use lease::{LeaseError, LeaseStore, WriterLease};
37pub use quorum::{QuorumConfig, QuorumCoordinator, QuorumError};
38pub use topology_advertiser::{
39    LagConfig, TopologyAdvertiser, TopologyAuthGate, DEFAULT_REPLICA_TIMEOUT_MS,
40    TOPOLOGY_READ_CAPABILITY,
41};
42
43/// Role of this RedDB instance in a replication cluster.
44#[derive(Debug, Clone, PartialEq, Eq, Default)]
45pub enum ReplicationRole {
46    /// Standalone instance (default, no replication).
47    #[default]
48    Standalone,
49    /// Primary: accepts reads and writes, streams WAL to replicas.
50    Primary,
51    /// Replica: read-only, receives WAL from primary.
52    Replica {
53        /// gRPC address of the primary (e.g., "http://primary:50051")
54        primary_addr: String,
55    },
56}
57
58/// Configuration for replication.
59#[derive(Debug, Clone)]
60pub struct ReplicationConfig {
61    pub role: ReplicationRole,
62    /// How often replica polls for new WAL records (milliseconds).
63    pub poll_interval_ms: u64,
64    /// Maximum batch size for WAL record transfer.
65    pub max_batch_size: usize,
66    /// Region identifier for this instance (Phase 2.6 multi-region).
67    ///
68    /// Used by the quorum coordinator to spread write acks across
69    /// fault domains: `QuorumConfig::required_regions` forces a commit
70    /// to wait until at least one replica in each listed region has
71    /// acked. Defaults to `"local"` for single-region deployments.
72    pub region: String,
73    /// Quorum configuration (Phase 2.6 multi-region).
74    pub quorum: QuorumConfig,
75}
76
77impl ReplicationConfig {
78    pub fn standalone() -> Self {
79        Self {
80            role: ReplicationRole::Standalone,
81            poll_interval_ms: 100,
82            max_batch_size: 1000,
83            region: "local".to_string(),
84            quorum: QuorumConfig::async_commit(),
85        }
86    }
87
88    pub fn primary() -> Self {
89        Self {
90            role: ReplicationRole::Primary,
91            poll_interval_ms: 100,
92            max_batch_size: 1000,
93            region: "local".to_string(),
94            quorum: QuorumConfig::async_commit(),
95        }
96    }
97
98    pub fn replica(primary_addr: impl Into<String>) -> Self {
99        Self {
100            role: ReplicationRole::Replica {
101                primary_addr: primary_addr.into(),
102            },
103            poll_interval_ms: 100,
104            max_batch_size: 1000,
105            region: "local".to_string(),
106            quorum: QuorumConfig::async_commit(),
107        }
108    }
109
110    /// Attach a quorum configuration (fluent setter).
111    pub fn with_quorum(mut self, quorum: QuorumConfig) -> Self {
112        self.quorum = quorum;
113        self
114    }
115
116    /// Set the region identifier (fluent setter).
117    pub fn with_region(mut self, region: impl Into<String>) -> Self {
118        self.region = region.into();
119        self
120    }
121}