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 /// Timeout for `leave()` and `abort()` oneshot replies.
43 pub request_timeout: Duration,
44}
45
46impl ClusterConfig {
47 /// Create a config with loopback defaults and no seed nodes.
48 #[must_use]
49 pub fn new(node_id: impl Into<String>) -> Self {
50 Self {
51 node_id: node_id.into(),
52 ..Self::default()
53 }
54 }
55
56 /// Return a copy using the supplied roles.
57 #[must_use]
58 pub fn with_roles(mut self, roles: impl IntoIterator<Item = impl Into<String>>) -> Self {
59 self.roles = roles.into_iter().map(Into::into).collect();
60 self
61 }
62
63 /// Return a copy using the supplied seed-node addresses.
64 #[must_use]
65 pub fn with_seed_nodes(mut self, seed_nodes: impl IntoIterator<Item = SocketAddr>) -> Self {
66 self.seed_nodes = seed_nodes.into_iter().collect();
67 self
68 }
69
70 pub(crate) fn normalized_roles(&self) -> Vec<String> {
71 let mut roles = self
72 .roles
73 .iter()
74 .map(|role| role.trim().to_owned())
75 .filter(|role| !role.is_empty())
76 .collect::<Vec<_>>();
77 roles.sort();
78 roles.dedup();
79 roles
80 }
81}
82
83impl Default for ClusterConfig {
84 fn default() -> Self {
85 let loopback = IpAddr::V4(Ipv4Addr::LOCALHOST);
86 Self {
87 node_id: format!("datum-node-{}", std::process::id()),
88 roles: Vec::new(),
89 bind_addr: SocketAddr::new(loopback, 0),
90 advertise_addr: SocketAddr::new(loopback, 0),
91 agent_addr: None,
92 seed_nodes: Vec::new(),
93 gossip_interval: Duration::from_millis(250),
94 probe_timeout: Duration::from_millis(75),
95 suspect_timeout: Duration::from_secs(2),
96 downing_timeout: Duration::from_millis(500),
97 remove_down_after: Duration::from_secs(60),
98 max_packet_size: 1400,
99 event_buffer: 256,
100 request_timeout: Duration::from_secs(5),
101 }
102 }
103}