MCPcopy Create free account
hub / github.com/NodeDB-Lab/nodedb / apply_join_response

Function apply_join_response

nodedb-cluster/src/bootstrap/join.rs:242–359  ·  view source on GitHub ↗

Apply a JoinResponse: reconstruct topology, routing, and MultiRaft from wire data. Order of operations is load-bearing for crash safety: 1. Reconstruct the `ClusterTopology` and `RoutingTable` in memory. 2. Persist topology + routing to the catalog **first**, before any on-disk side effects. If we crash after this step, the next boot sees `catalog.is_bootstrapped() == true` and takes the `restar

(
    config: &ClusterConfig,
    catalog: &ClusterCatalog,
    transport: &NexarTransport,
    resp: &JoinResponse,
)

Source from the content-addressed store, hash-verified

240/// 4. Register every peer address in the transport before returning
241/// so the first outgoing AppendEntries has a known destination.
242fn apply_join_response(
243 config: &ClusterConfig,
244 catalog: &ClusterCatalog,
245 transport: &NexarTransport,
246 resp: &JoinResponse,
247) -> Result<ClusterState> {
248 // 1. Reconstruct topology.
249 let mut topology = ClusterTopology::new();
250 for node in &resp.nodes {
251 let state = NodeState::from_u8(node.state).unwrap_or(NodeState::Active);
252 let spki_pin: Option<[u8; 32]> = node.spki_pin.as_deref().and_then(|b| {
253 if b.len() == 32 {
254 let mut arr = [0u8; 32];
255 arr.copy_from_slice(b);
256 Some(arr)
257 } else {
258 None
259 }
260 });
261 let mut info = NodeInfo::new(
262 node.node_id,
263 node.addr.parse().unwrap_or_else(|_| {
264 "0.0.0.0:0"
265 .parse()
266 .expect("invariant: \"0.0.0.0:0\" is a valid SocketAddr literal")
267 }),
268 state,
269 )
270 .with_wire_version(node.wire_version)
271 .with_spiffe_id(node.spiffe_id.clone())
272 .with_spki_pin(spki_pin);
273 // Override raft_groups from wire data (NodeInfo::new starts empty).
274 info.raft_groups = node.raft_groups.clone();
275 if node.node_id == config.node_id {
276 info.state = NodeState::Active;
277 }
278 topology.add_node(info);
279 }
280
281 // 1. Reconstruct routing table.
282 let mut group_members = std::collections::HashMap::new();
283 for g in &resp.groups {
284 group_members.insert(
285 g.group_id,
286 GroupInfo {
287 leader: g.leader,
288 members: g.members.clone(),
289 learners: g.learners.clone(),
290 },
291 );
292 }
293 let routing = RoutingTable::from_parts(resp.vshard_to_group.clone(), group_members);
294
295 // 2. Persist to catalog before any on-disk Raft side effects.
296 // Cluster id is written first so `is_bootstrapped()` returns
297 // `true` on any subsequent boot — without this, a joined node
298 // that restarts would re-enter the bootstrap/join path
299 // instead of taking `restart()`. Zero is a valid marker: the

Callers 1

try_join_onceFunction · 0.85

Calls 15

with_spki_pinMethod · 0.80
with_spiffe_idMethod · 0.80
with_wire_versionMethod · 0.80
save_cluster_idMethod · 0.80
save_topologyMethod · 0.80
save_routingMethod · 0.80
with_election_timeoutMethod · 0.80
collectMethod · 0.80
add_groupMethod · 0.80
add_group_as_learnerMethod · 0.80
register_peerMethod · 0.80
lenMethod · 0.45

Tested by

no test coverage detected