Skip to content

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ầnBitPhạm viMô tả
Timestamp48~8,919 nămMili giây kể từ epoch
Worker ID100--1023Định danh instance duy nhất
Sequence120--4095Bộ đế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

typescript
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:

ExportKiểuMô tả
SnowflakeUidHelperClassBộ sinh ID nền tảng
SnowflakeConfigConstantsĐộ rộng bit, giá trị tối đa, epoch mặc định
IIdGeneratorOptionsInterfaceTuỳ chọn cho constructor (workerId, epoch)
ISnowflakeParsedIdInterfaceCấu trúc ID đã phân tích (raw, timestamp, workerId, sequence)

Cấu hình

Biến môi trường

BiếnBắt buộcMặc địnhMô tả
APP_ENV_SNOWFLAKE_WORKER_ID--Worker ID (0--1023), phải duy nhất cho mỗi instance của service
APP_ENV_SNOWFLAKE_EPOCH_CHECKPOINTKhông1735689600000 (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:

bash
# 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
yaml
# docker-compose.yml
services:
  api-1:
    environment:
      - APP_ENV_SNOWFLAKE_WORKER_ID=1
  api-2:
    environment:
      - APP_ENV_SNOWFLAKE_WORKER_ID=2

Cách sử dụng

Import

typescript
import { IdGenerator } from '@nx/core';
// or
import { IdGenerator } from '@nx/core/utilities';

Sinh ID

typescript
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:

typescript
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

typescript
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();
// 1

Mã hoá và giải mã Base62

typescript
// Encode any bigint to Base62
const encoded = generator.encodeBase62(130546360012247045n);
// "9du1sJXO88"

// Decode Base62 back to bigint
const decoded = generator.decodeBase62('9du1sJXO88');
// 130546360012247045n

Cấu hình tuỳ chỉnh

typescript
// 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ứcChữ kýMô tả
nextId(): stringSinh một Snowflake ID mã hoá Base62 (10--12 ký tự)
nextSnowflake(): bigintSinh một Snowflake ID 70-bit nguyên gốc
parseId(base62Id: string): ISnowflakeParsedIdPhân tích một ID Base62 thành các thành phần
encodeBase62(num: bigint): stringMã hoá một bigint thành chuỗi Base62
decodeBase62(str: string): bigintGiải mã một chuỗi Base62 thành bigint
extractTimestamp(id: bigint): DateTrích xuất timestamp từ một Snowflake ID nguyên gốc
extractWorkerId(id: bigint): numberTrích xuất worker ID từ một Snowflake ID nguyên gốc
extractSequence(id: bigint): numberTrích xuất số sequence từ một Snowflake ID nguyên gốc
getWorkerId(): numberLấ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

typescript
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

typescript
@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

typescript
// 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ệ

typescript
// 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ệ

typescript
// 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

typescript
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

typescript
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:

Tài liệu liên quan

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.