Skip to main content

snowflake_gen/generator/
generation.rs

1use crate::error::SnowflakeError;
2use crate::utils::{get_time_millis, biding_time_conditions};
3use crate::generator::SnowflakeIdGenerator;
4
5impl SnowflakeIdGenerator {
6    /// Generates the next ID, **blocking** until the next millisecond if the
7    /// sequence counter wraps.
8    ///
9    /// ```rust
10    /// use snowflake_gen::SnowflakeIdGenerator;
11    ///
12    /// let mut idgen = SnowflakeIdGenerator::new(1, 1).unwrap();
13    /// let id = idgen.generate().unwrap();
14    /// assert!(id > 0);
15    /// ```
16    #[must_use = "generated ID is discarded; this advances the sequence counter for nothing"]
17    pub fn generate(&mut self) -> Result<i64, SnowflakeError> {
18        if self.idx > self.layout.max_sequence() {
19            let mut now = get_time_millis(self.epoch)?;
20            if now < self.last_time_millis {
21                return Err(SnowflakeError::ClockMovedBackwards);
22            }
23            if now == self.last_time_millis {
24                now = biding_time_conditions(self.last_time_millis, self.epoch)?;
25            }
26            self.last_time_millis = now;
27            self.idx = 0;
28        }
29
30        let id = self.assemble(self.last_time_millis);
31        self.idx += 1;
32        Ok(id)
33    }
34
35    /// Generates the next ID, **always** reading the current clock.
36    #[must_use = "generated ID is discarded; this advances the sequence counter for nothing"]
37    pub fn real_time_generate(&mut self) -> Result<i64, SnowflakeError> {
38        let now = get_time_millis(self.epoch)?;
39
40        if now < self.last_time_millis {
41            return Err(SnowflakeError::ClockMovedBackwards);
42        }
43
44        if now != self.last_time_millis {
45            self.last_time_millis = now;
46            self.idx = 0;
47        } else if self.idx > self.layout.max_sequence() {
48            let advanced = biding_time_conditions(self.last_time_millis, self.epoch)?;
49            self.last_time_millis = advanced;
50            self.idx = 0;
51        }
52
53        let id = self.assemble(self.last_time_millis);
54        self.idx += 1;
55        Ok(id)
56    }
57
58    /// Generates the next ID without reading the system clock.
59    ///
60    /// Instead of a syscall, `lazy_generate` synthetically increments
61    /// `last_time_millis` whenever the sequence counter wraps.  This makes it
62    /// the fastest generation mode but comes with an important constraint:
63    ///
64    /// **Do not mix `lazy_generate` with [`generate`](Self::generate) or
65    /// [`real_time_generate`](Self::real_time_generate) on the same generator
66    /// instance.**  Because `lazy_generate` can advance `last_time_millis`
67    /// ahead of the real clock, a subsequent clock-based call may produce a
68    /// timestamp that `lazy_generate` already used, resulting in **duplicate
69    /// IDs**.
70    ///
71    /// `SnowflakeIdBucket` uses `lazy_generate` internally on a dedicated
72    /// generator that is never exposed for clock-based calls, so it is safe.
73    #[must_use = "generated ID is discarded; this advances the sequence counter for nothing"]
74    pub fn lazy_generate(&mut self) -> i64 {
75        if self.idx > self.layout.max_sequence() {
76            self.last_time_millis += 1;
77            self.idx = 0;
78        }
79
80        let id = self.assemble(self.last_time_millis);
81        self.idx += 1;
82        id
83    }
84
85    #[inline]
86    pub(crate) fn assemble(&self, timestamp_millis: i64) -> i64 {
87        let layout = &self.layout;
88        (timestamp_millis << layout.timestamp_shift)
89            | (self.machine_id << layout.machine_id_shift)
90            | (self.node_id << layout.node_id_shift)
91            | (self.idx as i64)
92    }
93}