Skip to main content

nodedb_types/
surrogate.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Surrogate — a global, stable, monotonically-allocated u32 row identity.
4//!
5//! Every logical row in every engine carries a surrogate. The surrogate is
6//! allocated from a WAL-durable, Raft-replicated monotonic counter at insert
7//! time and never reused. Cross-engine prefilter and join therefore reduce
8//! to roaring-bitmap intersections — no per-query translation between
9//! engine-local internal IDs.
10//!
11//! This file defines only the value type and its derives. Allocation,
12//! persistence, and bootstrap live in `nodedb::control::surrogate`.
13
14use std::fmt;
15
16use serde::{Deserialize, Serialize};
17
18/// Global surrogate identifier.
19///
20/// A `u32` newtype carrying the same comparison + hashing semantics as the
21/// underlying integer. The `Display` impl renders as `sur:N` for diagnostics
22/// (mirrors the `Lsn` convention).
23#[derive(
24    Debug,
25    Clone,
26    Copy,
27    PartialEq,
28    Eq,
29    PartialOrd,
30    Ord,
31    Hash,
32    Serialize,
33    Deserialize,
34    zerompk::ToMessagePack,
35    zerompk::FromMessagePack,
36)]
37pub struct Surrogate(pub u32);
38
39impl Surrogate {
40    /// The zero surrogate. Reserved as a sentinel — real allocation starts at 1.
41    pub const ZERO: Surrogate = Surrogate(0);
42
43    /// Construct a surrogate from a raw `u32`.
44    pub const fn new(value: u32) -> Self {
45        Self(value)
46    }
47
48    /// Raw `u32` value.
49    pub const fn as_u32(self) -> u32 {
50        self.0
51    }
52}
53
54impl fmt::Display for Surrogate {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        write!(f, "sur:{}", self.0)
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn ordering_matches_u32() {
66        let a = Surrogate::new(1);
67        let b = Surrogate::new(2);
68        assert!(a < b);
69        assert_eq!(a.as_u32(), 1);
70    }
71
72    #[test]
73    fn display_renders_sur_prefix() {
74        assert_eq!(Surrogate::new(42).to_string(), "sur:42");
75    }
76
77    #[test]
78    fn msgpack_roundtrip() {
79        let s = Surrogate::new(0xDEAD_BEEF);
80        let bytes = zerompk::to_msgpack_vec(&s).unwrap();
81        let back: Surrogate = zerompk::from_msgpack(&bytes).unwrap();
82        assert_eq!(s, back);
83    }
84
85    #[test]
86    fn zero_sentinel() {
87        assert_eq!(Surrogate::ZERO.as_u32(), 0);
88    }
89}