@cdlab996/genid 1.2.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +280 -257
- package/README.zh-CN.md +377 -0
- package/dist/index.cjs +95 -240
- package/dist/index.d.cts +83 -236
- package/dist/index.d.mts +83 -236
- package/dist/index.mjs +93 -238
- package/package.json +7 -9
package/README.md
CHANGED
|
@@ -1,354 +1,377 @@
|
|
|
1
1
|
# @cdlab996/genid
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@cdlab996/genid)
|
|
4
|
+
[](./LICENSE)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
High-performance distributed unique ID generator based on the Snowflake algorithm, with drift mode and clock-rollback handling.
|
|
6
7
|
|
|
7
|
-
-
|
|
8
|
-
- 🔄 **时钟回拨处理**:优雅处理时钟回拨,不阻塞 ID 生成
|
|
9
|
-
- ⚙️ **灵活配置**:支持自定义位长度分配
|
|
10
|
-
- 📊 **性能监控**:内置统计和调试功能
|
|
11
|
-
- ✅ **ID 验证**:验证 ID 的有效性,支持严格/宽松模式
|
|
8
|
+
[中文文档](./README.zh-CN.md)
|
|
12
9
|
|
|
13
|
-
##
|
|
10
|
+
## Features
|
|
14
11
|
|
|
15
|
-
|
|
12
|
+
- **Drift Algorithm** - Exceeds per-millisecond sequence limits under high concurrency for better throughput
|
|
13
|
+
- **Clock Rollback Handling** - Graceful degradation using reserved sequence numbers without blocking ID generation
|
|
14
|
+
- **Flexible Configuration** - Customize bit allocation for timestamp, worker ID, and sequence
|
|
15
|
+
- **ID Validation** - Strict and loose validation modes with `afterTime` support
|
|
16
|
+
- **Runtime Monitoring** - Built-in statistics, parsing, and binary formatting for debugging
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
graph TB
|
|
19
|
-
A[开始生成 ID] --> B{是否处于漂移状态?}
|
|
20
|
-
|
|
21
|
-
B -->|否| C[正常路径]
|
|
22
|
-
B -->|是| D[漂移路径]
|
|
23
|
-
|
|
24
|
-
C --> E{检测时钟}
|
|
25
|
-
E -->|时钟回拨| F[使用保留序列号 0-4]
|
|
26
|
-
E -->|时间前进| G[重置序列号]
|
|
27
|
-
E -->|同一毫秒| H{序列号是否溢出?}
|
|
28
|
-
|
|
29
|
-
H -->|否| I[序列号+1 正常生成]
|
|
30
|
-
H -->|是| J[进入漂移状态 时间戳+1]
|
|
31
|
-
|
|
32
|
-
D --> K{检测时间}
|
|
33
|
-
K -->|时间追上| L[退出漂移 恢复正常]
|
|
34
|
-
K -->|超过最大漂移| M[等待下一毫秒 退出漂移]
|
|
35
|
-
K -->|继续漂移| N{序列号是否溢出?}
|
|
36
|
-
|
|
37
|
-
N -->|否| O[使用当前序列号]
|
|
38
|
-
N -->|是| P[时间戳+1 重置序列号]
|
|
39
|
-
|
|
40
|
-
F --> Q[计算 ID]
|
|
41
|
-
G --> Q
|
|
42
|
-
I --> Q
|
|
43
|
-
J --> Q
|
|
44
|
-
L --> Q
|
|
45
|
-
M --> Q
|
|
46
|
-
O --> Q
|
|
47
|
-
P --> Q
|
|
48
|
-
|
|
49
|
-
Q --> R[更新统计]
|
|
50
|
-
R --> S[返回 ID]
|
|
51
|
-
```
|
|
18
|
+
## Installation
|
|
52
19
|
|
|
53
|
-
|
|
20
|
+
```bash
|
|
21
|
+
# npm
|
|
22
|
+
npm install @cdlab996/genid
|
|
54
23
|
|
|
24
|
+
# pnpm
|
|
25
|
+
pnpm add @cdlab996/genid
|
|
55
26
|
```
|
|
56
|
-
|------------ 时间戳 ------------|-- 工作节点 ID --|-- 序列号 --|
|
|
57
|
-
42-52 bits 1-15 bits 3-21 bits
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
**位分配示例(默认配置):**
|
|
61
|
-
|
|
62
|
-
- 时间戳:52 bits(可用约 139 年)
|
|
63
|
-
- 工作节点 ID:6 bits(支持 64 个节点)
|
|
64
|
-
- 序列号:6 bits(每毫秒 59 个 ID,5-63)
|
|
65
|
-
|
|
66
|
-
**序列号分配:**
|
|
67
|
-
|
|
68
|
-
- `0-4`:保留用于时钟回拨
|
|
69
|
-
- `5-maxSeqNumber`:正常使用
|
|
70
27
|
|
|
71
|
-
##
|
|
28
|
+
## Quick Start
|
|
72
29
|
|
|
73
30
|
```typescript
|
|
74
31
|
import { GenidOptimized } from '@cdlab996/genid'
|
|
75
32
|
|
|
76
|
-
//
|
|
33
|
+
// Create an instance (use a different workerId for each worker/process)
|
|
77
34
|
const genid = new GenidOptimized({ workerId: 1 })
|
|
78
35
|
|
|
79
|
-
//
|
|
80
|
-
const id = genid.nextId()
|
|
81
|
-
console.log(id) // 123456789012345
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
## API 参考
|
|
85
|
-
|
|
86
|
-
### 构造函数
|
|
87
|
-
|
|
88
|
-
```typescript
|
|
89
|
-
new GenidOptimized(options: GenidOptions)
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
**配置选项**
|
|
93
|
-
|
|
94
|
-
| 参数 | 类型 | 必填 | 默认值 | 说明 |
|
|
95
|
-
|------|------|------|--------|------|
|
|
96
|
-
| `workerId` | number | ✅ | - | 工作节点 ID(范围:0 到 2^workerIdBitLength-1) |
|
|
97
|
-
| `method` | GenidMethod | ❌ | `DRIFT` | 算法类型:`DRIFT` 或 `TRADITIONAL` |
|
|
98
|
-
| `baseTime` | number | ❌ | `1577836800000` | 起始时间戳(毫秒,默认:2020-01-01) |
|
|
99
|
-
| `workerIdBitLength` | number | ❌ | `6` | 工作节点 ID 位数(1-15) |
|
|
100
|
-
| `seqBitLength` | number | ❌ | `6` | 序列号位数(3-21) |
|
|
101
|
-
| `maxSeqNumber` | number | ❌ | `2^seqBitLength-1` | 最大序列号 |
|
|
102
|
-
| `minSeqNumber` | number | ❌ | `5` | 最小序列号(0-4 保留用于时钟回拨) |
|
|
103
|
-
| `topOverCostCount` | number | ❌ | `2000` | 最大漂移次数 |
|
|
104
|
-
|
|
105
|
-
### 生成 ID
|
|
106
|
-
|
|
107
|
-
#### `nextId()`
|
|
108
|
-
|
|
109
|
-
返回 Number 或 BigInt 类型的 ID(自动选择)
|
|
110
|
-
|
|
111
|
-
```typescript
|
|
36
|
+
// Generate an ID
|
|
112
37
|
const id = genid.nextId()
|
|
113
|
-
```
|
|
114
38
|
|
|
115
|
-
|
|
39
|
+
// Batch generate
|
|
40
|
+
const ids = genid.nextBatch(1000)
|
|
116
41
|
|
|
117
|
-
|
|
42
|
+
// Parse an ID
|
|
43
|
+
const info = genid.parse(id)
|
|
44
|
+
// => { timestamp: Date, timestampMs: 1609459200000, workerId: 1, sequence: 42 }
|
|
118
45
|
|
|
119
|
-
|
|
120
|
-
|
|
46
|
+
// Validate an ID
|
|
47
|
+
genid.isValid(id) // true
|
|
121
48
|
```
|
|
122
49
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
返回 BigInt 类型的 ID
|
|
50
|
+
## API
|
|
126
51
|
|
|
127
|
-
|
|
128
|
-
const id = genid.nextBigId()
|
|
129
|
-
```
|
|
52
|
+
### `new GenidOptimized(options)`
|
|
130
53
|
|
|
131
|
-
|
|
54
|
+
| Parameter | Type | Required | Default | Description |
|
|
55
|
+
| ------------------- | ------------- | :------: | ------------------ | --------------------------------------------------- |
|
|
56
|
+
| `workerId` | `number` | Yes | - | Worker node ID (0 to 2^workerIdBitLength-1) |
|
|
57
|
+
| `method` | `GenidMethod` | | `DRIFT` | Algorithm: `DRIFT` or `TRADITIONAL` |
|
|
58
|
+
| `baseTime` | `number` | | `1577836800000` | Base timestamp in ms (default: 2020-01-01) |
|
|
59
|
+
| `workerIdBitLength` | `number` | | `6` | Bit length for worker ID (1-15) |
|
|
60
|
+
| `seqBitLength` | `number` | | `6` | Bit length for sequence (3-21) |
|
|
61
|
+
| `maxSeqNumber` | `number` | | `2^seqBitLength-1` | Maximum sequence number |
|
|
62
|
+
| `minSeqNumber` | `number` | | `5` | Minimum sequence number (0-4 reserved for rollback) |
|
|
63
|
+
| `topOverCostCount` | `number` | | `2000` | Maximum drift count |
|
|
132
64
|
|
|
133
|
-
|
|
65
|
+
### Generating IDs
|
|
134
66
|
|
|
135
67
|
```typescript
|
|
136
|
-
|
|
137
|
-
|
|
68
|
+
genid.nextId() // Returns number | bigint (auto-selects)
|
|
69
|
+
genid.nextNumber() // Returns number (throws if exceeds safe integer range)
|
|
70
|
+
genid.nextBigId() // Returns bigint
|
|
71
|
+
genid.nextBatch(100) // Batch generate 100 IDs
|
|
72
|
+
genid.nextBatch(100, true) // Batch generate 100 BigInt IDs
|
|
138
73
|
```
|
|
139
74
|
|
|
140
|
-
###
|
|
141
|
-
|
|
142
|
-
#### `parse(id)`
|
|
143
|
-
|
|
144
|
-
解析 ID,提取组成部分
|
|
75
|
+
### Parsing & Validation
|
|
145
76
|
|
|
146
77
|
```typescript
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
// {
|
|
150
|
-
|
|
151
|
-
//
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
|
|
78
|
+
// Parse an ID into its components
|
|
79
|
+
genid.parse(id)
|
|
80
|
+
// => { timestamp: Date, timestampMs: number, workerId: number, sequence: number }
|
|
81
|
+
|
|
82
|
+
// Loose validation: checks structural validity
|
|
83
|
+
genid.isValid(id) // true
|
|
84
|
+
genid.isValid('invalid') // false
|
|
85
|
+
|
|
86
|
+
// Strict validation: requires workerId to match the current instance
|
|
87
|
+
genid.isValid(id, true) // true (generated by this instance)
|
|
88
|
+
genid.isValid(otherId, true) // false (generated by another instance)
|
|
89
|
+
|
|
90
|
+
// Time-bound validation: reject IDs generated before a given time
|
|
91
|
+
const startupTime = Date.now()
|
|
92
|
+
genid.isValid(id, { afterTime: startupTime }) // true
|
|
93
|
+
genid.isValid(id, { strictWorkerId: true, afterTime: startupTime }) // combined
|
|
155
94
|
```
|
|
156
95
|
|
|
157
|
-
###
|
|
158
|
-
|
|
159
|
-
#### `isValid(id, strictWorkerId?)`
|
|
160
|
-
|
|
161
|
-
验证 ID 是否为有效的 Snowflake ID
|
|
162
|
-
|
|
163
|
-
**参数**
|
|
164
|
-
- `id` - 要验证的 ID(支持 Number、BigInt、String 类型)
|
|
165
|
-
- `strictWorkerId` - 可选,是否严格验证 workerId 必须匹配当前实例(默认:false)
|
|
166
|
-
|
|
167
|
-
**返回值**
|
|
168
|
-
- `boolean` - ID 是否有效
|
|
169
|
-
|
|
170
|
-
**验证规则**
|
|
171
|
-
- ✅ ID 为正数
|
|
172
|
-
- ✅ ID 在 64 位范围内
|
|
173
|
-
- ✅ 时间戳在合理范围内(>= baseTime,<= 当前时间 + 1秒容差)
|
|
174
|
-
- ✅ workerId 在有效范围内(0 到 2^workerIdBitLength-1)
|
|
175
|
-
- ✅ 序列号在有效范围内(0 到 2^seqBitLength-1)
|
|
176
|
-
- ✅ 严格模式下:workerId 必须匹配当前实例
|
|
177
|
-
|
|
178
|
-
```typescript
|
|
179
|
-
const genid = new GenidOptimized({ workerId: 1 })
|
|
180
|
-
const id = genid.nextId()
|
|
181
|
-
|
|
182
|
-
// 宽松模式:验证 ID 格式是否有效
|
|
183
|
-
genid.isValid(id) // true
|
|
184
|
-
genid.isValid(12345) // false(无效的 ID)
|
|
185
|
-
genid.isValid(-1) // false(负数)
|
|
186
|
-
genid.isValid('invalid') // false(无效格式)
|
|
187
|
-
|
|
188
|
-
// 严格模式:验证 ID 是否由当前实例生成
|
|
189
|
-
const genid2 = new GenidOptimized({ workerId: 2 })
|
|
190
|
-
const id2 = genid2.nextId()
|
|
191
|
-
|
|
192
|
-
genid.isValid(id2) // true(宽松模式,其他实例的 ID 也有效)
|
|
193
|
-
genid.isValid(id2, true) // false(严格模式,workerId 不匹配)
|
|
194
|
-
genid.isValid(id, true) // true(严格模式,workerId 匹配)
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
### 统计与配置
|
|
198
|
-
|
|
199
|
-
#### `getStats()`
|
|
200
|
-
|
|
201
|
-
获取生成器统计信息
|
|
96
|
+
### Statistics & Configuration
|
|
202
97
|
|
|
203
98
|
```typescript
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
//
|
|
207
|
-
//
|
|
208
|
-
//
|
|
209
|
-
//
|
|
210
|
-
//
|
|
211
|
-
//
|
|
99
|
+
// Get runtime statistics
|
|
100
|
+
genid.getStats()
|
|
101
|
+
// => {
|
|
102
|
+
// totalGenerated: 1000,
|
|
103
|
+
// overCostCount: 10,
|
|
104
|
+
// turnBackCount: 2,
|
|
105
|
+
// uptimeMs: 60000,
|
|
106
|
+
// avgPerSecond: 16,
|
|
107
|
+
// currentState: 'NORMAL' | 'OVER_COST'
|
|
212
108
|
// }
|
|
213
|
-
```
|
|
214
|
-
|
|
215
|
-
#### `getConfig()`
|
|
216
109
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
const config = genid.getConfig()
|
|
221
|
-
// {
|
|
110
|
+
// Get current configuration
|
|
111
|
+
genid.getConfig()
|
|
112
|
+
// => {
|
|
222
113
|
// method: 'DRIFT',
|
|
223
114
|
// workerId: 1,
|
|
224
115
|
// workerIdRange: '0-63',
|
|
225
116
|
// sequenceRange: '5-63',
|
|
117
|
+
// maxSequence: 63,
|
|
226
118
|
// idsPerMillisecond: 59,
|
|
227
119
|
// baseTime: Date,
|
|
228
120
|
// timestampBits: 52,
|
|
229
121
|
// workerIdBits: 6,
|
|
230
122
|
// sequenceBits: 6
|
|
231
123
|
// }
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
#### `resetStats()`
|
|
235
124
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
```typescript
|
|
125
|
+
// Reset statistics
|
|
239
126
|
genid.resetStats()
|
|
240
127
|
```
|
|
241
128
|
|
|
242
|
-
###
|
|
243
|
-
|
|
244
|
-
#### `formatBinary(id)`
|
|
245
|
-
|
|
246
|
-
格式化 ID 为二进制字符串
|
|
129
|
+
### Debugging
|
|
247
130
|
|
|
248
131
|
```typescript
|
|
249
|
-
|
|
132
|
+
genid.formatBinary(id)
|
|
250
133
|
// ID: 123456789012345
|
|
251
134
|
// Binary (64-bit):
|
|
252
|
-
// 0000000000011010... -
|
|
253
|
-
// 000001 -
|
|
254
|
-
// 101010 -
|
|
135
|
+
// 0000000000011010... - Timestamp (52 bits) = 2025-10-17T...
|
|
136
|
+
// 000001 - Worker ID (6 bits) = 1
|
|
137
|
+
// 101010 - Sequence (6 bits) = 42
|
|
255
138
|
```
|
|
256
139
|
|
|
257
|
-
##
|
|
140
|
+
## Examples
|
|
258
141
|
|
|
259
|
-
###
|
|
142
|
+
### Custom Bit Allocation
|
|
260
143
|
|
|
261
144
|
```typescript
|
|
262
|
-
|
|
263
|
-
workerId: 1
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
// 生成单个 ID
|
|
267
|
-
const id1 = genid.nextId()
|
|
268
|
-
|
|
269
|
-
// 批量生成
|
|
270
|
-
const ids = genid.nextBatch(1000)
|
|
271
|
-
```
|
|
145
|
+
import { GenidOptimized, GenidMethod } from '@cdlab996/genid'
|
|
272
146
|
|
|
273
|
-
### 自定义配置
|
|
274
|
-
|
|
275
|
-
```typescript
|
|
276
147
|
const genid = new GenidOptimized({
|
|
277
148
|
workerId: 1,
|
|
278
149
|
method: GenidMethod.TRADITIONAL,
|
|
279
150
|
baseTime: new Date('2024-01-01').valueOf(),
|
|
280
|
-
workerIdBitLength: 10,
|
|
281
|
-
seqBitLength: 12,
|
|
282
|
-
topOverCostCount: 5000
|
|
151
|
+
workerIdBitLength: 10, // Support 1024 nodes
|
|
152
|
+
seqBitLength: 12, // 4096 IDs per millisecond
|
|
153
|
+
topOverCostCount: 5000,
|
|
283
154
|
})
|
|
284
155
|
```
|
|
285
156
|
|
|
286
|
-
###
|
|
157
|
+
### Validating External IDs
|
|
287
158
|
|
|
288
159
|
```typescript
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
// 生成并验证 ID
|
|
292
|
-
const id = genid.nextId()
|
|
293
|
-
if (genid.isValid(id)) {
|
|
294
|
-
console.log('ID 有效')
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// 验证外部 ID(例如从数据库或 API 获取的 ID)
|
|
160
|
+
// Validate IDs from a database or API
|
|
298
161
|
const externalId = '123456789012345'
|
|
299
162
|
if (genid.isValid(externalId)) {
|
|
300
163
|
const info = genid.parse(externalId)
|
|
301
|
-
console.log('
|
|
164
|
+
console.log('Generated at:', info.timestamp)
|
|
165
|
+
console.log('From worker:', info.workerId)
|
|
302
166
|
} else {
|
|
303
|
-
console.error('ID
|
|
167
|
+
console.error('Invalid ID')
|
|
304
168
|
}
|
|
305
|
-
|
|
306
|
-
// 严格验证(只接受当前实例生成的 ID)
|
|
307
|
-
const isMyId = genid.isValid(id, true)
|
|
308
169
|
```
|
|
309
170
|
|
|
310
|
-
###
|
|
171
|
+
### Performance Monitoring
|
|
311
172
|
|
|
312
173
|
```typescript
|
|
313
|
-
// 定期检查统计信息
|
|
314
174
|
setInterval(() => {
|
|
315
175
|
const stats = genid.getStats()
|
|
316
|
-
console.log(
|
|
317
|
-
console.log(`漂移次数: ${stats.overCostCount}`)
|
|
176
|
+
console.log(`Rate: ${stats.avgPerSecond} ID/s | Drift: ${stats.overCostCount} | Rollback: ${stats.turnBackCount}`)
|
|
318
177
|
}, 10000)
|
|
319
178
|
```
|
|
320
179
|
|
|
321
|
-
##
|
|
180
|
+
## Algorithm Modes
|
|
181
|
+
|
|
182
|
+
| Mode | Description | Use Case |
|
|
183
|
+
| ------------------- | ------------------------------------------------------------------ | ----------------------------- |
|
|
184
|
+
| **DRIFT** (default) | Borrows future timestamps when sequence is exhausted; avoids waits | High-frequency ID generation |
|
|
185
|
+
| **TRADITIONAL** | Strictly increasing timestamps; waits for next ms on exhaustion | Strict time-ordering required |
|
|
186
|
+
|
|
187
|
+
## Architecture
|
|
188
|
+
|
|
189
|
+
### ID Structure (64-bit)
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
|------------ Timestamp ------------|-- Worker ID --|-- Sequence --|
|
|
193
|
+
42-52 bits 1-15 bits 3-21 bits
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Default: Timestamp 52 bits (~139 years) | Worker ID 6 bits (64 nodes) | Sequence 6 bits (59 IDs/ms)
|
|
197
|
+
|
|
198
|
+
Sequence values `0-4` are reserved for clock rollback; normal generation starts at `5`.
|
|
199
|
+
|
|
200
|
+
### Core Flow
|
|
201
|
+
|
|
202
|
+
```mermaid
|
|
203
|
+
graph TB
|
|
204
|
+
A[Start ID Generation] --> B{In drift mode?}
|
|
322
205
|
|
|
323
|
-
|
|
206
|
+
B -->|No| C[Normal Path]
|
|
207
|
+
B -->|Yes| D[Drift Path]
|
|
324
208
|
|
|
325
|
-
|
|
326
|
-
-
|
|
327
|
-
|
|
209
|
+
C --> E{Check Clock}
|
|
210
|
+
E -->|Clock Rollback| F[Use Reserved Sequence 0-4]
|
|
211
|
+
E -->|Time Advanced| G[Reset Sequence]
|
|
212
|
+
E -->|Same Millisecond| H{Sequence Exhausted?}
|
|
328
213
|
|
|
329
|
-
|
|
214
|
+
H -->|No| I[Increment Sequence]
|
|
215
|
+
H -->|Yes| J[Enter Drift Mode, Timestamp+1]
|
|
330
216
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
217
|
+
D --> K{Check Time}
|
|
218
|
+
K -->|Time Caught Up| L[Exit Drift, Resume Normal]
|
|
219
|
+
K -->|Exceeded Max Drift| M[Wait for Next ms, Exit Drift]
|
|
220
|
+
K -->|Continue Drift| N{Sequence Exhausted?}
|
|
334
221
|
|
|
335
|
-
|
|
222
|
+
N -->|No| O[Use Current Sequence]
|
|
223
|
+
N -->|Yes| P[Timestamp+1, Reset Sequence]
|
|
336
224
|
|
|
337
|
-
|
|
225
|
+
F --> Q[Assemble ID]
|
|
226
|
+
G --> Q
|
|
227
|
+
I --> Q
|
|
228
|
+
J --> Q
|
|
229
|
+
L --> Q
|
|
230
|
+
M --> Q
|
|
231
|
+
O --> Q
|
|
232
|
+
P --> Q
|
|
233
|
+
|
|
234
|
+
Q --> R[Update Statistics]
|
|
235
|
+
R --> S[Return ID]
|
|
236
|
+
```
|
|
338
237
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
238
|
+
## Performance
|
|
239
|
+
|
|
240
|
+
Throughput is determined by the number of available sequence slots per millisecond. Increasing `seqBitLength` scales linearly:
|
|
241
|
+
|
|
242
|
+
| seqBitLength | Slots/ms | Throughput |
|
|
243
|
+
| :-------------: | -------: | ------------------: |
|
|
244
|
+
| 3 | 3 | ~3,000 IDs/sec |
|
|
245
|
+
| 4 | 11 | ~11,000 IDs/sec |
|
|
246
|
+
| **6** (default) | **59** | **~58,000 IDs/sec** |
|
|
247
|
+
| 8 | 251 | ~247,000 IDs/sec |
|
|
248
|
+
| 10 | 1,019 | ~1,000,000 IDs/sec |
|
|
249
|
+
| 14 | 16,379 | ~4,500,000 IDs/sec |
|
|
250
|
+
|
|
251
|
+
> Measured on Node.js v22 (x64). Actual results vary by environment.
|
|
252
|
+
> Run `pnpm run benchmark` to probe your own machine.
|
|
253
|
+
|
|
254
|
+
| Metric | Value (default config) |
|
|
255
|
+
| ------------------------- | ---------------------: |
|
|
256
|
+
| Max worker nodes | 64 |
|
|
257
|
+
| Timestamp lifespan | ~139 years |
|
|
258
|
+
| P99 latency (single call) | < 1µs |
|
|
259
|
+
|
|
260
|
+
## Benchmark
|
|
261
|
+
|
|
262
|
+
A built-in probe script measures the actual capability of the current environment:
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
pnpm run benchmark
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
It reports:
|
|
269
|
+
|
|
270
|
+
1. **Single-call throughput** — peak `nextId()` IDs/sec
|
|
271
|
+
2. **Batch throughput** — `nextBatch()` across different batch sizes
|
|
272
|
+
3. **Latency percentiles** — P50 / P95 / P99 / P99.9 / Max
|
|
273
|
+
4. **Algorithm comparison** — DRIFT vs TRADITIONAL side-by-side
|
|
274
|
+
5. **Throughput by seqBitLength** — how bit allocation affects throughput
|
|
275
|
+
6. **Memory footprint** — heap delta after 1M generations
|
|
276
|
+
7. **Recommended thresholds** — suggested safe values for test assertions (60% of peak)
|
|
277
|
+
|
|
278
|
+
<details>
|
|
279
|
+
<summary>Example output</summary>
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
station :: /app/projects/genid ‹main*› » pnpm run benchmark
|
|
283
|
+
|
|
284
|
+
> @cdlab996/genid@1.4.0 benchmark /app/projects/genid
|
|
285
|
+
> npx tsx scripts/benchmark.ts
|
|
286
|
+
|
|
287
|
+
============================================================
|
|
288
|
+
GenidOptimized — Environment Capability Probe
|
|
289
|
+
Node v22.22.0 | linux x64
|
|
290
|
+
Date: 2026-04-01T08:41:07.669Z
|
|
291
|
+
============================================================
|
|
292
|
+
|
|
293
|
+
────────────────────────────────────────────────────────────
|
|
294
|
+
1. Single-call throughput (nextId)
|
|
295
|
+
────────────────────────────────────────────────────────────
|
|
296
|
+
Duration: 3001ms
|
|
297
|
+
Generated: 226,073
|
|
298
|
+
Throughput: 75,332 IDs/sec
|
|
299
|
+
|
|
300
|
+
────────────────────────────────────────────────────────────
|
|
301
|
+
2. Batch throughput (nextBatch)
|
|
302
|
+
────────────────────────────────────────────────────────────
|
|
303
|
+
batch= 100 × 5000 => 61,665 IDs/sec
|
|
304
|
+
batch= 1,000 × 500 => 61,577 IDs/sec
|
|
305
|
+
batch= 10,000 × 50 => 61,716 IDs/sec
|
|
306
|
+
batch=100,000 × 5 => 61,644 IDs/sec
|
|
307
|
+
|
|
308
|
+
────────────────────────────────────────────────────────────
|
|
309
|
+
3. Single-call latency percentiles
|
|
310
|
+
────────────────────────────────────────────────────────────
|
|
311
|
+
Samples: 100,000
|
|
312
|
+
Avg: 0µs
|
|
313
|
+
P50: 0µs
|
|
314
|
+
P95: 1µs
|
|
315
|
+
P99: 1µs
|
|
316
|
+
P99.9: 1µs
|
|
317
|
+
Max: 364µs
|
|
318
|
+
|
|
319
|
+
────────────────────────────────────────────────────────────
|
|
320
|
+
4. Algorithm comparison (DRIFT vs TRADITIONAL)
|
|
321
|
+
────────────────────────────────────────────────────────────
|
|
322
|
+
DRIFT 58,296 IDs/sec drift=1
|
|
323
|
+
TRADITIONAL 59,000 IDs/sec drift=0
|
|
324
|
+
|
|
325
|
+
────────────────────────────────────────────────────────────
|
|
326
|
+
5. Throughput by sequence bit length
|
|
327
|
+
────────────────────────────────────────────────────────────
|
|
328
|
+
seqBits= 3 maxSeq= 7 => 2,986 IDs/sec drift=1
|
|
329
|
+
seqBits= 4 maxSeq= 15 => 10,884 IDs/sec drift=1
|
|
330
|
+
seqBits= 6 maxSeq= 63 => 58,240 IDs/sec drift=1
|
|
331
|
+
seqBits= 8 maxSeq= 255 => 247,804 IDs/sec drift=1
|
|
332
|
+
seqBits=10 maxSeq= 1023 => 1,008,930 IDs/sec drift=1
|
|
333
|
+
seqBits=14 maxSeq=16383 => 4,130,701 IDs/sec drift=0
|
|
334
|
+
|
|
335
|
+
────────────────────────────────────────────────────────────
|
|
336
|
+
6. Memory footprint
|
|
337
|
+
────────────────────────────────────────────────────────────
|
|
338
|
+
Generated: 1,000,000 IDs (not stored)
|
|
339
|
+
Heap delta: -6.27 MB
|
|
340
|
+
Note: Run with --expose-gc for accurate GC-forced measurement
|
|
341
|
+
|
|
342
|
+
────────────────────────────────────────────────────────────
|
|
343
|
+
Summary — Recommended test thresholds
|
|
344
|
+
────────────────────────────────────────────────────────────
|
|
345
|
+
Peak single-call: 75,332 IDs/sec
|
|
346
|
+
Peak batch: 61,716 IDs/sec
|
|
347
|
+
Suggested min threshold: 45,199 IDs/sec (60% of peak)
|
|
348
|
+
Suggested batch threshold: 37,029 IDs/sec (60% of peak)
|
|
349
|
+
Suggested P99 cap: 2µs (3× measured P99)
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
</details>
|
|
353
|
+
|
|
354
|
+
## Development
|
|
355
|
+
|
|
356
|
+
```bash
|
|
357
|
+
pnpm install # Install dependencies
|
|
358
|
+
pnpm run build # Build (ESM + CJS)
|
|
359
|
+
pnpm run dev # Watch mode
|
|
360
|
+
pnpm run test # Run tests
|
|
361
|
+
pnpm run benchmark # Run environment capability probe
|
|
362
|
+
pnpm run typecheck # Type check
|
|
363
|
+
pnpm run lint # Lint (Biome)
|
|
364
|
+
pnpm run format # Format (Biome)
|
|
365
|
+
```
|
|
344
366
|
|
|
345
|
-
##
|
|
367
|
+
## Notes
|
|
346
368
|
|
|
347
|
-
-
|
|
348
|
-
-
|
|
349
|
-
-
|
|
350
|
-
-
|
|
369
|
+
- Each worker/process must use a **unique workerId**
|
|
370
|
+
- Instances are **not thread-safe** — do not share across threads
|
|
371
|
+
- `workerIdBitLength + seqBitLength` must not exceed 22
|
|
372
|
+
- Sequence values 0-4 are reserved for clock-rollback handling
|
|
373
|
+
- When IDs exceed the JavaScript safe integer range (2^53-1), use `nextBigId()` or `nextId()` (auto-returns BigInt)
|
|
351
374
|
|
|
352
|
-
##
|
|
375
|
+
## License
|
|
353
376
|
|
|
354
377
|
[MIT](./LICENSE) License © 2025-PRESENT [wudi](https://github.com/WuChenDi)
|