Skip to main content

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}