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

Function bootstrap

nodedb-cluster/src/bootstrap/bootstrap_fn.rs:23–86  ·  view source on GitHub ↗

Bootstrap a new cluster: this node is the founding member. `local_spki_pin` is the SHA-256 SPKI fingerprint of this node's own TLS leaf certificate; `None` in insecure transport mode. When present it is stored in the founding node's `NodeInfo` so that joining peers can pin our identity immediately.

(
    config: &ClusterConfig,
    catalog: &ClusterCatalog,
    local_spki_pin: Option<[u8; 32]>,
)

Source from the content-addressed store, hash-verified

21/// stored in the founding node's `NodeInfo` so that joining peers can pin our
22/// identity immediately.
23pub(super) fn bootstrap(
24 config: &ClusterConfig,
25 catalog: &ClusterCatalog,
26 local_spki_pin: Option<[u8; 32]>,
27) -> Result<ClusterState> {
28 info!(
29 node_id = config.node_id,
30 addr = %config.listen_addr,
31 groups = config.num_groups,
32 "bootstrapping new cluster"
33 );
34
35 // Create topology with this node.
36 let mut topology = ClusterTopology::new();
37 topology.add_node(
38 NodeInfo::new(config.node_id, config.listen_addr, NodeState::Active)
39 .with_spki_pin(local_spki_pin),
40 );
41
42 // Create routing table: all groups on this single node.
43 let routing = RoutingTable::uniform(
44 config.num_groups,
45 &[config.node_id],
46 config.replication_factor.min(1), // Single node → RF=1.
47 );
48
49 // Create MultiRaft with all groups (single-node, no peers).
50 let mut multi_raft = MultiRaft::new(config.node_id, routing.clone(), config.data_dir.clone())
51 .with_election_timeout(config.election_timeout_min, config.election_timeout_max);
52 for group_id in routing.group_ids() {
53 multi_raft.add_group(group_id, vec![])?;
54 }
55
56 // Kick every group's election deadline into the past so the very
57 // first tick of `RaftLoop::run` elects this node as leader of each
58 // group. Otherwise an incoming `JoinRequest` that arrives before the
59 // random 150–300 ms election timeout fires would hit a non-leader
60 // node and be rejected. The bootstrap seed is by definition the only
61 // voter in every group, so self-election is unambiguous.
62 let now = std::time::Instant::now();
63 for node in multi_raft.groups_mut().values_mut() {
64 node.election_deadline_override(now - std::time::Duration::from_millis(1));
65 }
66
67 // Generate cluster ID and persist everything.
68 let cluster_id = generate_cluster_id();
69 catalog.save_cluster_id(cluster_id)?;
70 catalog.save_cluster_settings(&ClusterSettings::from_config(config))?;
71 catalog.save_topology(&topology)?;
72 catalog.save_routing(&routing)?;
73
74 info!(
75 node_id = config.node_id,
76 cluster_id,
77 groups = config.num_groups,
78 "cluster bootstrapped"
79 );
80

Callers 4

start_clusterFunction · 0.85
restart_from_catalogFunction · 0.85
full_bootstrap_join_flowFunction · 0.85

Calls 14

nowFunction · 0.85
generate_cluster_idFunction · 0.85
with_spki_pinMethod · 0.80
with_election_timeoutMethod · 0.80
add_groupMethod · 0.80
groups_mutMethod · 0.80
save_cluster_idMethod · 0.80
save_cluster_settingsMethod · 0.80
save_topologyMethod · 0.80
save_routingMethod · 0.80
add_nodeMethod · 0.45

Tested by 1