Oregami
Repositories/oxedyne/fe2o3

oxedyne/fe2o3/fe2o3_o3db_sync/src/dist/config.rs

5.2 KiB, 63 runs

created by r1870400018:11380, which is this file's identity for as long as the history lasts, whatever it is later renamed to

download · who wrote it · its history

1//! Configuration types for distributed Ozone.
2//!
3//! A [`DistOzoneConfig`] block is the caller's one-stop description of
4//! distributed mode at start-up. It bundles the local peer's identity, the
5//! initial bootstrap peer list, the OAM placement parameters and the
6//! per-table consistency / anti-entropy cadence declarations.
7//!
8//! The config is *static* once a [`DistOzone`](crate::dist::DistOzone) engine
9//! is constructed; runtime mutation (peers joining, leaving, network size
10//! re-estimated) flows through the engine's own methods rather than through
11//! the config.
12//!
13//! [Written with AI entirely](https://need2know.ai/entirely-ai/code)\
14//! Anthropic Claude
15
16use oxedyne_fe2o3_core::prelude::*;
17use crate::kademlia::id::NodeId;
18use crate::oam::config::OamConfig;
19
20use std::time::Duration;
21
22
23/// The consistency guarantee a table provides under distributed mode.
24///
25/// *Eventual* tables accept concurrent writes and converge through IBLT
26/// anti-entropy. *Cohort-backed* tables serialise writes through a HotStuff
27/// consensus cohort and reach strict consistency after three message rounds.
28#[derive(Clone, Copy, Debug, Eq, PartialEq)]
29pub enum Consistency {
30 Eventual, // writes land locally and at every OAM holder
31 // Tolerates up to floor((lambda - 1) / 3) Byzantine members.
32 Cohort {
33 lambda: u64, // 5, 7 or 9
34 },
35}
36
37
38/// Per-table configuration: the name, the consistency model, the
39/// anti-entropy cadence, and the IBLT sketch dimensions used for
40/// anti-entropy reconciliation.
41#[derive(Clone, Debug, Eq, PartialEq)]
42pub struct TableConfig {
43 pub name: String, // unique within a DistOzoneConfig
44 pub consistency: Consistency,
45 pub anti_entropy: Duration, // ignored for Cohort tables
46 pub iblt_cells: usize, // 1.5 x d cells decode a difference of d
47}
48
49impl TableConfig {
50 // Identity directory, oxedation log, name claims.
51 pub const DEFAULT_AE: Duration = Duration::from_secs(30);
52
53 // Small high-value tables -- the peer set, the revocation list.
54 pub const HIGH_VALUE_AE: Duration = Duration::from_secs(3);
55
56 // Tuned for a steady-state symmetric difference of up to ~160 records
57 // (256 / 1.5). Larger differences overload the sketch; the anti-entropy
58 // handler falls back to a bulk transfer when decoding fails.
59 pub const DEFAULT_IBLT_CELLS: usize = 256;
60
61 /// The number of hash functions the anti-entropy IBLT uses. Fixed at
62 /// three across the crate, matching the sizing rule of thumb in
63 /// `fe2o3_data::iblt`.
64 pub const IBLT_NUM_HASHES: usize = 3;
65
66 pub fn new<S: Into<String>>(
67 name: S,
68 consistency: Consistency,
69 anti_entropy: Duration,
70 iblt_cells: usize,
71 )
72 -> Outcome<Self>
73 {
74 let name = name.into();
75 if name.is_empty() {
76 return Err(err!(
77 "TableConfig requires a non-empty name.";
78 Invalid, Input, Missing));
79 }
80 if let Consistency::Cohort { lambda } = consistency {
81 if !matches!(lambda, 5 | 7 | 9) {
82 return Err(err!(
83 "Cohort lambda must be 5, 7 or 9; got {}.", lambda;
84 Invalid, Input, Size));
85 }
86 }
87 if iblt_cells == 0 {
88 return Err(err!(
89 "TableConfig requires iblt_cells > 0.";
90 Invalid, Input, Size));
91 }
92 Ok(Self { name, consistency, anti_entropy, iblt_cells })
93 }
94
95 pub fn eventual<S: Into<String>>(name: S) -> Outcome<Self> {
96 Self::new(
97 name,
98 Consistency::Eventual,
99 Self::DEFAULT_AE,
100 Self::DEFAULT_IBLT_CELLS,
101 )
102 }
103
104 /// Cohort size 5.
105 pub fn cohort_default<S: Into<String>>(name: S) -> Outcome<Self> {
106 Self::new(
107 name,
108 Consistency::Cohort { lambda: 5 },
109 Self::DEFAULT_AE,
110 Self::DEFAULT_IBLT_CELLS,
111 )
112 }
113
114 /// The IBLT's splitmix64 salt, derived from the table name so that
115 /// different tables have different hash functions.
116 pub fn iblt_seed(&self) -> u64 {
117 let mut state: u64 = 0x9E3779B97F4A7C15;
118 for byte in self.name.as_bytes() {
119 state = state.wrapping_add(*byte as u64);
120 state = (state ^ (state >> 30)).wrapping_mul(0xBF58476D1CE4E5B9);
121 state = (state ^ (state >> 27)).wrapping_mul(0x94D049BB133111EB);
122 state ^= state >> 31;
123 }
124 state
125 }
126}
127
128
129/// Top-level configuration for distributed Ozone mode.
130#[derive(Clone, Debug)]
131pub struct DistOzoneConfig {
132 pub local_peer_id: NodeId,
133 pub bootstrap_peers: Vec<NodeId>, // the local peer is filtered out
134 pub oam: OamConfig, // network_size is the initial value only
135 pub tables: Vec<TableConfig>, // unique names
136}
137
138impl DistOzoneConfig {
139 pub fn new(
140 local_peer_id: NodeId,
141 bootstrap_peers: Vec<NodeId>,
142 oam: OamConfig,
143 tables: Vec<TableConfig>,
144 )
145 -> Outcome<Self>
146 {
147 if tables.is_empty() {
148 return Err(err!(
149 "DistOzoneConfig requires at least one table.";
150 Invalid, Input, Missing));
151 }
152 // Detect duplicate table names via a pairwise scan. Table counts are
153 // small (single-digits to low tens) so quadratic is fine and avoids
154 // pulling HashSet into a config type.
155 for i in 0..tables.len() {
156 for j in (i + 1)..tables.len() {
157 if tables[i].name == tables[j].name {
158 return Err(err!(
159 "Duplicate table name in DistOzoneConfig: {}.",
160 tables[i].name;
161 Invalid, Input, Duplicate));
162 }
163 }
164 }
165 Ok(Self { local_peer_id, bootstrap_peers, oam, tables })
166 }
167
168 pub fn table(&self, name: &str) -> Option<&TableConfig> {
169 self.tables.iter().find(|t| t.name == name)
170 }
171}