Skip to main content

Snowflake

Struct Snowflake 

Source
pub struct Snowflake { /* private fields */ }
Available on crate feature snowflake only.
Expand description

Snowflake ID generator.

Holds the worker ID, the epoch, and the packed (last_ms, next_seq) state used to mint monotonic IDs lock-free.

§Example

use id_forge::snowflake::Snowflake;

let gen = Snowflake::new(7);
let id = gen.next_id();
assert_eq!(Snowflake::parts(id).1, 7);

Implementations§

Source§

impl Snowflake

Source

pub const fn new(worker_id: u16) -> Self

Build a new generator with the given worker ID (0-1023) and the default 2026-01-01 epoch.

Worker IDs above 1023 are silently clamped to 10 bits.

§Example
use id_forge::snowflake::Snowflake;

let gen = Snowflake::new(42);
assert_eq!(gen.worker_id(), 42);
Examples found in repository?
examples/bench.rs (line 42)
33fn main() {
34    println!("id-forge throughput (single thread, release build)");
35    println!("---------------------------------------------------");
36
37    let iters = 1_000_000;
38    bench("Uuid::v4", iters, Uuid::v4);
39    bench("Uuid::v7", iters, Uuid::v7);
40    bench("Ulid::new", iters, Ulid::new);
41
42    let sf = Snowflake::new(1);
43    bench("Snowflake::next_id", iters, || sf.next_id());
44
45    bench("nanoid::generate", iters / 5, nanoid::generate);
46    bench("nanoid::with_length(8)", iters / 5, || {
47        nanoid::with_length(8)
48    });
49    bench("nanoid::custom(16, hex)", iters / 5, || {
50        nanoid::custom(16, b"0123456789abcdef")
51    });
52    bench("nanoid::custom(21, 17-char)", iters / 5, || {
53        nanoid::custom(21, b"ABCDEFGHIJKLMNOPQ")
54    });
55}
More examples
Hide additional examples
examples/basic.rs (line 18)
7fn main() {
8    let v4 = Uuid::v4();
9    println!("UUID v4:    {v4} (version={})", v4.version());
10    println!("UUID v7:    {}", Uuid::v7());
11    println!("UUID nil:   {}", Uuid::nil());
12
13    let a = Ulid::new();
14    let b = Ulid::new();
15    println!("ULID a:     {a}");
16    println!("ULID b:     {b} (monotonic: {})", b > a);
17
18    let gen = Snowflake::new(1);
19    let sf = gen.next_id();
20    let (ts_offset, worker, seq) = Snowflake::parts(sf);
21    println!(
22        "Snowflake:  {sf}  (ts+epoch={}, worker={worker}, seq={seq})",
23        ts_offset + gen.epoch_ms()
24    );
25
26    println!("NanoID 21:  {}", nanoid::generate());
27    println!("NanoID 8:   {}", nanoid::with_length(8));
28
29    assert_eq!(v4, Uuid::parse_str(&v4.to_string()).unwrap());
30    assert_eq!(a, Ulid::parse_str(&a.to_string()).unwrap());
31}
Source

pub const fn with_epoch(worker_id: u16, epoch_ms: u64) -> Self

Build a new generator with a custom epoch (milliseconds since the UNIX epoch).

§Example
use id_forge::snowflake::Snowflake;

// Twitter's original Snowflake epoch.
let gen = Snowflake::with_epoch(1, 1_288_834_974_657);
assert_eq!(gen.epoch_ms(), 1_288_834_974_657);
Source

pub const fn worker_id(&self) -> u16

The worker ID this generator was built with, clamped to 10 bits.

Source

pub const fn epoch_ms(&self) -> u64

The epoch this generator subtracts from the wall clock.

Examples found in repository?
examples/basic.rs (line 23)
7fn main() {
8    let v4 = Uuid::v4();
9    println!("UUID v4:    {v4} (version={})", v4.version());
10    println!("UUID v7:    {}", Uuid::v7());
11    println!("UUID nil:   {}", Uuid::nil());
12
13    let a = Ulid::new();
14    let b = Ulid::new();
15    println!("ULID a:     {a}");
16    println!("ULID b:     {b} (monotonic: {})", b > a);
17
18    let gen = Snowflake::new(1);
19    let sf = gen.next_id();
20    let (ts_offset, worker, seq) = Snowflake::parts(sf);
21    println!(
22        "Snowflake:  {sf}  (ts+epoch={}, worker={worker}, seq={seq})",
23        ts_offset + gen.epoch_ms()
24    );
25
26    println!("NanoID 21:  {}", nanoid::generate());
27    println!("NanoID 8:   {}", nanoid::with_length(8));
28
29    assert_eq!(v4, Uuid::parse_str(&v4.to_string()).unwrap());
30    assert_eq!(a, Ulid::parse_str(&a.to_string()).unwrap());
31}
Source

pub fn try_next_id(&self) -> Result<u64, ClockSkew>

Generate the next ID, returning Err if the wall clock has moved backward since the previous call.

When 4096 IDs have been issued in the same millisecond, this method blocks in a microsecond sleep loop until the wall clock advances. It does not spin on a busy loop.

§Example
use id_forge::snowflake::Snowflake;

let gen = Snowflake::new(1);
let id = gen.try_next_id().expect("clock should not run backward");
assert!(id > 0);
Source

pub fn next_id(&self) -> u64

Generate the next ID. Panics if the wall clock has moved backward since the previous call.

Use Snowflake::try_next_id when callers need to recover from clock skew (e.g. to surface it as a service-level error rather than crash the process).

§Panics

Panics with a ClockSkew description if the wall clock has regressed below the most recently issued ID’s timestamp.

§Example
use id_forge::snowflake::Snowflake;

let gen = Snowflake::new(1);
let id = gen.next_id();
assert!(id > 0);
Examples found in repository?
examples/bench.rs (line 43)
33fn main() {
34    println!("id-forge throughput (single thread, release build)");
35    println!("---------------------------------------------------");
36
37    let iters = 1_000_000;
38    bench("Uuid::v4", iters, Uuid::v4);
39    bench("Uuid::v7", iters, Uuid::v7);
40    bench("Ulid::new", iters, Ulid::new);
41
42    let sf = Snowflake::new(1);
43    bench("Snowflake::next_id", iters, || sf.next_id());
44
45    bench("nanoid::generate", iters / 5, nanoid::generate);
46    bench("nanoid::with_length(8)", iters / 5, || {
47        nanoid::with_length(8)
48    });
49    bench("nanoid::custom(16, hex)", iters / 5, || {
50        nanoid::custom(16, b"0123456789abcdef")
51    });
52    bench("nanoid::custom(21, 17-char)", iters / 5, || {
53        nanoid::custom(21, b"ABCDEFGHIJKLMNOPQ")
54    });
55}
More examples
Hide additional examples
examples/basic.rs (line 19)
7fn main() {
8    let v4 = Uuid::v4();
9    println!("UUID v4:    {v4} (version={})", v4.version());
10    println!("UUID v7:    {}", Uuid::v7());
11    println!("UUID nil:   {}", Uuid::nil());
12
13    let a = Ulid::new();
14    let b = Ulid::new();
15    println!("ULID a:     {a}");
16    println!("ULID b:     {b} (monotonic: {})", b > a);
17
18    let gen = Snowflake::new(1);
19    let sf = gen.next_id();
20    let (ts_offset, worker, seq) = Snowflake::parts(sf);
21    println!(
22        "Snowflake:  {sf}  (ts+epoch={}, worker={worker}, seq={seq})",
23        ts_offset + gen.epoch_ms()
24    );
25
26    println!("NanoID 21:  {}", nanoid::generate());
27    println!("NanoID 8:   {}", nanoid::with_length(8));
28
29    assert_eq!(v4, Uuid::parse_str(&v4.to_string()).unwrap());
30    assert_eq!(a, Ulid::parse_str(&a.to_string()).unwrap());
31}
Source

pub const fn parts(id: u64) -> (u64, u16, u16)

Decompose an ID minted by any Snowflake generator into its (timestamp_offset_ms, worker_id, sequence) parts.

The first element is the millisecond offset from whatever epoch the originating generator was built with. To recover the wall-clock millisecond, add the generator’s epoch_ms.

§Example
use id_forge::snowflake::Snowflake;

let gen = Snowflake::new(7);
let id = gen.next_id();
let (ts_offset, worker, seq) = Snowflake::parts(id);
assert_eq!(worker, 7);
assert!(seq <= 4095);
let wall_ms = ts_offset + gen.epoch_ms();
assert!(wall_ms > gen.epoch_ms());
Examples found in repository?
examples/basic.rs (line 20)
7fn main() {
8    let v4 = Uuid::v4();
9    println!("UUID v4:    {v4} (version={})", v4.version());
10    println!("UUID v7:    {}", Uuid::v7());
11    println!("UUID nil:   {}", Uuid::nil());
12
13    let a = Ulid::new();
14    let b = Ulid::new();
15    println!("ULID a:     {a}");
16    println!("ULID b:     {b} (monotonic: {})", b > a);
17
18    let gen = Snowflake::new(1);
19    let sf = gen.next_id();
20    let (ts_offset, worker, seq) = Snowflake::parts(sf);
21    println!(
22        "Snowflake:  {sf}  (ts+epoch={}, worker={worker}, seq={seq})",
23        ts_offset + gen.epoch_ms()
24    );
25
26    println!("NanoID 21:  {}", nanoid::generate());
27    println!("NanoID 8:   {}", nanoid::with_length(8));
28
29    assert_eq!(v4, Uuid::parse_str(&v4.to_string()).unwrap());
30    assert_eq!(a, Ulid::parse_str(&a.to_string()).unwrap());
31}

Trait Implementations§

Source§

impl Debug for Snowflake

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.