1use std::sync::atomic::{AtomicU64, Ordering};
2use std::sync::{Mutex, OnceLock};
3use std::time::{Duration, SystemTime, UNIX_EPOCH};
4
5use crate::RuntimeError;
6
7pub trait InternalIdGenerator: Send + Sync {
8 fn generate_id(&self, entity: &str) -> Result<u64, RuntimeError>;
9
10 fn ensure_floor(&self, entity: &str, floor: u64) -> Result<(), RuntimeError> {
11 Err(RuntimeError::IdGeneration(format!(
12 "ID generator cannot reserve fixed bootstrap ID floor {floor} for {entity}"
13 )))
14 }
15}
16
17pub fn canonical_id_space_entity(entity: &str) -> String {
20 let mut result = String::with_capacity(entity.len() + 4);
21 for (index, character) in entity.chars().enumerate() {
22 if character.is_ascii_uppercase() {
23 if index > 0 {
24 result.push('_');
25 }
26 result.push(character.to_ascii_lowercase());
27 } else {
28 result.push(character);
29 }
30 }
31 result
32}
33
34#[derive(Debug)]
44pub struct AtomicCounterIdGenerator {
45 counter: AtomicU64,
46}
47
48impl Default for AtomicCounterIdGenerator {
49 fn default() -> Self {
50 Self::new(1000)
51 }
52}
53
54impl AtomicCounterIdGenerator {
55 pub fn new(start: u64) -> Self {
58 Self {
59 counter: AtomicU64::new(start),
60 }
61 }
62}
63
64impl InternalIdGenerator for AtomicCounterIdGenerator {
65 fn generate_id(&self, _entity: &str) -> Result<u64, RuntimeError> {
66 Ok(self.counter.fetch_add(1, Ordering::Relaxed) + 1)
67 }
68
69 fn ensure_floor(&self, _entity: &str, floor: u64) -> Result<(), RuntimeError> {
70 self.counter.fetch_max(floor, Ordering::Relaxed);
71 Ok(())
72 }
73}
74
75#[derive(Debug)]
80pub struct SnowflakeIdGenerator {
81 epoch_millis: u64,
82 worker_id: u64,
83 datacenter_id: u64,
84 state: Mutex<SnowflakeState>,
85}
86
87#[derive(Debug, Default)]
88struct SnowflakeState {
89 last_timestamp: u64,
90 sequence: u64,
91}
92
93impl Default for SnowflakeIdGenerator {
94 fn default() -> Self {
95 Self::new(0, 0)
96 }
97}
98
99impl SnowflakeIdGenerator {
100 const DEFAULT_EPOCH_MILLIS: u64 = 1_288_834_974_657;
101 const WORKER_ID_BITS: u64 = 5;
102 const DATACENTER_ID_BITS: u64 = 5;
103 const SEQUENCE_BITS: u64 = 12;
104 const MAX_WORKER_ID: u64 = (1 << Self::WORKER_ID_BITS) - 1;
105 const MAX_DATACENTER_ID: u64 = (1 << Self::DATACENTER_ID_BITS) - 1;
106 const SEQUENCE_MASK: u64 = (1 << Self::SEQUENCE_BITS) - 1;
107 const WORKER_ID_SHIFT: u64 = Self::SEQUENCE_BITS;
108 const DATACENTER_ID_SHIFT: u64 = Self::SEQUENCE_BITS + Self::WORKER_ID_BITS;
109 const TIMESTAMP_SHIFT: u64 =
110 Self::SEQUENCE_BITS + Self::WORKER_ID_BITS + Self::DATACENTER_ID_BITS;
111
112 pub fn new(worker_id: u64, datacenter_id: u64) -> Self {
113 assert!(worker_id <= Self::MAX_WORKER_ID, "worker id out of range");
114 assert!(
115 datacenter_id <= Self::MAX_DATACENTER_ID,
116 "datacenter id out of range"
117 );
118
119 Self {
120 epoch_millis: Self::DEFAULT_EPOCH_MILLIS,
121 worker_id,
122 datacenter_id,
123 state: Mutex::new(SnowflakeState::default()),
124 }
125 }
126
127 fn current_millis() -> Result<u64, RuntimeError> {
128 let now = SystemTime::now()
129 .duration_since(UNIX_EPOCH)
130 .map_err(|err| RuntimeError::IdGeneration(err.to_string()))?;
131 Ok(now.as_millis() as u64)
132 }
133
134 fn wait_until_next_millis(last_timestamp: u64) -> Result<u64, RuntimeError> {
135 loop {
136 let timestamp = Self::current_millis()?;
137 if timestamp > last_timestamp {
138 return Ok(timestamp);
139 }
140 std::thread::sleep(Duration::from_millis(1));
141 }
142 }
143}
144
145impl InternalIdGenerator for SnowflakeIdGenerator {
146 fn generate_id(&self, _entity: &str) -> Result<u64, RuntimeError> {
147 let mut state = self
148 .state
149 .lock()
150 .map_err(|_| RuntimeError::IdGeneration("snowflake state poisoned".to_owned()))?;
151 let mut timestamp = Self::current_millis()?;
152
153 if timestamp < state.last_timestamp {
154 timestamp = Self::wait_until_next_millis(state.last_timestamp)?;
155 }
156
157 match timestamp == state.last_timestamp {
158 true => {
159 state.sequence = (state.sequence + 1) & Self::SEQUENCE_MASK;
160 if state.sequence == 0 {
161 timestamp = Self::wait_until_next_millis(state.last_timestamp)?;
162 }
163 }
164 false => state.sequence = 0,
165 }
166
167 state.last_timestamp = timestamp;
168
169 let relative_timestamp = timestamp.checked_sub(self.epoch_millis).ok_or_else(|| {
170 RuntimeError::IdGeneration("system clock is before snowflake epoch".to_owned())
171 })?;
172
173 Ok((relative_timestamp << Self::TIMESTAMP_SHIFT)
174 | (self.datacenter_id << Self::DATACENTER_ID_SHIFT)
175 | (self.worker_id << Self::WORKER_ID_SHIFT)
176 | state.sequence)
177 }
178
179 fn ensure_floor(&self, _entity: &str, _floor: u64) -> Result<(), RuntimeError> {
180 Ok(())
181 }
182}
183
184pub(crate) fn local_id_generator() -> &'static AtomicCounterIdGenerator {
185 static LOCAL_ID_GENERATOR: OnceLock<AtomicCounterIdGenerator> = OnceLock::new();
186 LOCAL_ID_GENERATOR.get_or_init(AtomicCounterIdGenerator::default)
187}