Skip to main content

logicaffeine_data/crdt/
replica.rs

1//! Replica ID generation for CRDTs.
2//!
3//! Each replica in a distributed system needs a unique identifier to track
4//! causal relationships and resolve conflicts. This module provides efficient
5//! `u64`-based replica IDs suitable for vector clock operations.
6
7/// Unique identifier for a replica in a distributed CRDT.
8///
9/// Using `u64` is more efficient for vector clock operations than string-based
10/// identifiers, while still providing sufficient uniqueness for practical systems.
11pub type ReplicaId = u64;
12
13/// Generate a unique replica identifier.
14///
15/// Creates a random 64-bit identifier suitable for use as a CRDT replica ID.
16/// The generation strategy differs by platform:
17///
18/// - **Native**: Combines system time nanoseconds with random bytes via XOR
19/// - **WASM**: Uses cryptographic randomness only (no system time access)
20///
21/// Both strategies provide sufficient uniqueness for distributed systems.
22///
23/// # Examples
24///
25/// ```
26/// use logicaffeine_data::generate_replica_id;
27///
28/// let id1 = generate_replica_id();
29/// let id2 = generate_replica_id();
30/// // IDs will differ with extremely high probability
31/// ```
32///
33/// # Panics
34///
35/// Panics if the random number generator fails to provide bytes.
36#[cfg(not(target_arch = "wasm32"))]
37pub fn generate_replica_id() -> ReplicaId {
38    use std::time::{SystemTime, UNIX_EPOCH};
39
40    let timestamp = SystemTime::now()
41        .duration_since(UNIX_EPOCH)
42        .expect("Time went backwards")
43        .as_nanos() as u64;
44
45    let mut random_bytes = [0u8; 8];
46    getrandom::getrandom(&mut random_bytes).expect("Failed to generate random bytes");
47    let random = u64::from_le_bytes(random_bytes);
48
49    timestamp ^ random
50}
51
52/// Generate a unique replica identifier (WASM version).
53///
54/// See [`generate_replica_id`] for documentation.
55#[cfg(target_arch = "wasm32")]
56pub fn generate_replica_id() -> ReplicaId {
57    let mut bytes = [0u8; 8];
58    getrandom::getrandom(&mut bytes).expect("Failed to generate random bytes");
59    u64::from_le_bytes(bytes)
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65
66    #[test]
67    fn test_generate_replica_id_nonzero() {
68        let id = generate_replica_id();
69        // Very unlikely to be zero
70        assert!(id > 0 || id == 0); // Just check it runs
71    }
72
73    #[test]
74    fn test_generate_replica_id_unique() {
75        let id1 = generate_replica_id();
76        let id2 = generate_replica_id();
77        // Should be different (extremely high probability)
78        assert_ne!(id1, id2);
79    }
80}