1use log::{debug, error};
2use rand::{Rng, SeedableRng};
3use rand_chacha::ChaCha20Rng;
4use serde::{Deserialize, Serialize};
5use std::fmt::{Display, Formatter};
6use std::time::SystemTime;
7
8use dcs::config::{get_argument, SystemNodeArgs};
9use dcs::membership::metadata::{BasicMetadata, CoordinationMetadataBuilder, SerializableMetadata};
10use dcs::nodes::NodeRole;
11
12#[derive(Clone, Default, Debug, Serialize, Deserialize)]
13pub struct RaftMetadata {
14 pub timeout: u64,
15}
16
17impl RaftMetadata {
18 pub fn new(timeout: u64) -> Self {
19 Self { timeout }
20 }
21}
22
23impl BasicMetadata for RaftMetadata {}
24
25impl SerializableMetadata for RaftMetadata {}
26
27impl Display for RaftMetadata {
28 fn fmt(&self, formatter: &mut Formatter<'_>) -> std::fmt::Result {
29 write!(formatter, "[Timeout: {}]", self.timeout)
30 }
31}
32
33#[derive(Default)]
34pub struct RaftMetadataBuilder;
35
36impl RaftMetadataBuilder {
37 fn randomize_timeout(original_timeout: u64) -> u64 {
38 let seed = SystemTime::now()
39 .duration_since(SystemTime::UNIX_EPOCH)
40 .unwrap()
41 .as_secs();
42 let mut rng = ChaCha20Rng::seed_from_u64(seed);
43 let random_timeout = rng.gen_range(original_timeout..original_timeout * 6);
44 debug!("Random timeout: {}", random_timeout);
45 random_timeout
46 }
47}
48
49impl CoordinationMetadataBuilder<RaftMetadata> for RaftMetadataBuilder {
50 fn build(args: &SystemNodeArgs) -> RaftMetadata {
51 match args.role {
52 NodeRole::SENSOR(_) => {
53 let timeout = get_argument(args.clone().coordination.unwrap(), "timeout")
54 .and_then(|val| val.parse::<u64>().ok())
55 .unwrap_or_else(|| {
56 error!("TIMEOUT argument expected.");
57 panic!()
58 });
59
60 let timeout = RaftMetadataBuilder::randomize_timeout(timeout);
61 log::info!("Generated timeout of {} for Raft.", timeout);
62 RaftMetadata::new(timeout)
63 }
64 _ => {
65 error!(
66 "Shouldn't try to parse Raft metadata for an {:?}.",
67 args.role
68 );
69 panic!()
70 }
71 }
72 }
73}