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}