snowid

Crates.iosnowid
lib.rssnowid
version0.2.0
created_at2025-02-23 01:24:17.552127+00
updated_at2025-08-19 13:27:04.946519+00
descriptionA Rust library for generating SnowID - a Snowflake-like timestamp-based distributed unique identifier
homepagehttps://github.com/qeeqez/snowid-rust
repositoryhttps://github.com/qeeqez/snowid-rust
max_upload_size
id1565971
size103,317
Maksim Sasnouski (theqeeqez)

documentation

https://docs.rs/snowid

README

โ„๏ธ SnowID Rust

Crates.io Documentation License: MIT

A Rust implementation of a Snowflake-like ID generator with 42-bit timestamp.

Generate 64-bit unique identifiers that are:

  • โšก๏ธ Fast (~244ns per ID)
  • ๐Ÿ“ˆ Time-sorted
  • ๐Ÿ”„ Monotonic
  • ๐Ÿ”’ Thread-safe
  • ๐ŸŒ Distributed-ready
  • ๐ŸŽฏ Small dependency footprint (thiserror, base62 for encoding)

๐Ÿงฎ ID Structure

Example ID: 151819733950271234

Default configuration:

|------------------------------------------|------------|------------|
|           TIMESTAMP (42 bits)            | NODE (10)  |  SEQ (12)  |
|------------------------------------------|------------|------------|
  • Timestamp: 42 bits = 139 years from 2024-01-01 (1704067200000)
  • Node ID: 10 bits = 1,024 nodes (valid range: 6-16 bits)
  • Sequence: 12 bits = 4,096 IDs/ms/node

๐ŸŽฏ Quick Start

[dependencies]
snowid = "0.2.0"
use snowid::SnowID;

fn main() {
    let gen = SnowID::new(1).unwrap();
    let id = gen.generate();
    println!("Generated ID: {}", id);
}

๐Ÿ”  Base62 Encoded IDs

Generate base62 encoded IDs (using characters 0-9, a-z, A-Z) for more compact and URL-friendly identifiers:

use snowid::SnowID;

fn main() {
    // Create a generator
    let gen = SnowID::new(1).unwrap();

    // Generate a base62 encoded ID
    let encoded_id = gen.generate_base62();
    println!("Base62 ID: {}", encoded_id); // Example: "2qPfVQh7Jw9"

    // Generate with raw value
    let (encoded_id, raw_id) = gen.generate_base62_with_raw();
    println!("Base62: {}, Raw: {}", encoded_id, raw_id);

    // Decode a base62 ID back to u64
    let decoded = gen.decode_base62(&encoded_id).unwrap();
    assert_eq!(decoded, raw_id);

    // Extract components from a base62 ID
    let (timestamp, node, sequence) = gen.decompose_base62(&encoded_id).unwrap();
    println!("Timestamp: {}, Node: {}, Sequence: {}", timestamp, node, sequence);
}

Benefits of Base62 IDs

  • ๐Ÿ”ค More compact representation (11 chars max vs 20 digits for u64)
  • ๐Ÿ”— URL-friendly (no special characters)
  • ๐Ÿ‘๏ธ Human-readable and easier to share
  • ๐Ÿ”„ Fully compatible with original SnowID structure

๐Ÿ”ง Configuration

use snowid::{SnowID, SnowIDConfig};

fn main() {
    // Create custom configuration
    let config = SnowIDConfig::builder()
        .epoch(1577836800000) // 2020-01-01 00:00:00 UTC
        .node_bits(8).unwrap()         // Supports 255 nodes
        .build();

    // Create generator with custom config
    let gen = SnowID::with_config(1, config).unwrap();
}

โ„น๏ธ Available Methods

use snowid::SnowID;

fn main() {
    let gen = SnowID::new(1).unwrap();
    
    // Generate numeric IDs
    let id = gen.generate();
    
    // Generate Base62 encoded IDs
    let base62_id = gen.generate_base62();
    let (base62_id, raw_id) = gen.generate_base62_with_raw();
    
    // Decode Base62 IDs
    let decoded = gen.decode_base62(&base62_id).unwrap();
    let (ts, node, seq) = gen.decompose_base62(&base62_id).unwrap();

    // Extract individual components from numeric IDs
    let timestamp = gen.extract.timestamp(id);  // Get timestamp from ID
    let node = gen.extract.node(id);           // Get node ID from ID
    let sequence = gen.extract.sequence(id);    // Get sequence from ID

    // Extract all components at once
    let (ts, node, seq) = gen.extract.decompose(id);

    // Configuration information
    let max_node = gen.config.max_node_id();          // Get maximum allowed node ID
    let node_bits = gen.config.node_bits();           // Get number of bits used for node ID
    let max_seq = gen.config.max_sequence_id();    // Get maximum sequence per millisecond
    let timestamp_bits = SnowID::TIMESTAMP_BITS; // Get number of bits used for timestamp (42)
}

โณ Tuning Overflow Wait (Spin/Yield)

When the per-millisecond sequence is exhausted, SnowID waits for the next millisecond. You can tune the short busy-wait (spin) before sleeping:

use snowid::{SnowID, SnowIDConfig};

fn main() {
    let config = SnowIDConfig::builder()
        .node_bits(10).unwrap()
        .enable_spin(true)   // default: true
        .spin_loops(64)      // default: 64 spin iterations before sleeping
        .spin_yield_every(16) // default: yield every 16 iterations (0 disables yielding)
        .build();

    let gen = SnowID::with_config(1, config).unwrap();
}

Notes:

  • Set enable_spin(false) or spin_loops(0) to disable spinning entirely.
  • Lower spin_loops can reduce CPU usage; higher values may reduce tail latency under overflow.

๐Ÿ“Š Performance & Comparisons

Social Media Platform Configurations

Platform Timestamp Node Bits Sequence Bits Max Nodes IDs/ms/node Time/ID
Twitter 41 10 12 1,024 4,096 ~242ns
Instagram 41 13 10 8,192 1,024 ~1.94ยตs
Discord 42 10 12 1,024 4,096 ~245ns

Node vs Sequence Bits Trade-off

Node Bits Max Nodes IDs/ms/node Time/ID
6 64 65,536 ~20ns
8 256 16,384 ~60ns
10 1,024 4,096 ~243ns
12 4,096 1,024 ~968ns
14 16,384 256 ~3.86ยตs
16 65,536 64 ~15.4ยตs

Choose configuration based on your needs:

  • More nodes โ†’ Increase node bits (max 16 bits = 65,536 nodes)
  • More IDs per node โ†’ Increase sequence bits (min 6 node bits = 64 nodes)
  • Total bits (node + sequence) is fixed at 22 bits

Int64 vs Base62 Performance

Variant Time/ID Size Notes
Int64 ~290 ns 18-20 digits Fastest option
Base62 ~295 ns 10-11 chars ~2% slower, more compact

Base62 encoding provides more compact, URL-friendly IDs with a small performance trade-off.

๐Ÿš€ Examples

Check out examples for:

  • Basic usage
  • Custom configuration
  • Base62 encoding and decoding
  • Performance comparisons between Int64 and Base62
  • Distributed generation
  • Performance benchmarks

๐Ÿ“œ License

MIT - See LICENSE for details

Commit count: 76

cargo fmt