koatty_schedule
Version:
Schedule for koatty.
420 lines (399 loc) • 11.8 kB
TypeScript
/*!
* @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 { }