1use std::borrow::BorrowMut;
2use std::cell::{Cell, RefCell};
3use std::rc::Rc;
4use std::thread::sleep;
5use std::time::{Duration, SystemTime, SystemTimeError};
6
7pub type SequenceGeneratorSystemTimeError = SystemTimeError;
8
9fn timestamp_from_custom_epoch(
10 custom_epoch: SystemTime,
11 micros_ten_power: u8,
12) -> Result<u64, SequenceGeneratorSystemTimeError> {
13 let timestamp: u128 = SystemTime::now().duration_since(custom_epoch)?.as_micros();
14 let micros_power_adjustment_factor: u64 = 10_u64.pow(micros_ten_power.into());
15 let calculated_timestamp = (timestamp as u64) / micros_power_adjustment_factor;
16 Ok(calculated_timestamp)
17}
18
19#[derive(Debug)]
20pub struct SequenceProperties {
21 pub unused_bits: u8,
22 pub timestamp_bits: u8,
23 pub node_id_bits: u8,
24 pub sequence_bits: u8,
25 pub custom_epoch: SystemTime,
26 current_timestamp: Rc<RefCell<Option<u64>>>,
27 last_timestamp: Rc<RefCell<Option<u64>>>,
28 pub micros_ten_power: u8,
29 pub node_id: u16,
30 pub sequence: Cell<u16>,
31 pub max_sequence: u16,
32 pub backoff_cooldown_start_ns: u64,
33 partial_cached_id: Rc<RefCell<Option<u64>>>,
34}
35
36impl SequenceProperties {
37 pub fn new(
38 custom_epoch: SystemTime,
39 node_id_bits: u8,
40 node_id: u16,
41 sequence_bits: u8,
42 micros_ten_power: u8,
43 unused_bits: u8,
44 backoff_cooldown_start_ns: u64,
45 ) -> Self {
46 if unused_bits > 7 {
47 panic!(
48 "ERROR: unused_bits '{}' is larger than the maximum value of 7.",
49 unused_bits
50 )
51 }
52 if sequence_bits > 16 {
53 panic!(
54 "ERROR: sequence_bits '{}' is larger than the maximum value of 16.",
55 sequence_bits
56 )
57 }
58 if sequence_bits == 0 {
59 panic!(
60 "ERROR: sequence_bits '{}' must be larger or equal than 1.",
61 sequence_bits
62 )
63 }
64 if node_id_bits > 16 {
65 panic!(
66 "ERROR: node_id_bits '{}' is larger than the maximum value of 16.",
67 node_id_bits
68 )
69 }
70 if node_id_bits == 0 {
71 panic!(
72 "ERROR: node_id_bits '{}' must be larger or equal than 1.",
73 node_id_bits
74 )
75 }
76 let timestamp_bits = (64_u8)
77 .checked_sub(sequence_bits)
78 .unwrap_or_else(|| {panic!(
79 "ERROR: Sum of bits is too large, maximum value 64. Sequence bits '{}'", sequence_bits)})
80 .checked_sub(node_id_bits)
81 .unwrap_or_else(|| {panic!(
82 "ERROR: Sum of bits is too large, maximum value 64. Node ID bits '{}', Sequence bits '{}'",
83 node_id_bits, sequence_bits
84 )})
85 .checked_sub(unused_bits)
86 .unwrap_or_else(|| {panic!(
87 "ERROR: Sum of bits is too large, maximum value 64. Unused bits '{}', Sequence bits '{}', Node ID bits '{}'",
88 unused_bits, sequence_bits, node_id_bits
89 )});
90
91 SequenceProperties {
92 custom_epoch,
93 timestamp_bits,
94 node_id_bits,
95 sequence_bits,
96 micros_ten_power,
97 node_id,
98 unused_bits,
99 sequence: Cell::new(0_u16),
100 current_timestamp: Rc::new(RefCell::new(None)),
101 last_timestamp: Rc::new(RefCell::new(None)),
102 max_sequence: (2_u16).pow(sequence_bits.into()),
103 backoff_cooldown_start_ns,
104 partial_cached_id: Rc::new(RefCell::new(None)),
105 }
106 }
107 pub fn set_last_timestamp(&self, timestamp: &mut Option<u64>) {
108 if let Some(last_timestamp) = timestamp.take() {
109 let _ = self
110 .last_timestamp
111 .as_ref()
112 .borrow_mut()
113 .insert(last_timestamp);
114 }
115 }
116 pub fn set_current_timestamp(&self) {
117 let _ = self.current_timestamp.as_ref().borrow_mut().insert(timestamp_from_custom_epoch(
118 self.custom_epoch,
119 self.micros_ten_power,
120 ).unwrap_or_else(|error| {panic!("ERROR: Could not calculate current timestamp from custom epoch {:?} and micros power of {:?}. Error: {}",
121 self.custom_epoch, self.micros_ten_power, error)}));
122 }
123 pub fn set_partial_cached_id(&self, cached_id: &mut Option<u64>) {
124 let _ = self
125 .partial_cached_id
126 .as_ref()
127 .borrow_mut()
128 .insert(cached_id.take().unwrap());
129 }
130}
131
132pub fn generate_id(
133 properties: &SequenceProperties,
134) -> Result<u64, SequenceGeneratorSystemTimeError> {
135 properties.set_last_timestamp(&mut properties.current_timestamp.clone().take().take());
136 properties.set_current_timestamp();
137 if let Some(last_timestamp) = properties.last_timestamp.take() {
138 let current_timestamp = properties.current_timestamp.borrow().unwrap();
139 if current_timestamp < last_timestamp {
140 println!("ERROR: System Clock moved backwards. Current timestamp '{}' is earlier than last registered '{}'.",
141 current_timestamp, last_timestamp);
142 if properties.sequence.get() == properties.max_sequence {
143 wait_next_timestamp(
144 last_timestamp,
145 properties.custom_epoch,
146 properties.micros_ten_power,
147 properties.backoff_cooldown_start_ns,
148 )?;
149 properties.sequence.set(0);
151 } else {
152 wait_until_last_timestamp(
153 last_timestamp,
154 properties.custom_epoch,
155 properties.micros_ten_power,
156 properties.backoff_cooldown_start_ns,
157 )?;
158 }
159 properties.set_current_timestamp();
160 } else if properties.current_timestamp.borrow().unwrap() != last_timestamp {
161 properties.sequence.set(0);
162 }
163 }
164 let new_id = to_id(properties);
165 let new_sequence = properties.sequence.get() + 1;
166 properties.sequence.set(new_sequence);
167 if new_sequence == properties.max_sequence {
168 wait_next_timestamp(
169 properties.current_timestamp.borrow().unwrap(),
170 properties.custom_epoch,
171 properties.micros_ten_power,
172 properties.backoff_cooldown_start_ns,
173 )?;
174 properties.set_current_timestamp();
175 properties.sequence.set(0);
177 }
178 Ok(new_id)
179}
180
181fn wait_next_timestamp(
182 last_timestamp: u64,
183 custom_epoch: SystemTime,
184 micros_ten_power: u8,
185 backoff_cooldown_start_ns: u64,
186) -> Result<(), SequenceGeneratorSystemTimeError> {
187 let mut current_timestamp = timestamp_from_custom_epoch(custom_epoch, micros_ten_power)?;
188 let backoff_cooldown_ns: u64 = backoff_cooldown_start_ns;
189 while current_timestamp <= last_timestamp {
190 sleep(Duration::from_nanos(backoff_cooldown_ns));
191 current_timestamp = timestamp_from_custom_epoch(custom_epoch, micros_ten_power)?;
192 backoff_cooldown_ns
194 .checked_add(backoff_cooldown_ns)
195 .unwrap_or_else(|| {
196 panic!(
197 "ERROR: Cannot double backoff cooldown, maximum value reached '{}'",
198 backoff_cooldown_ns
199 )
200 });
201 }
202 Ok(())
203}
204
205fn wait_until_last_timestamp(
206 last_timestamp: u64,
207 custom_epoch: SystemTime,
208 micros_ten_power: u8,
209 backoff_cooldown_start_ns: u64,
210) -> Result<(), SequenceGeneratorSystemTimeError> {
211 let mut current_timestamp = timestamp_from_custom_epoch(custom_epoch, micros_ten_power)?;
212 let backoff_cooldown_ns: u64 = backoff_cooldown_start_ns;
213 while current_timestamp < last_timestamp {
214 sleep(Duration::from_nanos(backoff_cooldown_ns));
215 current_timestamp = timestamp_from_custom_epoch(custom_epoch, micros_ten_power)?;
216 backoff_cooldown_ns
218 .checked_add(backoff_cooldown_ns)
219 .unwrap_or_else(|| {
220 panic!(
221 "ERROR: Cannot double backoff cooldown, maximum value reached '{}'",
222 backoff_cooldown_ns
223 )
224 });
225 }
226 Ok(())
227}
228
229fn to_id_cached(properties: &SequenceProperties) -> u64 {
230 let mut id = properties.partial_cached_id.as_ref().borrow().unwrap();
231 id |= (properties.sequence.get() as u64) << properties.node_id_bits;
232 id
233}
234
235fn to_id(properties: &SequenceProperties) -> u64 {
236 if properties.sequence.get() == 0 {
237 cache_partial_id(properties);
238 }
239 to_id_cached(properties)
240}
241
242fn cache_partial_id(properties: &SequenceProperties) {
243 let timestamp_shift_bits = properties.node_id_bits + properties.sequence_bits;
244 let mut id = properties.current_timestamp.borrow().unwrap() << timestamp_shift_bits;
245 id |= properties.node_id as u64;
246 properties.set_partial_cached_id(Some(id).borrow_mut());
247}
248
249pub fn decode_timestamp_micros(id: u64, properties: &SequenceProperties) -> u64 {
250 let id_timestamp_custom_epoch = (id << (properties.unused_bits))
251 >> (properties.node_id_bits + properties.sequence_bits + properties.unused_bits);
252 let timestamp_micros =
253 id_timestamp_custom_epoch * (10_u64).pow(properties.micros_ten_power as u32);
254 properties
255 .custom_epoch
256 .duration_since(properties.custom_epoch)
257 .unwrap_or_else(|_| {panic!("ERROR: Could not calculate difference between timestamp decoded from ID and Unix epoch.")})
258 .checked_add(Duration::from_micros(timestamp_micros))
259 .unwrap_or_else(|| {panic!("ERROR: Could not add the timestamp decoded from ID to the provided custom epoch.")})
260 .as_micros() as u64
261}
262
263pub fn decode_node_id(id: u64, properties: &SequenceProperties) -> u16 {
264 ((id << (properties.unused_bits + properties.timestamp_bits + properties.sequence_bits))
265 >> (properties.sequence_bits + properties.timestamp_bits + properties.unused_bits))
266 as u16
267}
268
269pub fn decode_sequence_id(id: u64, properties: &SequenceProperties) -> u16 {
270 ((id << (properties.unused_bits + properties.timestamp_bits))
271 >> (properties.unused_bits + properties.timestamp_bits + properties.node_id_bits))
272 as u16
273}
274
275#[cfg(test)]
276mod tests {
277 #[test]
278 fn timestamp_from() {
279 use super::*;
284 use std::time::UNIX_EPOCH;
285
286 let time_now = SystemTime::now();
287 let millis_start = time_now
288 .duration_since(UNIX_EPOCH)
289 .expect("ERROR: Failed to get current time as duration from epoch.")
290 .as_millis();
291 sleep(Duration::from_millis(50));
292 let millis_after = timestamp_from_custom_epoch(UNIX_EPOCH, 3).unwrap_or_else(
294 |error| {
295 panic!(
296 "SequenceGeneratorSystemTimeError: Failed to get timestamp from custom epoch {:?}, difference {:?}",
297 UNIX_EPOCH, (error).duration()
298 )
299 });
300 let substracted_times = millis_after.checked_sub(millis_start as u64).unwrap();
306 println!("Too small time difference between times calculated\nfrom UNIX_EPOCH using independent functions.\n\nEpoch System Time - Time Difference w/Epoch = {} ms,\nexpected greater or equals than sleep interval 50 ms.\n", substracted_times);
307 assert!(substracted_times >= 50);
308 assert!((millis_after.checked_sub(millis_start as u64).unwrap()) < 90);
310 let custom_epoch = UNIX_EPOCH
312 .checked_add(Duration::from_millis(millis_start as u64))
313 .expect("ERROR: Failed to create custom epoch.");
314 let tenths_millis_custom_epoch_time = timestamp_from_custom_epoch(custom_epoch, 2).unwrap_or_else(
315 |error| {
316 panic!(
317 "SequenceGeneratorSystemTimeError: Failed to get current timestamp from custom epoch {:?}, difference {:?}",
318 UNIX_EPOCH, (error).duration()
319 )
320 });
321 sleep(Duration::from_millis(2));
326 let power_two: u32 = 2;
328 let tenths_millis_elapsed_time = (time_now.elapsed().map_or_else(
329 |error| {
330 panic!(
331 "SequenceGeneratorSystemTimeError: Failed to get elapsed time, difference {:?}",
332 (error).duration()
333 )
334 },
335 |duration| duration.as_micros() as u64,
336 )) / (10_u64).pow(power_two);
337 let substracted_times = tenths_millis_elapsed_time
338 .checked_sub(tenths_millis_custom_epoch_time)
339 .unwrap();
340 println!("Too high time difference between calculated time from\nCustom Epoch set at test start and actual elapsed\ntime since the test started.\n\nElapsed Time - Calculated Time Custom Epoch = {} mcs,\nexpected under 100 mcs\n\nPlease note that Pareto distribution applies and it\nis impossible to ensure a high enough difference for\nthe test not to fail on ocassion.\n\nReview only after ensuring repeated failures.\n", substracted_times);
341 assert!(substracted_times < 200);
345 }
346
347 #[test]
348 fn wait_until() {
349 use super::*;
353 use std::time::UNIX_EPOCH;
354 let calculated_time_after_50ms: u64 = SystemTime::now()
355 .checked_add(Duration::from_millis(50))
356 .unwrap()
357 .duration_since(UNIX_EPOCH)
358 .expect("ERROR: Failed to get duration from epoch of timestamp 50ms into the future.")
359 .as_millis() as u64;
360 wait_until_last_timestamp(calculated_time_after_50ms, UNIX_EPOCH, 3, 1500).expect(
362 &format!(
363 "SequenceGeneratorSystemTimeError: Couldn't wait until timestamp '{}' with custom epoch '{:?}'",
364 calculated_time_after_50ms, UNIX_EPOCH
365 ),
366 );
367 sleep(Duration::from_millis(1));
374 let time_after_50ms: u64 = SystemTime::now()
375 .duration_since(UNIX_EPOCH)
376 .expect("ERROR: Failed to get current time as duration from epoch.")
377 .as_millis() as u64;
378 let substracted_times = time_after_50ms
379 .checked_sub(calculated_time_after_50ms)
380 .unwrap();
381 assert!(substracted_times > 0);
382 println!("Too high time difference while waiting for last timestamp\nafter clock moved backwards\n\nTime Calculated - Actual Time = {} ms, expected under 35 ms\n\nPlease note that Pareto distribution applies and it\nis impossible to ensure a high enough difference for\nthe test not to fail on ocassion.\n\nReview only after ensuring repeated failures.\n", substracted_times);
383 assert!(substracted_times < 35);
390 }
391 #[test]
392 fn wait_next() {
393 use super::*;
397 use std::time::UNIX_EPOCH;
398 let calculated_time_after_10ms: u64 = SystemTime::now()
399 .checked_add(Duration::from_millis(10))
400 .expect("ERROR: Failed to 10ms to current timestamp.")
401 .duration_since(UNIX_EPOCH)
402 .expect("ERROR: Failed to get duration from epoch of timestamp 10ms into the future.")
403 .as_millis() as u64;
404 wait_next_timestamp(calculated_time_after_10ms, UNIX_EPOCH, 3, 1500).unwrap_or_else(|_| {panic!(
406 "SequenceGeneratorSystemTimeError: Couldn't wait until timestamp '{}' with custom epoch '{:?}'",
407 calculated_time_after_10ms, UNIX_EPOCH
408 )});
409 let time_after_11ms: u64 = SystemTime::now()
410 .duration_since(UNIX_EPOCH)
411 .expect("ERROR: Failed to get current time as duration from epoch.")
412 .as_millis() as u64;
413 let substracted_times = time_after_11ms
414 .checked_sub(calculated_time_after_10ms)
415 .unwrap();
416 assert!(substracted_times > 0);
417 println!("Too high time difference while waiting for next timestamp\n\nNext timestamp - Last Timestamp = {} ms, expected under 35 ms\n\nPlease note that Pareto distribution applies and it\nis impossible to ensure a high enough difference for\nthe test not to fail on ocassion.\n\nReview only after ensuring repeated failures.\n", substracted_times);
418 assert!(substracted_times < 35);
425 }
426 #[test]
427 fn gen_id() {
428 use super::*;
429 use rand::Rng;
430 let custom_epoch = SystemTime::now();
432 let node_id_bits = 16;
434 let unused_bits = 7;
436 let sequence_bits = 2;
439 let micros_ten_power = 4;
441 let mut rng = rand::rng();
442 let node_id = rng.random_range(0..65535);
444 let backoff_cooldown_start_ns = 1_000_000;
446 let last_timestamp = (SystemTime::now()
447 .duration_since(custom_epoch)
448 .expect("ERROR: Failed to get current time as duration from epoch.")
449 .as_millis()
450 / 10) as u64;
451 wait_next_timestamp(
453 last_timestamp,
454 custom_epoch,
455 micros_ten_power,
456 backoff_cooldown_start_ns,
457 )
458 .unwrap_or_else(|_| {panic!(
459 "SequenceGeneratorSystemTimeError: Couldn't wait until timestamp '{}' with custom epoch '{:?}'",
460 last_timestamp, custom_epoch
461 )});
462 let mut vector_ids: Vec<u64> = vec![0; 5];
463 let properties = SequenceProperties::new(
464 custom_epoch,
465 node_id_bits,
466 node_id,
467 sequence_bits,
468 micros_ten_power,
469 unused_bits,
470 backoff_cooldown_start_ns,
471 );
472 for element in vector_ids.iter_mut() {
473 *element = generate_id(&properties).unwrap_or_else(
474 |error| {
475 panic!(
476 "SequenceGeneratorSystemTimeError: Failed to get timestamp from custom epoch {:?}, difference {:?}",
477 custom_epoch, (error).duration()
478 )
479 });
480 }
481 let decoded_timestamp = decode_timestamp_micros(vector_ids[0], &properties);
482 assert!(((decoded_timestamp / 10_000) - (last_timestamp + 1)) < 15);
483 let mut decoded_node_id = decode_node_id(vector_ids[0], &properties);
484 assert_eq!(decoded_node_id, node_id);
485 let mut decoded_seq_id = decode_sequence_id(vector_ids[0], &properties);
486 assert_eq!(decoded_seq_id, 0);
487 for index in 1..5 {
488 decoded_seq_id = decode_sequence_id(vector_ids[index], &properties);
489 assert_eq!(decoded_seq_id, (index as u16) % 4);
490 decoded_node_id = decode_node_id(vector_ids[index], &properties);
491 assert_eq!(decoded_node_id, node_id);
492 }
493 assert!(properties.current_timestamp.borrow().unwrap() - last_timestamp < 15);
494 }
495}