UNPKG

koatty_schedule

Version:
420 lines (399 loc) 11.8 kB
/*! * @Author: richen * @Date: 2026-04-24 16:20:40 * @License: BSD (3-Clause) * @Copyright (c) - <richenlin(at)gmail.com> * @HomePage: https://koatty.org/ */ import { Cluster } from 'ioredis'; import { Koatty } from 'koatty_core'; import { Lock } from '@sesamecare-oss/redlock'; import Redis from 'ioredis'; import type { Settings } from '@sesamecare-oss/redlock'; /** * Base Redis configuration */ declare interface BaseRedisConfig { mode?: RedisMode; host?: string; port?: number; password?: string; db?: number; keyPrefix?: string; connectTimeout?: number; commandTimeout?: number; maxRetriesPerRequest?: number; } /** * Abstract distributed lock interface * Allows for different lock implementations (RedLock, Zookeeper, etc.) */ export declare interface IDistributedLock { /** * Initialize the lock system */ initialize(): Promise<void>; /** * Acquire a distributed lock * @param resources - Resource identifiers * @param ttl - Time to live in milliseconds */ acquire(resources: string[], ttl: number): Promise<Lock>; /** * Release a lock * @param lock - Lock instance */ release(lock: Lock): Promise<void>; /** * Extend lock TTL * @param lock - Lock instance * @param ttl - New TTL in milliseconds */ extend(lock: Lock, ttl: number): Promise<Lock>; /** * Check if the lock system is ready */ isReady(): boolean; /** * Get current configuration */ getConfig(): any; /** * Close and cleanup */ close(): Promise<void>; /** * Health check */ healthCheck(): Promise<{ status: 'healthy' | 'unhealthy'; details: Record<string, any>; }>; } /** * Lock configuration options */ export declare interface ILockOptions extends Partial<Settings> { lockTimeOut?: number; clockDriftFactor?: number; maxRetries?: number; retryDelayMs?: number; redisConfig?: RedisConfig; } /** * Abstract Redis client interface * Provides unified interface for different Redis implementations */ export declare interface IRedisClient { /** * Get connection status */ readonly status: string; /** * Execute Redis command * @param command - Command name * @param args - Command arguments */ call(command: string, ...args: any[]): Promise<any>; /** * Set a key-value pair * @param key - Key name * @param value - Value * @param mode - Optional mode (e.g., 'EX' for expiration) * @param duration - Optional duration in seconds */ set(key: string, value: string | Buffer, mode?: string, duration?: number): Promise<'OK' | null>; /** * Get value by key * @param key - Key name */ get(key: string): Promise<string | null>; /** * Delete one or more keys * @param keys - Key names */ del(...keys: string[]): Promise<number>; /** * Check if key exists * @param key - Key name */ exists(key: string): Promise<number>; /** * Evaluate Lua script * @param script - Lua script * @param numKeys - Number of keys * @param args - Script arguments */ eval(script: string, numKeys: number, ...args: any[]): Promise<any>; /** * Close the connection */ quit(): Promise<'OK'>; /** * Disconnect immediately */ disconnect(): void; } /** * @param options - The options for the scheduled job * @param app - The Koatty application instance */ export declare function KoattyScheduled(options: ScheduledOptions, app: Koatty): Promise<void>; /** * Redis client wrapper that implements IRedisClient interface * Wraps ioredis client to provide unified interface */ export declare class RedisClientAdapter implements IRedisClient { constructor(client: Redis | Cluster); get status(): string; call(command: string, ...args: any[]): Promise<any>; set(key: string, value: string | Buffer, mode?: string, duration?: number): Promise<'OK' | null>; get(key: string): Promise<string | null>; del(...keys: string[]): Promise<number>; exists(key: string): Promise<number>; eval(script: string, numKeys: number, ...args: any[]): Promise<any>; quit(): Promise<'OK'>; disconnect(): void; /** * Get underlying Redis/Cluster instance * Used for RedLock initialization */ getClient(): Redis | Cluster; } /** * Cluster configuration */ export declare interface RedisClusterConfig extends BaseRedisConfig { mode: RedisMode.CLUSTER; nodes: Array<{ host: string; port: number; }>; redisOptions?: { password?: string; db?: number; }; } /** * Union type for all Redis configurations */ export declare type RedisConfig = RedisStandaloneConfig | RedisSentinelConfig | RedisClusterConfig; /** * Redis client factory * Creates appropriate Redis client based on configuration */ export declare class RedisFactory { /** * Create Redis client based on configuration mode * @param config - Redis configuration * @returns Redis client adapter */ static createClient(config: RedisConfig): RedisClientAdapter; /** * Create standalone Redis client * @param config - Standalone configuration */ /** * Create sentinel Redis client * @param config - Sentinel configuration */ /** * Create cluster Redis client * @param config - Cluster configuration */ /** * Validate Redis configuration * @param config - Redis configuration to validate */ static validateConfig(config: RedisConfig): void; } /** * Redis connection mode */ export declare enum RedisMode { STANDALONE = "standalone",// 单机模式 SENTINEL = "sentinel",// 哨兵模式 CLUSTER = "cluster" } /** * Sentinel configuration */ export declare interface RedisSentinelConfig extends BaseRedisConfig { mode: RedisMode.SENTINEL; sentinels: Array<{ host: string; port: number; }>; name: string; sentinelPassword?: string; } /** * Standalone configuration */ export declare interface RedisStandaloneConfig extends BaseRedisConfig { mode?: RedisMode.STANDALONE; host: string; port: number; } /** * Redis-based distributed lock decorator * * @export * @param {string} [name] - The locker name. If name is duplicated, lock sharing contention will result. * If not provided, a unique name will be auto-generated using method name + random suffix. * IMPORTANT: Auto-generated names are unique per method deployment and not predictable. * @param {RedLockMethodOptions} [options] - Lock configuration options for this method * * @returns {MethodDecorator} * @throws {Error} When decorator is used on wrong class type or invalid configuration * * @example * ```typescript * class UserService { * @RedLock('user_update_lock', { lockTimeOut: 5000, maxRetries: 2 }) * async updateUser(id: string, data: any) { * // This method will be protected by a distributed lock with predictable name * } * * @RedLock() // Auto-generated unique name like "deleteUser_abc123_xyz789" * async deleteUser(id: string) { * // This method will be protected by a distributed lock with auto-generated unique name * } * } * ``` */ export declare function RedLock(lockName?: string, options?: RedLockMethodOptions): (...args: any[]) => any; /** * RedLock distributed lock manager * Integrated with koatty IOC container * Implements singleton pattern for safe instance management * Implements IDistributedLock interface for abstraction */ export declare class RedLocker implements IDistributedLock { /** * Register RedLocker in IOC container * @private */ /** * Get RedLocker singleton instance with thread-safe initialization * @static * @param options - RedLock configuration options (only used for first initialization) * @returns RedLocker singleton instance */ static getInstance(options?: RedLockOptions): RedLocker; /** * Reset singleton instance (主要用于测试) * @static */ static resetInstance(): void; /** * Initialize RedLock with Redis connection * Uses cached promise to avoid duplicate initialization * @private */ initialize(): Promise<void>; /** * 执行实际的初始化操作 * @private */ /** * Acquire a distributed lock * @param resources - Resource identifiers to lock * @param ttl - Time to live in milliseconds * @returns Promise<Lock> */ acquire(resources: string[], ttl?: number): Promise<Lock>; /** * Release a lock * @param lock - Lock instance to release */ release(lock: Lock): Promise<void>; /** * Extend a lock's TTL * @param lock - Lock instance to extend * @param ttl - New TTL in milliseconds * @returns Extended lock */ extend(lock: Lock, ttl: number): Promise<Lock>; /** * Check if RedLocker is initialized * @returns true if initialized, false otherwise */ isReady(): boolean; /** * Get current configuration * @returns Current RedLock configuration */ getConfig(): RedLockOptions; /** * Update configuration (requires reinitialization) * @param options - New RedLock options */ updateConfig(options?: Partial<RedLockOptions>): void; /** * Close Redis connection and cleanup */ close(): Promise<void>; /** * Get container registration status * @returns Registration information */ getContainerInfo(): { registered: boolean; identifier: string; }; /** * Health check for RedLocker * @returns Health status */ healthCheck(): Promise<{ status: 'healthy' | 'unhealthy'; details: Record<string, any>; }>; } /** * RedLock method-level options (excluding Redis connection config) */ declare interface RedLockMethodOptions { lockTimeOut?: number; clockDriftFactor?: number; maxRetries?: number; retryDelayMs?: number; } /** * Configuration options for RedLock * @deprecated Use ILockOptions from interface instead */ export declare interface RedLockOptions extends ILockOptions { redisConfig?: RedisConfig; } /** * Schedule task decorator with optimized preprocessing * * @export * @param {string} cron - Cron expression for task scheduling * @param {string} [timezone='Asia/Beijing'] - Timezone for the schedule * * Cron expression format: * * Seconds: 0-59 * * Minutes: 0-59 * * Hours: 0-23 * * Day of Month: 1-31 * * Months: 1-12 (Jan-Dec) * * Day of Week: 1-7 (Sun-Sat) * * @returns {MethodDecorator} * @throws {Error} When cron expression is invalid or decorator is used on wrong class type */ export declare function Scheduled(cron: string, timezone?: string): (...args: any[]) => any; /** * Scheduled global options interface */ declare interface ScheduledOptions extends RedLockOptions { timezone?: string; } /** * @deprecated Use RedLock instead. This will be removed in v3.0.0 */ export declare const SchedulerLock: typeof RedLock; export { }