IdGenerator
Tổng quan
Tiện ích IdGenerator là một singleton bọc SnowflakeUidHelper của IGNIS Framework. Nó cung cấp các ID phân tán, duy nhất toàn cục, sắp theo thứ tự thời gian mà không cần điều phối tập trung. Mọi service trong BANA đều dùng IdGenerator để sinh khoá chính.
Nguồn: packages/core/src/utilities/id-generator.utility.ts (81 dòng)
Cấu trúc Snowflake ID
┌──────────────────────────────────────────────────────────────────────┐
│ 70-bit Snowflake ID │
├──────────────────────────────┬──────────────┬────────────────────────┤
│ Timestamp │ Worker ID │ Sequence │
│ (48 bits) │ (10 bits) │ (12 bits) │
├──────────────────────────────┼──────────────┼────────────────────────┤
│ ~8,919 years range │ 0 - 1023 │ 0 - 4095 │
└──────────────────────────────┴──────────────┴────────────────────────┘| Thành phần | Bit | Phạm vi | Mô tả |
|---|---|---|---|
| Timestamp | 48 | ~8,919 năm | Mili giây kể từ epoch |
| Worker ID | 10 | 0--1023 | Định danh instance duy nhất |
| Sequence | 12 | 0--4095 | Bộ đếm trong mỗi mili giây |
Đầu ra là một chuỗi mã hoá Base62 gọn nhẹ (10--12 ký tự), phù hợp để làm khoá chính trong cơ sở dữ liệu và làm định danh an toàn cho URL.
Định nghĩa lớp
import { SnowflakeUidHelper } from '@venizia/ignis';
export class IdGenerator {
private static instance: SnowflakeUidHelper;
// Get or create the singleton instance
static getInstance(opts?: {
workerId?: number;
epoch?: bigint;
}): SnowflakeUidHelper;
// Reset instance (for testing only)
static resetInstance(): void;
}Re-export từ IGNIS
Module re-export các kiểu sau từ @venizia/ignis cho tiện:
| Export | Kiểu | Mô tả |
|---|---|---|
SnowflakeUidHelper | Class | Bộ sinh ID nền tảng |
SnowflakeConfig | Constants | Độ rộng bit, giá trị tối đa, epoch mặc định |
IIdGeneratorOptions | Interface | Tuỳ chọn cho constructor (workerId, epoch) |
ISnowflakeParsedId | Interface | Cấu trúc ID đã phân tích (raw, timestamp, workerId, sequence) |
Cấu hình
Biến môi trường
| Biến | Bắt buộc | Mặc định | Mô tả |
|---|---|---|---|
APP_ENV_SNOWFLAKE_WORKER_ID | Có | -- | Worker ID (0--1023), phải duy nhất cho mỗi instance của service |
APP_ENV_SNOWFLAKE_EPOCH_CHECKPOINT | Không | 1735689600000 (2025-01-01 UTC) | Mốc epoch tuỳ chỉnh tính bằng mili giây |
Gán Worker ID
Mỗi instance của service phải có một Worker ID duy nhất để tránh trùng ID:
# Development
APP_ENV_SNOWFLAKE_WORKER_ID=1
# Production (per instance)
# Instance 1: APP_ENV_SNOWFLAKE_WORKER_ID=1
# Instance 2: APP_ENV_SNOWFLAKE_WORKER_ID=2
# Instance 3: APP_ENV_SNOWFLAKE_WORKER_ID=3# docker-compose.yml
services:
api-1:
environment:
- APP_ENV_SNOWFLAKE_WORKER_ID=1
api-2:
environment:
- APP_ENV_SNOWFLAKE_WORKER_ID=2Cách sử dụng
Import
import { IdGenerator } from '@nx/core';
// or
import { IdGenerator } from '@nx/core/utilities';Sinh ID
const generator = IdGenerator.getInstance();
// Generate a Base62-encoded ID (recommended)
const id = generator.nextId();
// "9du1sJXO88"
// Generate a raw Snowflake bigint
const snowflakeId = generator.nextSnowflake();
// 130546360012247045n
// Generate multiple IDs
const ids = Array.from({ length: 10 }, () => generator.nextId());Phân tích ID
Trích xuất timestamp, worker ID và sequence được nhúng trong một ID có sẵn:
const parsed = generator.parseId('9du1sJXO88');
// {
// raw: 130546360012247045n,
// timestamp: Date, // Date object
// workerId: 1,
// sequence: 0
// }Trích xuất từng thành phần
const snowflakeId = generator.nextSnowflake();
// Extract timestamp
const timestamp = generator.extractTimestamp(snowflakeId);
// Date object
// Extract worker ID
const workerId = generator.extractWorkerId(snowflakeId);
// 1
// Extract sequence
const sequence = generator.extractSequence(snowflakeId);
// 0-4095
// Get current instance's worker ID
const currentWorkerId = generator.getWorkerId();
// 1Mã hoá và giải mã Base62
// Encode any bigint to Base62
const encoded = generator.encodeBase62(130546360012247045n);
// "9du1sJXO88"
// Decode Base62 back to bigint
const decoded = generator.decodeBase62('9du1sJXO88');
// 130546360012247045nCấu hình tuỳ chỉnh
// First call initializes with config
const generator = IdGenerator.getInstance({
workerId: 100,
epoch: BigInt(1609459200000), // Custom epoch: 2021-01-01
});
// Subsequent calls return the same instance (options are ignored)
const sameGenerator = IdGenerator.getInstance();Tóm tắt API
| Phương thức | Chữ ký | Mô tả |
|---|---|---|
nextId | (): string | Sinh một Snowflake ID mã hoá Base62 (10--12 ký tự) |
nextSnowflake | (): bigint | Sinh một Snowflake ID 70-bit nguyên gốc |
parseId | (base62Id: string): ISnowflakeParsedId | Phân tích một ID Base62 thành các thành phần |
encodeBase62 | (num: bigint): string | Mã hoá một bigint thành chuỗi Base62 |
decodeBase62 | (str: string): bigint | Giải mã một chuỗi Base62 thành bigint |
extractTimestamp | (id: bigint): Date | Trích xuất timestamp từ một Snowflake ID nguyên gốc |
extractWorkerId | (id: bigint): number | Trích xuất worker ID từ một Snowflake ID nguyên gốc |
extractSequence | (id: bigint): number | Trích xuất số sequence từ một Snowflake ID nguyên gốc |
getWorkerId | (): number | Lấy worker ID của instance hiện tại |
Luồng sinh ID phân tán
Sinh ID khối lượng lớn
Tích hợp với Model
Schema Drizzle
import { pgTable, text } from 'drizzle-orm/pg-core';
import { IdGenerator } from '@nx/core';
export const User = pgTable('User', {
id: text('id')
.primaryKey()
.$defaultFn(() => IdGenerator.getInstance().nextId()),
// ... other columns
});Sử dụng trong Repository
@repository({ dataSource: PostgresCoreDataSource, model: User })
export class UserRepository extends SoftDeletableRepository<TUserSchema, TUser> {
async createUser(data: TUserCreate): Promise<TUser> {
const id = IdGenerator.getInstance().nextId();
return this.create({
data: {
id,
...data,
createdAt: new Date(),
},
});
}
}Xử lý lỗi
Thiếu Worker ID
// APP_ENV_SNOWFLAKE_WORKER_ID is not set
try {
IdGenerator.getInstance();
} catch (error) {
// [IdGenerator][getWorkerIdFromEnv] Missing required environment variable
// APP_ENV_SNOWFLAKE_WORKER_ID | hint: Set APP_ENV_SNOWFLAKE_WORKER_ID
// in between 0 and 1023 for each service instance
}Worker ID không hợp lệ
// APP_ENV_SNOWFLAKE_WORKER_ID="abc"
try {
IdGenerator.getInstance();
} catch (error) {
// [IdGenerator][getWorkerIdFromEnv] Invalid APP_ENV_SNOWFLAKE_WORKER_ID value
// received: abc | expected: number between 0 and 1023
}Epoch không hợp lệ
// APP_ENV_SNOWFLAKE_EPOCH_CHECKPOINT="not-a-number"
try {
IdGenerator.getInstance();
} catch (error) {
// [IdGenerator][getEpochFromEnv] Invalid APP_ENV_SNOWFLAKE_EPOCH_CHECKPOINT value
// received: not-a-number | expected: timestamp in milliseconds
}Kiểm thử
Reset để có trạng thái sạch
describe('UserService', () => {
beforeEach(() => {
// Reset singleton for clean state
IdGenerator.resetInstance();
// Set test worker ID
process.env.APP_ENV_SNOWFLAKE_WORKER_ID = '999';
});
it('generates user with Snowflake ID', async () => {
const user = await userService.create({ name: 'Test' });
expect(user.id).toBeTruthy();
});
});Mock bộ sinh ID
jest.mock('@nx/core/utilities', () => ({
IdGenerator: {
getInstance: () => ({
nextId: jest.fn().mockReturnValue('mockId123'),
}),
},
}));WARNING
Không bao giờ gọi IdGenerator.resetInstance() trong production. Nó được thiết kế chỉ để cô lập các test.
Tham chiếu IGNIS Framework
IdGenerator bọc SnowflakeUidHelper của IGNIS. Để xem API đầy đủ, bao gồm xử lý lệch đồng hồ, hành vi khi cạn sequence, và toàn bộ hằng số cấu hình, xem tham chiếu IGNIS UID Helper: