spacedb_consistency/tier.rs
1//! Consistency tiers and the per-field schema that selects them.
2//!
3//! In a partition-prone world one global consistency setting is always wrong:
4//! most data wants availability, a little wants linearizability, and only the
5//! developer knows which is which. So consistency is a **per-field choice**,
6//! declared in the schema.
7
8use std::collections::HashMap;
9
10/// The consistency a field is served at.
11#[derive(Clone, Copy, Debug, PartialEq, Eq)]
12pub enum Tier {
13 /// CRDT, the default for ~95% of data: always writable offline, auto-merging,
14 /// never blocks. Content, profiles, tags, feeds, tallies.
15 Convergent,
16 /// Session causal+: read-your-writes and monotonic reads via a causal token
17 /// over state vectors — cheap and partition-tolerant, no consensus.
18 Causal,
19 /// Linearizable, opt-in and deliberately expensive: uniqueness, non-negative
20 /// invariants, money. A quorum that **fails safe** under partition.
21 Strong,
22}
23
24/// Which tier each field is served at; everything defaults to [`Tier::Convergent`].
25#[derive(Clone, Debug)]
26pub struct ConsistencySchema {
27 default: Tier,
28 fields: HashMap<String, Tier>,
29}
30
31impl Default for ConsistencySchema {
32 fn default() -> Self {
33 Self {
34 default: Tier::Convergent,
35 fields: HashMap::new(),
36 }
37 }
38}
39
40impl ConsistencySchema {
41 /// A schema where every field defaults to [`Tier::Convergent`].
42 pub fn new() -> Self {
43 Self::default()
44 }
45
46 /// Annotate `field` with `tier` (builder style).
47 pub fn with_field(mut self, field: impl Into<String>, tier: Tier) -> Self {
48 self.fields.insert(field.into(), tier);
49 self
50 }
51
52 /// The tier `field` is served at — its annotation, or the default.
53 pub fn tier_of(&self, field: &str) -> Tier {
54 self.fields.get(field).copied().unwrap_or(self.default)
55 }
56
57 /// The default tier.
58 pub fn default_tier(&self) -> Tier {
59 self.default
60 }
61}