datum_cluster/config.rs
1use std::{
2 net::{IpAddr, Ipv4Addr, SocketAddr},
3 time::Duration,
4};
5
6/// Configuration for one Datum cluster member.
7#[derive(Debug, Clone)]
8pub struct ClusterConfig {
9 /// Stable logical node id used by Datum control-plane APIs.
10 pub node_id: String,
11 /// Akka-style role labels advertised in membership gossip.
12 pub roles: Vec<String>,
13 /// Local UDP address used for membership gossip.
14 pub bind_addr: SocketAddr,
15 /// Address peers should use to contact this node. If the port is `0`, the
16 /// bound UDP port is substituted at startup.
17 pub advertise_addr: SocketAddr,
18 /// DCP agent address peers should use for node-to-node control sessions.
19 ///
20 /// `ClusterAgent` fills this from the bound DCP listener when it starts the
21 /// agent and membership together. Bare membership nodes may leave it empty.
22 pub agent_addr: Option<SocketAddr>,
23 /// Seed node UDP addresses. The seed identity is resolved by foca from the
24 /// first announce reply, so only addresses are required here.
25 pub seed_nodes: Vec<SocketAddr>,
26 /// Foca probe period and periodic gossip cadence.
27 pub gossip_interval: Duration,
28 /// Direct probe round-trip timeout before foca asks indirect probes.
29 pub probe_timeout: Duration,
30 /// Foca suspect-to-down timeout. Keep this longer than the downing timeout
31 /// when `TimeoutDowning` should make the public downing decision first.
32 pub suspect_timeout: Duration,
33 /// Default timeout used by [`crate::TimeoutDowning`].
34 pub downing_timeout: Duration,
35 /// How long foca remembers down identities before accepting the exact same
36 /// identity again. New incarnations can rejoin before this expires.
37 pub remove_down_after: Duration,
38 /// Maximum UDP packet size foca will produce and consume.
39 pub max_packet_size: usize,
40 /// Bounded event-feed capacity for [`crate::MemberEvent`] subscribers.
41 pub event_buffer: usize,
42}
43
44impl ClusterConfig {
45 /// Create a config with loopback defaults and no seed nodes.
46 #[must_use]
47 pub fn new(node_id: impl Into<String>) -> Self {
48 Self {
49 node_id: node_id.into(),
50 ..Self::default()
51 }
52 }
53
54 /// Return a copy using the supplied roles.
55 #[must_use]
56 pub fn with_roles(mut self, roles: impl IntoIterator<Item = impl Into<String>>) -> Self {
57 self.roles = roles.into_iter().map(Into::into).collect();
58 self
59 }
60
61 /// Return a copy using the supplied seed-node addresses.
62 #[must_use]
63 pub fn with_seed_nodes(mut self, seed_nodes: impl IntoIterator<Item = SocketAddr>) -> Self {
64 self.seed_nodes = seed_nodes.into_iter().collect();
65 self
66 }
67
68 pub(crate) fn normalized_roles(&self) -> Vec<String> {
69 let mut roles = self
70 .roles
71 .iter()
72 .map(|role| role.trim().to_owned())
73 .filter(|role| !role.is_empty())
74 .collect::<Vec<_>>();
75 roles.sort();
76 roles.dedup();
77 roles
78 }
79}
80
81impl Default for ClusterConfig {
82 fn default() -> Self {
83 let loopback = IpAddr::V4(Ipv4Addr::LOCALHOST);
84 Self {
85 node_id: format!("datum-node-{}", std::process::id()),
86 roles: Vec::new(),
87 bind_addr: SocketAddr::new(loopback, 0),
88 advertise_addr: SocketAddr::new(loopback, 0),
89 agent_addr: None,
90 seed_nodes: Vec::new(),
91 gossip_interval: Duration::from_millis(250),
92 probe_timeout: Duration::from_millis(75),
93 suspect_timeout: Duration::from_secs(2),
94 downing_timeout: Duration::from_millis(500),
95 remove_down_after: Duration::from_secs(60),
96 max_packet_size: 1400,
97 event_buffer: 256,
98 }
99 }
100}