Skip to main content

Crate snowflake_me

Crate snowflake_me 

Source
Expand description

A high-performance, highly concurrent, distributed Snowflake ID generator.

This implementation is lock-free, designed for maximum throughput and minimum latency on multi-core CPUs.

§Highlights

  • Lock-Free Concurrency: Uses AtomicU64 and CAS operations to manage internal state, eliminating Mutex lock overhead.
  • High Performance: The lock-free design makes ID generation extremely fast, performing exceptionally well under high concurrency.
  • Highly Customizable: The Builder pattern allows you to flexibly configure the start time, machine ID, data center ID, and the bit lengths of each component.
  • Smart IP Fallback: With the ip-fallback feature enabled, if machine_id or data_center_id are not provided, the system automatically derives them from local network interfaces.
    • Supports both IPv4 and IPv6: It prioritizes private IPv4 addresses and falls back to private IPv6 addresses.
    • Conflict-Free: To ensure uniqueness, machine_id and data_center_id are derived from distinct parts of the IP address.
  • no_std Support: Works in no_std + alloc environments with a user-provided time source.

§Architecture

A Snowflake ID is a 64-bit integer composed of four sections:

┌─────────────────────────────────────────────────────────────────┐
│ 0 │ 41 bits: time  │ 5 bits: dc │ 5 bits: machine │ 12 bits: seq │
└─────────────────────────────────────────────────────────────────┘
  ↑                                                                  │
  sign bit (always 0)                                                 │
  • Time (41 bits): Milliseconds since the configured start time. Default epoch is 2022-01-01.
  • Data Center ID (5 bits): Identifies the data center (0–31).
  • Machine ID (5 bits): Identifies the machine within the data center (0–31).
  • Sequence (12 bits): Per-millisecond counter (0–4095).

The bit lengths are fully configurable via Builder, as long as they sum to 63.

§Performance

The generator uses a lock-free CAS (Compare-And-Swap) loop with cache-line aligned state (#[repr(align(64))]) to prevent false sharing between threads. Under contention, it defaults to compare_exchange_weak which allows spurious failures for better throughput on ARM and other architectures. Enable the use-strong-cas feature to use compare_exchange instead.

§Thread Safety

Snowflake is safe to Clone and share across threads. Cloning is a cheap Arc increment — all clones share the same internal state. The next_id method is lock-free and safe to call concurrently from any number of threads.

§Feature Flags

FeatureDependenciesDefaultDescription
stdjiff, thiserror/stdYesStandard library support (time via jiff)
ip-fallbackstd, pnet_datalinkNoAuto-derive IDs from local IP address
serdeserdeNoSerde serialization for SnowflakeId and DecomposedSnowflake
tracingtracingNoStructured logging at key points
metricsmetricsNoRuntime counters and gauges
use-strong-casNoUse compare_exchange instead of compare_exchange_weak
fullall of the aboveNoEnable all optional features

§Quick Start

§1. Add Dependency

Add this to your Cargo.toml. To use automatic IP-based configuration, enable the ip-fallback feature.

[dependencies]
snowflake_me = { version = "2.1.1", features = ["ip-fallback"] }

§2. Basic Usage

use snowflake_me::Snowflake;

// Create a generator with the default configuration.
// Note: This requires the `ip-fallback` feature to auto-detect machine and data center IDs.
let sf = Snowflake::new().unwrap();
let next_id = sf.next_id().unwrap();
println!("Generated ID: {}", next_id);

For production environments, it is highly recommended to use the Builder to manually configure machine_id and data_center_id for maximum reliability.

use snowflake_me::Snowflake;
use std::thread;
use std::sync::Arc;
use std::collections::HashSet;

// Manually configure IDs for reliability.
let sf = Snowflake::builder()
    .machine_id(&|| Ok(10))
    .data_center_id(&|| Ok(5))
    .finalize()
    .unwrap();

let sf_arc = Arc::new(sf);
let mut handles = vec![];

for _ in 0..10 {
    let sf_clone = Arc::clone(&sf_arc);
    let handle = thread::spawn(move || {
        let mut ids = Vec::new();
        for _ in 0..1000 {
            ids.push(sf_clone.next_id().unwrap());
        }
        ids
    });
    handles.push(handle);
}

let mut all_ids = HashSet::new();
for handle in handles {
    let ids = handle.join().unwrap();
    for id in ids {
        // Verify that all IDs are unique
        assert!(all_ids.insert(id), "Found duplicate ID: {}", id);
    }
}

println!("Successfully generated {} unique IDs.", all_ids.len());

§Decomposing an ID

You can decompose a Snowflake ID back into its original components.

use snowflake_me::{Snowflake, DecomposedSnowflake};

// Use the same configuration that was used for generation.
let sf = Snowflake::builder()
    .machine_id(&|| Ok(15))
    .data_center_id(&|| Ok(7))
    .finalize()
    .unwrap();

let id = sf.next_id().unwrap();

// Decompose the ID using the generator's configuration.
let decomposed = sf.decompose(id);

println!("ID: {}", decomposed.id);
println!("Time: {}", decomposed.time);
println!("Data Center ID: {}", decomposed.data_center_id);
println!("Machine ID: {}", decomposed.machine_id);
println!("Sequence: {}", decomposed.sequence);

assert_eq!(decomposed.machine_id, 15);
assert_eq!(decomposed.data_center_id, 7);

§Clock Drift Protection

If the system clock moves backward (e.g., due to NTP adjustments), the generator can handle it using one of three strategies:

use snowflake_me::{Snowflake, ClockDriftStrategy};

let sf = Snowflake::builder()
    .machine_id(&|| Ok(1))
    .data_center_id(&|| Ok(1))
    .clock_drift_strategy(ClockDriftStrategy::Wait)
    .max_clock_drift_ms(5000)  // fail if drift > 5 seconds
    .finalize()
    .unwrap();

See ClockDriftStrategy for details on each strategy.

§no_std Usage

In no_std environments, disable default features and provide a time source:

// In your timer interrupt or main loop:
snowflake_me::set_time_source(current_millis);

// Then create the generator (start_time must be a raw i64 in milliseconds):
let sf = Snowflake::builder()
    .start_time(1_640_995_200_000) // 2022-01-01 UTC
    .machine_id(&|| Ok(1))
    .data_center_id(&|| Ok(1))
    .finalize()
    .unwrap();

Re-exports§

pub use clock::ClockDriftStrategy;
pub use id::SnowflakeId;

Modules§

clock
Clock drift handling strategies for backward clock detection.
id
The SnowflakeId newtype with encoding methods and trait implementations.

Structs§

Builder
A builder for constructing the Snowflake generator.
DecomposedSnowflake
All components of a decomposed Snowflake ID.
Snowflake
A high-performance, distributed, unique ID generator.

Enums§

Error
The error type for this crate.