ferroid
ferroid is a Rust crate for generating
and parsing Snowflake and ULID identifiers.
Features
- ๐ Bit-level compatibility with major Snowflake and ULID formats
- ๐งฉ Pluggable clocks and RNGs via
TimeSourceandRandSource - ๐งต Lock-free, lock-based, and single-threaded generators
- ๐ Custom layouts via
define_snowflake_id!anddefine_ulid!macros - ๐ข Crockford base32 support with
base32feature flag
๐ฆ Supported Layouts
Snowflake
| Platform | Timestamp Bits | Machine ID Bits | Sequence Bits | Epoch |
|---|---|---|---|---|
| 41 | 10 | 12 | 2010-11-04 01:42:54.657 | |
| Discord | 42 | 10 | 12 | 2015-01-01 00:00:00.000 |
| 41 | 13 | 10 | 2011-01-01 00:00:00.000 | |
| Mastodon | 48 | 0 | 16 | 1970-01-01 00:00:00.000 |
Ulid
| Platform | Timestamp Bits | Random Bits | Epoch |
|---|---|---|---|
| ULID | 48 | 80 | 1970-01-01 00:00:00.000 |
ULIDs offer high-entropy, time-sortable IDs without coordination, but are not strictly monotonic.
๐ง Generator Comparison
| Generator | Thread-Safe | Lock-Free | Throughput | Use Case |
|---|---|---|---|---|
BasicSnowflakeGenerator |
โ | โ | Highest | Sharded / single-threaded |
LockSnowflakeGenerator |
โ | โ | Medium | Fair multithreaded access |
AtomicSnowflakeGenerator |
โ | โ | High | Fast concurrent generation (less fair) |
BasicUlidGenerator |
โ | โ ๏ธ | Lower | Scalable, zero-coordination ULID generation |
[โ ๏ธ]: Uses thread-local RNG with no global locks, but not strictly lock-free in the atomic/CAS sense.
Snowflake IDs are always unique and strictly ordered. ULIDs are globally sortable but only monotonic per timestamp interval.
๐ Usage
Generate an ID
Synchronous
Calling next_id() may yield Pending if the current sequence is exhausted. In
that case, you can spin, yield, or sleep depending on your environment:
Asynchronous
If you're in an async context (e.g., using Tokio or Smol), you can enable one of the following features:
async-tokioasync-smol
Custom Layouts
To define a custom layouts, use the define_* macros:
โ ๏ธ Note: All four sections (
reserved,timestamp,machine_id, andsequence) must be specified in the snowflake macro, even if a section uses 0 bits.reservedbits are always stored as zero and can be used for future expansion. Similarly, the ulid macro requries (reserved,timestamp,random) fields.
Behavior
Snowflake:
- If the clock advances: reset sequence to 0 โ
IdGenStatus::Ready - If the clock is unchanged: increment sequence โ
IdGenStatus::Ready - If the clock goes backward: return
IdGenStatus::Pending - If the sequence overflows: return
IdGenStatus::Pending
Ulid:
- Always returns โ
IdGenStatus::Readyto have a Compatable API with Snowflake.
Serialize as padded string
Use .to_padded_string() or .encode() for sortable string representations:
๐ Benchmarks
Snowflake ID generation is theoretically capped by:
max IDs/sec = 2^sequence_bits ร 1000ms
For example, Twitter-style IDs (12 sequence bits) allow:
4096 IDs/ms ร 1000 ms/sec = ~4M IDs/sec
To benchmark this, we generate IDs in chunks of 4096, which aligns with the sequence limit per millisecond.
- Sync Snowflake: Benchmarks the hot path without yielding to the clock.
- Async Snowflake: Also uses 4096-ID batches, but may yield (sequence exhaustion/CAS failure) or await due to task scheduling, reducing throughput.
- ULID: Benchmarked using the same chunk size, but performance is primarily limited by random number generation, not sequence or clock behavior.
Tests were ran on an M1 Macbook Pro 14", 32GB, 10 cores (8 perf, 2 efficiency).
Synchronous Generators
| Generator | Time per IDs | Throughput |
|---|---|---|
| BasicSnowflakeGenerator | ~2.8 ns | ~353M IDs/sec |
| LockSnowflakeGenerator | ~8.9 ns | ~111M IDs/sec |
| AtomicSnowflakeGenerator | ~3.1 ns | ~320M IDs/sec |
| BasicUlidGenerator | ~22.9 ns | ~43M IDs/sec |
Async (Tokio Runtime)
| Generator | Generators | Time per 4M IDs | Throughput |
|---|---|---|---|
| LockSnowflakeGenerator | 1024 | ~6.95 ms | ~604M IDs/sec |
| AtomicSnowflakeGenerator | 1024 | ~3.82 ms | ~1.09B IDs/sec |
| BasicUlidGenerator | 128 | ~17.3 ms | ~242M IDs/sec |
Async (Smol Runtime)
| Generator | Generators | Time per 4M IDs | Throughput |
|---|---|---|---|
| LockSnowflakeGenerator | 1024 | ~8.10 ms | ~517M IDs/sec |
| AtomicSnowflakeGenerator | 512 | ~4.31 ms | ~973M IDs/sec |
| BasicUlidGenerator | 128 | ~14.3 ms | ~294M IDs/sec |
To run all benchmarks:
๐งช Testing
Run all tests with:
๐ License
Licensed under either of:
at your option.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.