Skip to main content

sapphire_framework_sync/
id.rs

1//! Replica identity.
2
3use grain_id::GrainId;
4use serde::{Deserialize, Serialize};
5use uuid::Uuid;
6
7/// Identity of one replica store, created once (UUIDv7). A reinstall gets a new one,
8/// so dots are never reused.
9#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
10pub struct ReplicaId(pub Uuid);
11
12impl ReplicaId {
13    /// A fresh time-ordered id.
14    pub fn new() -> Self {
15        Self(Uuid::now_v7())
16    }
17
18    /// The grain-id shown in file names. A UUIDv7 starts with a timestamp, so the
19    /// trailing random bytes are used.
20    pub fn display_id(&self) -> GrainId {
21        let bytes = self.0.as_bytes();
22        let tail: &[u8; 5] = bytes[11..16].try_into().expect("a UUID has 16 bytes");
23        GrainId::from_byte_suffix(tail)
24    }
25}
26
27impl Default for ReplicaId {
28    fn default() -> Self {
29        Self::new()
30    }
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36
37    #[test]
38    fn display_id_uses_the_uuid_suffix() {
39        let max = ReplicaId(Uuid::from_u128(0x0190_0000_0000_7000_8000_00ff_ffff_ffff));
40        let nil = ReplicaId(Uuid::from_u128(0x0190_0000_0000_7000_8000_0000_0000_0000));
41        assert_eq!(max.display_id(), GrainId::MAX);
42        assert_eq!(nil.display_id(), GrainId::NIL);
43    }
44
45    #[test]
46    fn ids_created_together_rarely_share_a_display_id() {
47        let ids: std::collections::HashSet<_> =
48            (0..1000).map(|_| ReplicaId::new().display_id()).collect();
49        assert!(ids.len() > 990);
50    }
51}