pub struct Snowflake { /* private fields */ }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
impl Snowflake
Sourcepub const fn new(worker_id: u16) -> Self
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?
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
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}Sourcepub const fn with_epoch(worker_id: u16, epoch_ms: u64) -> Self
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);Sourcepub const fn worker_id(&self) -> u16
pub const fn worker_id(&self) -> u16
The worker ID this generator was built with, clamped to 10 bits.
Sourcepub const fn epoch_ms(&self) -> u64
pub const fn epoch_ms(&self) -> u64
The epoch this generator subtracts from the wall clock.
Examples found in repository?
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}Sourcepub fn try_next_id(&self) -> Result<u64, ClockSkew>
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);Sourcepub fn next_id(&self) -> u64
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?
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
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}Sourcepub const fn parts(id: u64) -> (u64, u16, u16)
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?
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}