(
node_id: u64,
transport: Arc<NexarTransport>,
seed_nodes: Vec<SocketAddr>,
data_dir_path: PathBuf,
owned_data_dir: Option<TempDir>,
)
| 159 | } |
| 160 | |
| 161 | pub async fn spawn_inner( |
| 162 | node_id: u64, |
| 163 | transport: Arc<NexarTransport>, |
| 164 | seed_nodes: Vec<SocketAddr>, |
| 165 | data_dir_path: PathBuf, |
| 166 | owned_data_dir: Option<TempDir>, |
| 167 | ) -> Result<Self, Box<dyn std::error::Error + Send + Sync>> { |
| 168 | let catalog = Arc::new(ClusterCatalog::open(&data_dir_path.join("cluster.redb"))?); |
| 169 | let listen_addr = transport.local_addr(); |
| 170 | |
| 171 | // Empty seeds → imply single-node bootstrap by listing only |
| 172 | // our own address. Otherwise use whatever the caller supplied. |
| 173 | let seeds = if seed_nodes.is_empty() { |
| 174 | vec![listen_addr] |
| 175 | } else { |
| 176 | seed_nodes |
| 177 | }; |
| 178 | |
| 179 | let config = ClusterConfig { |
| 180 | node_id, |
| 181 | listen_addr, |
| 182 | seed_nodes: seeds, |
| 183 | num_groups: 2, |
| 184 | replication_factor: 3, |
| 185 | data_dir: data_dir_path.clone(), |
| 186 | force_bootstrap: false, |
| 187 | // Fast retry policy: 2 s ceiling keeps the join-failure |
| 188 | // tests (especially `cluster_join_leader_crash`) under |
| 189 | // ~5 s of sleeping instead of the production ~64 s. |
| 190 | join_retry: nodedb_cluster::JoinRetryPolicy { |
| 191 | max_attempts: 8, |
| 192 | max_backoff_secs: 2, |
| 193 | }, |
| 194 | swim_udp_addr: None, |
| 195 | election_timeout_min: std::time::Duration::from_millis(150), |
| 196 | election_timeout_max: std::time::Duration::from_millis(300), |
| 197 | install_snapshot_chunk_bytes: 4 * 1024 * 1024, |
| 198 | orphan_partial_max_age_secs: 300, |
| 199 | }; |
| 200 | |
| 201 | let lifecycle = ClusterLifecycleTracker::new(); |
| 202 | let state = start_cluster(&config, &catalog, Arc::clone(&transport), &lifecycle).await?; |
| 203 | // Match the main binary: the caller is responsible for the |
| 204 | // final `Ready` transition once the node is wired up. The |
| 205 | // node count is whatever the node observed at the moment of |
| 206 | // transition. |
| 207 | lifecycle.to_ready(state.topology.read().map(|t| t.node_count()).unwrap_or(0)); |
| 208 | |
| 209 | // state.topology is already Arc<RwLock<ClusterTopology>>. |
| 210 | let topology = state.topology.clone(); |
| 211 | // Real in-memory metadata cache, driven by a `CacheApplier` |
| 212 | // installed on the raft loop. Every test can read this |
| 213 | // directly to assert DDL replication. |
| 214 | let metadata_cache = Arc::new(RwLock::new(MetadataCache::new())); |
| 215 | let metadata_applier: Arc<dyn nodedb_cluster::MetadataApplier> = |
| 216 | Arc::new(CacheApplier::new(metadata_cache.clone())); |
| 217 | // `start_cluster` no longer spawns subsystems, so its |
| 218 | // `Arc<Mutex<MultiRaft>>` has exactly one strong owner here. |
nothing calls this directly
no test coverage detected