| Crates.io | ider |
| lib.rs | ider |
| version | 0.1.7 |
| created_at | 2025-12-27 18:37:00.984566+00 |
| updated_at | 2026-01-13 05:24:56.018862+00 |
| description | 高性能时间戳ID生成器 / High-performance timestamp ID generator |
| homepage | https://github.com/js0-site/rust/tree/main/ider |
| repository | https://github.com/js0-site/rust.git |
| max_upload_size | |
| id | 2007641 |
| size | 75,317 |
Ider is a high-performance, time-based unique ID generator written in Rust. It generates 64-bit monotonically increasing IDs with clock backward tolerance, restart collision avoidance, and configurable timestamp offset.
Add this to Cargo.toml:
[dependencies]
ider = "0.1.3"
The crate supports optional features:
id (default: disabled): Enables global ID generator (id() and id_init())path (default: disabled): Enables file path encoding utilities (encode(), decode(), new())Enable features as needed:
[dependencies]
ider = { version = "0.1.3", features = ["id", "path"] }
use ider::Ider;
let mut ider = Ider::new();
let id = ider.get();
println!("Generated ID: {}", id);
The id feature provides a thread-local global ID generator for convenient use:
// Enable the "id" feature in Cargo.toml
use ider::id;
let id1 = id();
let id2 = id();
assert!(id2 > id1); // Monotonically increasing
// Initialize with a base ID (e.g., after recovery from storage)
use ider::id_init;
id_init(last_id_from_storage);
The path feature provides utilities for encoding IDs as filesystem-safe paths:
// Enable the "path" feature in Cargo.toml (also enables "id")
use ider::path::{encode, decode, new};
use std::path::Path;
// Encode an ID to a base32 string
let id = 1234567890u64;
let encoded = encode(id);
println!("Encoded: {}", encoded);
// Decode back to ID
let decoded = decode(&encoded);
assert_eq!(decoded, Some(id));
// Create a path with auto-generated ID
let dir = Path::new("/tmp/data");
let (id, path) = new(dir);
println!("Generated ID: {}, Path: {:?}", id, path);
use ider::Ider;
let mut ider = Ider::new();
let ids: Vec<u64> = ider.by_ref().take(5).collect();
use ider::Ider;
let mut ider = Ider::new();
let last_id = load_last_id_from_storage();
ider.init(last_id);
let new_id = ider.get();
use ider::{Ider, id_to_ts, id_to_ts_with_offset};
let mut ider = Ider::new();
let id = ider.get();
// Using default offset
let ts = id_to_ts(id);
// Using custom offset
let offset = 1735689600;
let ts_custom = id_to_ts_with_offset(id, offset);
Main ID generator structure with fields:
ts: Relative timestamp in seconds (adjusted by offset)n: Sequence number within second (0 to 1,048,575)offset: Timestamp offset in seconds (default: 2026-01-01 00:00:00 UTC)new() -> SelfCreates new generator with default offset (2026-01-01 00:00:00 UTC). Uses microseconds within second as initial sequence to avoid collision after restart.
with_offset(offset: u64) -> SelfCreates new generator with custom timestamp offset. Uses microseconds within second as initial sequence.
Parameters:
offset: Timestamp offset in secondsinit(&mut self, last_id: u64)Initializes generator to ensure it's ahead of last_id. Must call after recovery from persistent storage to prevent ID collision.
Parameters:
last_id: Last generated ID from persistent storageget(&mut self) -> u64Generates next unique 64-bit ID with O(1) time complexity.
Returns:
id feature)id() -> u64Generate a unique ID using the thread-local global generator.
Returns:
id_init(base: u64)Initialize the global ID generator with a base ID. Useful for recovery from persistent storage.
Parameters:
base: Base ID to initialize frompath feature)encode(id: u64) -> StringEncode an ID to a base32 string suitable for filesystem use.
Parameters:
id: ID to encodeReturns:
decode(name: &str) -> Option<u64>Decode a base32 string back to an ID.
Parameters:
name: Base32 encoded stringReturns:
None if invalidnew(dir: &Path) -> (ID, PathBuf)Create a path by joining the directory with an auto-generated encoded ID. Returns both the generated ID and the path.
Parameters:
dir: Base directory pathReturns:
ID: The generated unique IDPathBuf: Path with encoded ID appendedIDType alias for u64, representing a unique ID.
use ider::ID;
let id: ID = 1234567890u64;
id_to_ts(id: u64) -> u64Extracts timestamp from ID using default offset.
Parameters:
id: ID to parseReturns:
id_to_ts_with_offset(id: u64, offset: u64) -> u64Extracts timestamp from ID using custom offset.
Parameters:
id: ID to parseoffset: Timestamp offset in secondsReturns:
| 44 bits timestamp | 20 bits sequence |
|---------------------------------------|
| seconds since offset | sequence number |
ID generation follows two-phase approach:
graph TD
A[Ider::new] --> B[Get current time]
B --> C[Extract micros as initial n]
C --> D[Store ts and offset]
D --> E[Ready for generation]
F[Ider::get] --> G[Get current timestamp]
G --> H[Subtract offset]
H --> I{Time advanced?}
I -->|Yes| J[Reset n to 0]
I -->|No| K{n at max?}
K -->|Yes| L[Increment ts, reset n]
K -->|No| M[Keep current ts]
J --> N[Compose ID]
L --> N
M --> N
N --> O["Return ts << 20 | n"]
O --> P[Increment n]
Timestamp offset allows customization of ID epoch. Default offset (2026-01-01) extends ID lifespan and provides flexibility for different deployment scenarios. The relative timestamp stored in ID is calculated as: actual_timestamp - offset.
The global ID generator (id()) uses thread-local storage, providing a separate generator per thread. This design ensures thread safety without locks and maintains monotonicity within each thread.
The path module uses Crockford's base32 encoding for filesystem compatibility. This encoding avoids ambiguous characters and works well across different filesystems.
coarsetime for efficient time operationsfast32 for base32 encoding/decodingider/
├── src/
│ ├── lib.rs # Library entry point
│ ├── ider.rs # Core Ider implementation
│ ├── id.rs # Global ID generator (feature: id)
│ └── path.rs # File path encoding (feature: path)
├── tests/
│ └── main.rs # Test cases
├── readme/
│ ├── en.md # English documentation
│ └── zh.md # Chinese documentation
├── Cargo.toml # Project configuration
└── README.mdt # Documentation index
The concept of time-based ID generation dates back to early distributed systems. Twitter's Snowflake, introduced in 2010, popularized combining timestamps with sequence numbers. Ider builds upon this foundation but optimizes for simplicity and performance in single-node scenarios.
Unlike Snowflake's 41-bit timestamp with machine ID and sequence, Ider uses 44-bit timestamp with 20-bit sequence, providing sufficient capacity for most applications while eliminating need for machine ID allocation. This design choice reflects evolution toward containerized and stateless services where unique machine identification becomes less critical.
The microsecond-based initialization strategy in Ider addresses common pain point in time-based ID generators: collision avoidance after service restarts. By using current microsecond position as starting sequence, Ider minimizes probability of ID collision without requiring persistent state synchronization.
The offset feature in Ider draws inspiration from database timestamp strategies where epoch customization helps with data migration and multi-region deployment. Setting custom offset allows applications to align ID generation with business timelines or extend ID lifespan beyond default 44-bit capacity.
The thread-local global generator design ensures thread safety without locking overhead, making it ideal for high-throughput concurrent applications.
This project is an open-source component of js0.site ⋅ Refactoring the Internet Plan.
We are redefining the development paradigm of the Internet in a componentized way. Welcome to follow us:
Ider 是用 Rust 编写的高性能基于时间的唯一 ID 生成器。它生成 64 位单调递增 ID,具有时钟回拨容错、重启冲突避免和可配置时间戳偏移功能。
在 Cargo.toml 中添加:
[dependencies]
ider = "0.1.3"
该 crate 支持可选功能:
id(默认禁用):启用全局 ID 生成器(id() 和 id_init())path(默认禁用):启用文件路径编码工具(encode()、decode()、new())根据需要启用功能:
[dependencies]
ider = { version = "0.1.3", features = ["id", "path"] }
use ider::Ider;
let mut ider = Ider::new();
let id = ider.get();
println!("生成的 ID: {}", id);
id 功能提供了线程局部全局 ID 生成器,方便使用:
// 在 Cargo.toml 中启用 "id" 功能
use ider::id;
let id1 = id();
let id2 = id();
assert!(id2 > id1); // 单调递增
// 使用基础 ID 初始化(例如从存储恢复后)
use ider::id_init;
id_init(last_id_from_storage);
path 功能提供了将 ID 编码为文件系统安全路径的工具:
// 在 Cargo.toml 中启用 "path" 功能(同时启用 "id")
use ider::path::{encode, decode, new};
use std::path::Path;
// 将 ID 编码为 base32 字符串
let id = 1234567890u64;
let encoded = encode(id);
println!("编码后: {}", encoded);
// 解码回 ID
let decoded = decode(&encoded);
assert_eq!(decoded, Some(id));
// 创建自动生成 ID 的路径
let dir = Path::new("/tmp/data");
let (id, path) = new(dir);
println!("生成的 ID: {}, 路径: {:?}", id, path);
use ider::Ider;
let mut ider = Ider::new();
let ids: Vec<u64> = ider.by_ref().take(5).collect();
use ider::Ider;
let mut ider = Ider::new();
let last_id = load_last_id_from_storage();
ider.init(last_id);
let new_id = ider.get();
use ider::{Ider, id_to_ts, id_to_ts_with_offset};
let mut ider = Ider::new();
let id = ider.get();
// 使用默认偏移
let ts = id_to_ts(id);
// 使用自定义偏移
let offset = 1735689600;
let ts_custom = id_to_ts_with_offset(id, offset);
主要 ID 生成器结构,包含字段:
ts: 相对时间戳(秒,经偏移调整)n: 秒内序列号(0 到 1,048,575)offset: 时间戳偏移(秒,默认:2026-01-01 00:00:00 UTC)new() -> Self创建使用默认偏移(2026-01-01 00:00:00 UTC)的新生成器。使用秒内微秒作为初始序列,避免重启后冲突。
with_offset(offset: u64) -> Self创建带自定义时间戳偏移的新生成器。使用秒内微秒作为初始序列。
参数:
offset: 时间戳偏移(秒)init(&mut self, last_id: u64)初始化生成器确保领先于 last_id。从持久化存储恢复后必须调用,防止 ID 碰撞。
参数:
last_id: 来自持久化存储的最后生成的 IDget(&mut self) -> u64生成下一个唯一 64 位 ID,时间复杂度 O(1)。
返回:
id 功能)id() -> u64使用线程局部全局生成器生成唯一 ID。
返回:
id_init(base: u64)使用基础 ID 初始化全局 ID 生成器。适用于从持久化存储恢复。
参数:
base: 初始化的基础 IDpath 功能)encode(id: u64) -> String将 ID 编码为适用于文件系统的 base32 字符串。
参数:
id: 要编码的 ID返回:
decode(name: &str) -> Option<u64>将 base32 字符串解码回 ID。
参数:
name: Base32 编码的字符串返回:
Nonenew(dir: &Path) -> (ID, PathBuf)通过将目录与自动生成的编码 ID 拼接来创建路径。返回生成的 ID 和路径。
参数:
dir: 基础目录路径返回:
ID: 生成的唯一 IDPathBuf: 附加编码 ID 的路径IDu64 的类型别名,表示唯一 ID。
use ider::ID;
let id: ID = 1234567890u64;
id_to_ts(id: u64) -> u64使用默认偏移从 ID 提取时间戳。
参数:
id: 要解析的 ID返回:
id_to_ts_with_offset(id: u64, offset: u64) -> u64使用自定义偏移从 ID 提取时间戳。
参数:
id: 要解析的 IDoffset: 时间戳偏移(秒)返回:
| 44 位时间戳 | 20 位序列 |
|---------------------------------------|
| 自偏移的秒数 | 序列号 |
ID 生成采用两阶段方法:
graph TD
A[Ider::new] --> B[获取当前时间]
B --> C[提取微秒作为初始 n]
C --> D[存储 ts 和 offset]
D --> E[准备生成]
F[Ider::get] --> G[获取当前时间戳]
G --> H[减去偏移]
H --> I{时间前进?}
I -->|是| J[重置 n 为 0]
I -->|否| K{n 达到最大?}
K -->|是| L[递增 ts, 重置 n]
K -->|否| M[保持当前 ts]
J --> N[组合 ID]
L --> N
M --> N
N --> O["返回 ts << 20 | n"]
O --> P[递增 n]
时间戳偏移允许自定义 ID 纪元。默认偏移(2026-01-01)延长 ID 使用寿命,并为不同部署场景提供灵活性。ID 中存储的相对时间戳计算为:实际时间戳 - 偏移。
全局 ID 生成器(id())使用线程局部存储,为每个线程提供单独的生成器。这种设计确保了线程安全而无需锁,并在每个线程内保持单调性。
path 模块使用 Crockford 的 base32 编码以确保文件系统兼容性。此编码避免了歧义字符,在不同文件系统上都能很好地工作。
coarsetime 用于高效时间操作fast32 用于 base32 编码/解码ider/
├── src/
│ ├── lib.rs # 库入口
│ ├── ider.rs # 核心 Ider 实现
│ ├── id.rs # 全局 ID 生成器(功能:id)
│ └── path.rs # 文件路径编码(功能:path)
├── tests/
│ └── main.rs # 测试用例
├── readme/
│ ├── en.md # 英文文档
│ └── zh.md # 中文文档
├── Cargo.toml # 项目配置
└── README.mdt # 文档索引
基于时间的 ID 生成概念可以追溯到分布式系统的早期。Twitter 在 2010 年推出的 Snowflake 普及了将时间戳与序列号组合的方法。Ider 在此基础之上构建,但针对单节点场景进行了简单性和性能的优化。
与 Snowflake 的 41 位时间戳加机器 ID 和序列号不同,Ider 使用 44 位时间戳和 20 位序列号,为大多数应用提供足够容量,同时消除了机器 ID 分配的需要。这种设计选择反映了向容器化和无状态服务的演进,在这些场景中,唯一机器标识变得不那么关键。
Ider 中基于微秒的初始化策略解决了基于时间 ID 生成器的常见痛点:服务重启后的冲突避免。通过使用当前微秒位置作为起始序列,Ider 在不需要持久化状态同步的情况下最小化了 ID 冲突的概率。
Ider 的偏移功能借鉴了数据库时间戳策略,其中纪元自定义有助于数据迁移和多区域部署。设置自定义偏移允许应用程序将 ID 生成与业务时间线对齐,或延长 ID 使用寿命超出默认 44 位容量。
线程局部全局生成器设计确保了线程安全而无需锁定开销,使其非常适合高吞吐量并发应用程序。
本项目为 js0.site ⋅ 重构互联网计划 的开源组件。
我们正在以组件化的方式重新定义互联网的开发范式,欢迎关注: