@fluojs/cron
Advanced tools
@@ -20,2 +20,3 @@ import type { Container } from '@fluojs/di'; | ||
| private readonly ownedLockKeys; | ||
| private lockIoError; | ||
| private redisClient; | ||
@@ -27,2 +28,3 @@ private lockOwnershipLosses; | ||
| get ownedLocks(): number; | ||
| get lockIoAvailable(): boolean; | ||
| get ownershipLosses(): number; | ||
@@ -34,3 +36,3 @@ get renewalFailures(): number; | ||
| startLockRenewalMonitor(descriptor: CronTaskDescriptor): LockRenewalMonitor; | ||
| releaseLock(descriptor: CronTaskDescriptor): Promise<void>; | ||
| releaseLock(descriptor: CronTaskDescriptor): Promise<boolean>; | ||
| releaseOwnedLocks(excludedLockKeys?: ReadonlySet<string>): Promise<void>; | ||
@@ -43,3 +45,6 @@ private createLockRenewalState; | ||
| private releaseLockKey; | ||
| private verifyLockIoAvailability; | ||
| private markLockIoAvailable; | ||
| private markLockIoUnavailable; | ||
| } | ||
| //# sourceMappingURL=distributed-lock-manager.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"distributed-lock-manager.d.ts","sourceRoot":"","sources":["../src/distributed-lock-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAElF,yEAAyE;AACzE,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrF,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;CAC7G;AAED,mEAAmE;AACnE,MAAM,WAAW,kBAAkB;IACjC,eAAe,IAAI,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;IAC9C,IAAI,IAAI,IAAI,CAAC;CACd;AAoDD,yFAAyF;AACzF,qBAAa,0BAA0B;IAOnC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM;IARzB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,mBAAmB,CAAK;gBAGb,OAAO,EAAE,2BAA2B,EACpC,gBAAgB,EAAE,SAAS,EAC3B,MAAM,EAAE,iBAAiB;IAG5C,IAAI,cAAc,IAAI,eAAe,GAAG,SAAS,CAEhD;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED,IAAI,eAAe,IAAI,MAAM,CAE5B;IAEK,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAqBpC,KAAK,IAAI,IAAI;IAIP,cAAc,CAAC,UAAU,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;IA+BtE,uBAAuB,CAAC,UAAU,EAAE,kBAAkB,GAAG,kBAAkB;IA8BrE,WAAW,CAAC,UAAU,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1D,iBAAiB,CAAC,gBAAgB,GAAE,WAAW,CAAC,MAAM,CAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBzF,OAAO,CAAC,sBAAsB;IAY9B,OAAO,CAAC,2BAA2B;YAcrB,qBAAqB;IAqBnC,OAAO,CAAC,kBAAkB;YAYZ,SAAS;YAwCT,cAAc;CAgC7B"} | ||
| {"version":3,"file":"distributed-lock-manager.d.ts","sourceRoot":"","sources":["../src/distributed-lock-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAElF,yEAAyE;AACzE,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACrF,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;CAC7G;AAED,mEAAmE;AACnE,MAAM,WAAW,kBAAkB;IACjC,eAAe,IAAI,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC;IAC9C,IAAI,IAAI,IAAI,CAAC;CACd;AAoDD,yFAAyF;AACzF,qBAAa,0BAA0B;IAQnC,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM;IATzB,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqB;IACnD,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,WAAW,CAA8B;IACjD,OAAO,CAAC,mBAAmB,CAAK;IAChC,OAAO,CAAC,mBAAmB,CAAK;gBAGb,OAAO,EAAE,2BAA2B,EACpC,gBAAgB,EAAE,SAAS,EAC3B,MAAM,EAAE,iBAAiB;IAG5C,IAAI,cAAc,IAAI,eAAe,GAAG,SAAS,CAEhD;IAED,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,IAAI,eAAe,IAAI,OAAO,CAM7B;IAED,IAAI,eAAe,IAAI,MAAM,CAE5B;IAED,IAAI,eAAe,IAAI,MAAM,CAE5B;IAEK,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC;IAsBpC,KAAK,IAAI,IAAI;IAKP,cAAc,CAAC,UAAU,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;IAkCtE,uBAAuB,CAAC,UAAU,EAAE,kBAAkB,GAAG,kBAAkB;IA8BrE,WAAW,CAAC,UAAU,EAAE,kBAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;IAI7D,iBAAiB,CAAC,gBAAgB,GAAE,WAAW,CAAC,MAAM,CAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBzF,OAAO,CAAC,sBAAsB;IAY9B,OAAO,CAAC,2BAA2B;YAcrB,qBAAqB;IAqBnC,OAAO,CAAC,kBAAkB;YAYZ,SAAS;YA2CT,cAAc;YAsCd,wBAAwB;IAmBtC,OAAO,CAAC,mBAAmB;IAI3B,OAAO,CAAC,qBAAqB;CAG9B"} |
@@ -33,2 +33,3 @@ /** Minimal Redis command surface required for distributed cron locks. */ | ||
| ownedLockKeys = new Set(); | ||
| lockIoError; | ||
| redisClient; | ||
@@ -48,2 +49,8 @@ lockOwnershipLosses = 0; | ||
| } | ||
| get lockIoAvailable() { | ||
| if (!this.options.distributed.enabled) { | ||
| return true; | ||
| } | ||
| return this.redisClient !== undefined && this.lockIoError === undefined; | ||
| } | ||
| get ownershipLosses() { | ||
@@ -71,4 +78,6 @@ return this.lockOwnershipLosses; | ||
| this.redisClient = redisClient; | ||
| await this.verifyLockIoAvailability(); | ||
| } | ||
| reset() { | ||
| this.lockIoError = undefined; | ||
| this.redisClient = undefined; | ||
@@ -83,2 +92,3 @@ } | ||
| const result = await redis.set(descriptor.lockKey, this.options.distributed.ownerId, 'PX', descriptor.lockTtlMs, 'NX'); | ||
| this.markLockIoAvailable(); | ||
| if (result === 'OK') { | ||
@@ -89,2 +99,3 @@ this.ownedLockKeys.add(descriptor.lockKey); | ||
| } catch (error) { | ||
| this.markLockIoUnavailable(error); | ||
| this.logger.error(`Failed to acquire distributed cron lock for ${descriptor.taskName}.`, error, 'CronLifecycleService'); | ||
@@ -121,3 +132,3 @@ return false; | ||
| async releaseLock(descriptor) { | ||
| await this.releaseLockKey(descriptor.lockKey, descriptor.taskName); | ||
| return await this.releaseLockKey(descriptor.lockKey, descriptor.taskName); | ||
| } | ||
@@ -185,8 +196,11 @@ async releaseOwnedLocks(excludedLockKeys = new Set()) { | ||
| if (typeof result === 'number' && result <= 0) { | ||
| this.markLockIoAvailable(); | ||
| this.logger.warn(`Distributed cron lock ownership was lost for ${descriptor.taskName}.`, 'CronLifecycleService'); | ||
| return 'ownership-lost'; | ||
| } | ||
| this.markLockIoAvailable(); | ||
| this.logger.log(`Renewed distributed cron lock for ${descriptor.taskName}.`, 'CronLifecycleService'); | ||
| return 'renewed'; | ||
| } catch (error) { | ||
| this.markLockIoUnavailable(error); | ||
| this.logger.error(`Failed to renew distributed cron lock for ${descriptor.taskName}.`, error, 'CronLifecycleService'); | ||
@@ -199,3 +213,3 @@ return 'renewal-failed'; | ||
| if (!redis) { | ||
| return; | ||
| return true; | ||
| } | ||
@@ -205,12 +219,38 @@ try { | ||
| if (typeof result === 'number' && result <= 0) { | ||
| this.markLockIoAvailable(); | ||
| this.logger.warn(`Distributed cron lock for ${taskName} was already released or owned by another node.`, 'CronLifecycleService'); | ||
| this.ownedLockKeys.delete(lockKey); | ||
| return; | ||
| return true; | ||
| } | ||
| this.markLockIoAvailable(); | ||
| this.logger.log(`Released distributed cron lock for ${taskName}.`, 'CronLifecycleService'); | ||
| this.ownedLockKeys.delete(lockKey); | ||
| return true; | ||
| } catch (error) { | ||
| this.markLockIoUnavailable(error); | ||
| this.logger.error(`Failed to release distributed cron lock for ${taskName}.`, error, 'CronLifecycleService'); | ||
| return false; | ||
| } | ||
| } | ||
| async verifyLockIoAvailability() { | ||
| const redis = this.redisClient; | ||
| if (!redis) { | ||
| return; | ||
| } | ||
| const probeKey = `${this.options.distributed.keyPrefix}:__probe:${this.options.distributed.ownerId}`; | ||
| try { | ||
| await redis.set(probeKey, this.options.distributed.ownerId, 'PX', 1_000, 'NX'); | ||
| await redis.eval(RELEASE_LOCK_SCRIPT, 1, probeKey, this.options.distributed.ownerId); | ||
| this.markLockIoAvailable(); | ||
| } catch (error) { | ||
| this.markLockIoUnavailable(error); | ||
| throw new Error('Cron distributed mode requires Redis lock I/O to be available.'); | ||
| } | ||
| } | ||
| markLockIoAvailable() { | ||
| this.lockIoError = undefined; | ||
| } | ||
| markLockIoUnavailable(error) { | ||
| this.lockIoError = error instanceof Error ? error : new Error('Redis lock I/O failed.'); | ||
| } | ||
| } | ||
@@ -217,0 +257,0 @@ function hasRedisLockClient(value) { |
@@ -93,2 +93,9 @@ import type { Container } from '@fluojs/di'; | ||
| updateCronExpression(name: string, expression: string): void; | ||
| /** | ||
| * Replaces the millisecond cadence of one existing interval task. | ||
| * | ||
| * @param name Name of the interval task to update. | ||
| * @param ms New positive interval in milliseconds. | ||
| */ | ||
| updateIntervalMs(name: string, ms: number): void; | ||
| onApplicationBootstrap(): Promise<void>; | ||
@@ -100,2 +107,3 @@ onApplicationShutdown(): Promise<void>; | ||
| private shutdown; | ||
| private retryReleasedDistributedLocksAfterShutdown; | ||
| private startLifecycle; | ||
@@ -102,0 +110,0 @@ private validateDistributedLockConfiguration; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"} |
+65
-11
@@ -8,4 +8,4 @@ let _initClass; | ||
| import { Inject } from '@fluojs/core'; | ||
| import { APPLICATION_LOGGER, COMPILED_MODULES, RUNTIME_CONTAINER } from '@fluojs/runtime/internal'; | ||
| import { Cron as CronValidator } from 'croner'; | ||
| import { APPLICATION_LOGGER, COMPILED_MODULES, RUNTIME_CONTAINER } from '@fluojs/runtime/internal'; | ||
| import { CronDistributedLockManager } from './distributed-lock-manager.js'; | ||
@@ -26,2 +26,10 @@ import { createCronPlatformStatusSnapshot } from './status.js'; | ||
| } | ||
| function resolveDynamicTaskName(name, optionName) { | ||
| assertValidTaskName(name); | ||
| if (optionName !== undefined) { | ||
| assertValidTaskName(optionName); | ||
| return optionName; | ||
| } | ||
| return name; | ||
| } | ||
| function assertValidMs(ms, context) { | ||
@@ -90,4 +98,4 @@ if (!Number.isFinite(ms) || !Number.isInteger(ms) || ms <= 0) { | ||
| addCron(name, expression, callback, options = {}) { | ||
| assertValidTaskName(name); | ||
| assertValidCronExpression(expression); | ||
| const taskName = resolveDynamicTaskName(name, options.name); | ||
| this.registerTask({ | ||
@@ -100,7 +108,7 @@ afterRun: options.afterRun, | ||
| kind: 'cron', | ||
| lockKey: createLockKey(this.options.distributed.keyPrefix, options.key ?? name), | ||
| lockKey: createLockKey(this.options.distributed.keyPrefix, options.key ?? taskName), | ||
| lockTtlMs: options.lockTtlMs ?? this.options.distributed.lockTtlMs, | ||
| onError: options.onError, | ||
| onSuccess: options.onSuccess, | ||
| taskName: name, | ||
| taskName, | ||
| timezone: options.timezone | ||
@@ -119,4 +127,4 @@ }, 'dynamic'); | ||
| addInterval(name, ms, callback, options = {}) { | ||
| assertValidTaskName(name); | ||
| assertValidMs(ms, 'scheduling registry'); | ||
| const taskName = resolveDynamicTaskName(name, options.name); | ||
| this.registerTask({ | ||
@@ -128,3 +136,3 @@ afterRun: options.afterRun, | ||
| kind: 'interval', | ||
| lockKey: createLockKey(this.options.distributed.keyPrefix, options.key ?? name), | ||
| lockKey: createLockKey(this.options.distributed.keyPrefix, options.key ?? taskName), | ||
| lockTtlMs: options.lockTtlMs ?? this.options.distributed.lockTtlMs, | ||
@@ -134,3 +142,3 @@ ms, | ||
| onSuccess: options.onSuccess, | ||
| taskName: name | ||
| taskName | ||
| }, 'dynamic'); | ||
@@ -148,4 +156,4 @@ } | ||
| addTimeout(name, ms, callback, options = {}) { | ||
| assertValidTaskName(name); | ||
| assertValidMs(ms, 'scheduling registry'); | ||
| const taskName = resolveDynamicTaskName(name, options.name); | ||
| this.registerTask({ | ||
@@ -157,3 +165,3 @@ afterRun: options.afterRun, | ||
| kind: 'timeout', | ||
| lockKey: createLockKey(this.options.distributed.keyPrefix, options.key ?? name), | ||
| lockKey: createLockKey(this.options.distributed.keyPrefix, options.key ?? taskName), | ||
| lockTtlMs: options.lockTtlMs ?? this.options.distributed.lockTtlMs, | ||
@@ -163,3 +171,3 @@ ms, | ||
| onSuccess: options.onSuccess, | ||
| taskName: name | ||
| taskName | ||
| }, 'dynamic'); | ||
@@ -285,2 +293,37 @@ } | ||
| } | ||
| /** | ||
| * Replaces the millisecond cadence of one existing interval task. | ||
| * | ||
| * @param name Name of the interval task to update. | ||
| * @param ms New positive interval in milliseconds. | ||
| */ | ||
| updateIntervalMs(name, ms) { | ||
| assertValidMs(ms, 'scheduling registry'); | ||
| const task = this.tasks.get(name); | ||
| if (!task) { | ||
| throw new Error(`Scheduling task "${name}" does not exist.`); | ||
| } | ||
| if (task.descriptor.kind !== 'interval') { | ||
| throw new Error(`updateIntervalMs() supports only interval tasks. Received ${task.descriptor.kind} task "${name}".`); | ||
| } | ||
| if (!task.enabled || !this.started) { | ||
| task.descriptor.ms = ms; | ||
| return; | ||
| } | ||
| const previousMs = task.descriptor.ms; | ||
| const previousHandle = task.scheduledHandle; | ||
| task.descriptor.ms = ms; | ||
| try { | ||
| const nextHandle = this.createScheduledHandle(task); | ||
| task.scheduledHandle = nextHandle; | ||
| if (previousHandle) { | ||
| this.stopScheduledHandle(previousHandle); | ||
| } | ||
| } catch (error) { | ||
| task.descriptor.ms = previousMs; | ||
| task.scheduledHandle = previousHandle; | ||
| throw error; | ||
| } | ||
| } | ||
| async onApplicationBootstrap() { | ||
@@ -327,2 +370,3 @@ if (this.started) { | ||
| redisDependencyResolved: this.distributedLocks.resolvedClient !== undefined, | ||
| redisLockIoAvailable: this.distributedLocks.lockIoAvailable, | ||
| runningTasks, | ||
@@ -352,2 +396,3 @@ totalTasks: this.tasks.size | ||
| await this.shutdownPromise; | ||
| await this.retryReleasedDistributedLocksAfterShutdown(); | ||
| return; | ||
@@ -358,2 +403,8 @@ } | ||
| } | ||
| async retryReleasedDistributedLocksAfterShutdown() { | ||
| if (this.lifecycleState !== 'stopped' || this.activeTasks.size > 0) { | ||
| return; | ||
| } | ||
| await this.distributedLocks.releaseOwnedLocks(); | ||
| } | ||
| async startLifecycle() { | ||
@@ -535,4 +586,7 @@ await this.distributedLocks.resolveClient(); | ||
| lockRenewalMonitor.stop(); | ||
| await this.distributedLocks.releaseLock(descriptor); | ||
| this.runningDistributedLockKeys.delete(descriptor.lockKey); | ||
| const released = await this.distributedLocks.releaseLock(descriptor); | ||
| if (!released && this.lifecycleState === 'stopped') { | ||
| await this.distributedLocks.releaseOwnedLocks(); | ||
| } | ||
| } | ||
@@ -539,0 +593,0 @@ } |
+1
-0
@@ -15,2 +15,3 @@ import type { PlatformHealthReport, PlatformReadinessReport, PlatformSnapshot } from '@fluojs/runtime'; | ||
| redisDependencyResolved: boolean; | ||
| redisLockIoAvailable?: boolean; | ||
| runningTasks: number; | ||
@@ -17,0 +18,0 @@ totalTasks: number; |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEvG,qEAAqE;AACrE,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEtG,mFAAmF;AACnF,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,kBAAkB,CAAC;IACnC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,uBAAuB,EAAE,OAAO,CAAC;IACjC,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qFAAqF;AACrF,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,uBAAuB,CAAC;IACnC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,SAAS,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACzC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAoFD;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,sBAAsB,GAAG,0BAA0B,CAsB1G"} | ||
| {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../src/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEvG,qEAAqE;AACrE,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEtG,mFAAmF;AACnF,MAAM,WAAW,sBAAsB;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,kBAAkB,CAAC;IACnC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,mBAAmB,EAAE,MAAM,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,uBAAuB,EAAE,OAAO,CAAC;IACjC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qFAAqF;AACrF,MAAM,WAAW,0BAA0B;IACzC,SAAS,EAAE,uBAAuB,CAAC;IACnC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,SAAS,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAC;IACzC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAClC;AAuGD;;;;;GAKG;AACH,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,sBAAsB,GAAG,0BAA0B,CAyB1G"} |
+17
-1
@@ -8,4 +8,5 @@ /** Lifecycle phases reported by the cron platform status adapter. */ | ||
| function createReadiness(input) { | ||
| const redisLockIoAvailable = resolveRedisLockIoAvailable(input); | ||
| if (input.lifecycleState === 'ready') { | ||
| if (input.distributedEnabled && !input.redisDependencyResolved) { | ||
| if (input.distributedEnabled && (!input.redisDependencyResolved || !redisLockIoAvailable)) { | ||
| return { | ||
@@ -57,2 +58,3 @@ critical: true, | ||
| function createHealth(input) { | ||
| const redisLockIoAvailable = resolveRedisLockIoAvailable(input); | ||
| if (input.lifecycleState === 'failed' || input.lifecycleState === 'stopped') { | ||
@@ -70,2 +72,8 @@ return { | ||
| } | ||
| if (input.distributedEnabled && (!input.redisDependencyResolved || !redisLockIoAvailable)) { | ||
| return { | ||
| reason: 'Distributed cron Redis lock I/O is unavailable.', | ||
| status: 'unhealthy' | ||
| }; | ||
| } | ||
| if (input.lockRenewalFailures > 0 || input.lockOwnershipLosses > 0) { | ||
@@ -81,2 +89,8 @@ return { | ||
| } | ||
| function resolveRedisLockIoAvailable(input) { | ||
| if (!input.distributedEnabled) { | ||
| return true; | ||
| } | ||
| return input.redisLockIoAvailable ?? input.redisDependencyResolved; | ||
| } | ||
@@ -90,2 +104,3 @@ /** | ||
| export function createCronPlatformStatusSnapshot(input) { | ||
| const redisLockIoAvailable = resolveRedisLockIoAvailable(input); | ||
| return { | ||
@@ -102,2 +117,3 @@ details: { | ||
| redisDependencyResolved: input.redisDependencyResolved, | ||
| redisLockIoAvailable, | ||
| runningTasks: input.runningTasks, | ||
@@ -104,0 +120,0 @@ totalTasks: input.totalTasks |
+7
-0
@@ -207,3 +207,10 @@ import type { MetadataPropertyKey, Token } from '@fluojs/core'; | ||
| updateCronExpression(name: string, expression: string): void; | ||
| /** | ||
| * Replaces the millisecond cadence for an existing interval task. | ||
| * | ||
| * @param name Task name to update. | ||
| * @param ms New positive interval in milliseconds. | ||
| */ | ||
| updateIntervalMs(name: string, ms: number): void; | ||
| } | ||
| //# sourceMappingURL=types.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"} |
+5
-5
@@ -12,3 +12,3 @@ { | ||
| ], | ||
| "version": "1.0.3", | ||
| "version": "1.1.0", | ||
| "private": false, | ||
@@ -42,7 +42,7 @@ "license": "MIT", | ||
| "@fluojs/core": "^1.0.3", | ||
| "@fluojs/runtime": "^1.1.7", | ||
| "@fluojs/di": "^1.1.0" | ||
| "@fluojs/di": "^1.1.0", | ||
| "@fluojs/runtime": "^1.1.8" | ||
| }, | ||
| "peerDependencies": { | ||
| "@fluojs/redis": "^1.0.1" | ||
| "@fluojs/redis": "^1.0.2" | ||
| }, | ||
@@ -56,3 +56,3 @@ "peerDependenciesMeta": { | ||
| "vitest": "^3.2.4", | ||
| "@fluojs/redis": "^1.0.1" | ||
| "@fluojs/redis": "^1.0.2" | ||
| }, | ||
@@ -59,0 +59,0 @@ "scripts": { |
+8
-2
@@ -44,2 +44,4 @@ # @fluojs/cron | ||
| Scheduling decorator는 public instance method에만 적용됩니다. NestJS에서 사용하던 private scheduled method, static helper, legacy decorator metadata 가정 뒤에 숨은 method name을 그대로 옮기지 마세요. 공개 provider/controller method를 노출하고 private 구현 세부사항은 그 method 뒤에 두세요. | ||
| ```typescript | ||
@@ -141,2 +143,6 @@ import { Module } from '@fluojs/core'; | ||
| speedUpPolling() { | ||
| this.registry.updateIntervalMs('inventory.poll', 5_000); | ||
| } | ||
| stopTask() { | ||
@@ -148,5 +154,5 @@ this.registry.remove('dynamic-job'); | ||
| Registry는 `addCron`, `addInterval`, `addTimeout`, `remove`, `enable`, `disable`, `get`, `getAll`, `updateCronExpression`을 제공합니다. Timeout task는 한 번 실행된 뒤 비활성화되지만 registry에는 남아 있어 의도적으로 다시 활성화할 수 있습니다. | ||
| Registry는 `addCron`, `addInterval`, `addTimeout`, `remove`, `enable`, `disable`, `get`, `getAll`, `updateCronExpression`, `updateIntervalMs`를 제공합니다. 첫 번째 `name` 인자는 기본 registry key이며, `options.name`을 전달하면 dynamic task의 실제 registry key, scheduler metadata name, 기본 distributed lock key가 이를 사용해 decorator naming semantics와 일치합니다. `get`과 `getAll`은 live `CronJob` handle이 아니라 read-only `SchedulingTaskDescriptor` 값을 반환합니다. Timeout task는 한 번 실행된 뒤 비활성화되지만 registry에는 남아 있어 의도적으로 다시 활성화할 수 있습니다. | ||
| Dynamic cron 등록은 scheduler startup과 원자적으로 처리됩니다. Scheduler가 새 cron job을 거부하면 registry는 half-registered task를 남기지 않습니다. 실행 중인 cron expression update도 rollback-safe합니다. Rescheduling이 실패하면 이전 expression과 scheduled job이 그대로 유지됩니다. Cron task는 scheduler-level no-overlap protection과 fluo의 in-process running guard를 함께 사용하므로 같은 task instance가 overlapping tick으로 실행되지 않습니다. | ||
| Dynamic cron 등록은 scheduler startup과 원자적으로 처리됩니다. Scheduler가 새 cron job을 거부하면 registry는 half-registered task를 남기지 않습니다. 실행 중인 cron expression 또는 interval cadence update도 rollback-safe합니다. Rescheduling이 실패하면 이전 expression 또는 interval milliseconds와 scheduled handle이 그대로 유지됩니다. Cron task는 scheduler-level no-overlap protection과 fluo의 in-process running guard를 함께 사용하므로 같은 task instance가 overlapping tick으로 실행되지 않습니다. | ||
@@ -153,0 +159,0 @@ ### 제한된 종료 |
+8
-2
@@ -44,2 +44,4 @@ # @fluojs/cron | ||
| Scheduling decorators apply to public instance methods only. Do not migrate NestJS private scheduled methods, static helpers, or method names that are hidden behind legacy decorator metadata assumptions as-is; expose a public provider/controller method and keep any private implementation details behind that method. | ||
| ```typescript | ||
@@ -141,2 +143,6 @@ import { Module } from '@fluojs/core'; | ||
| speedUpPolling() { | ||
| this.registry.updateIntervalMs('inventory.poll', 5_000); | ||
| } | ||
| stopTask() { | ||
@@ -148,5 +154,5 @@ this.registry.remove('dynamic-job'); | ||
| The registry exposes `addCron`, `addInterval`, `addTimeout`, `remove`, `enable`, `disable`, `get`, `getAll`, and `updateCronExpression`. Timeout tasks run once, then disable themselves while remaining in the registry so they can be re-enabled deliberately. | ||
| The registry exposes `addCron`, `addInterval`, `addTimeout`, `remove`, `enable`, `disable`, `get`, `getAll`, `updateCronExpression`, and `updateIntervalMs`. The first `name` argument is the default registry key; passing `options.name` overrides the actual registry key, scheduler metadata name, and default distributed lock key for dynamic tasks so dynamic registration matches decorator naming semantics. `get` and `getAll` return read-only `SchedulingTaskDescriptor` values, not live `CronJob` handles. Timeout tasks run once, then disable themselves while remaining in the registry so they can be re-enabled deliberately. | ||
| Dynamic cron registration is atomic with scheduler startup: if the scheduler rejects a new cron job, the registry does not retain a half-registered task. Updating a running cron expression is also rollback-safe. If rescheduling fails, the previous expression and scheduled job remain active. Cron tasks use both scheduler-level no-overlap protection and fluo's in-process running guard, so the same task instance will not run overlapping ticks. | ||
| Dynamic cron registration is atomic with scheduler startup: if the scheduler rejects a new cron job, the registry does not retain a half-registered task. Updating a running cron expression or interval cadence is also rollback-safe. If rescheduling fails, the previous expression or interval milliseconds and scheduled handle remain active. Cron tasks use both scheduler-level no-overlap protection and fluo's in-process running guard, so the same task instance will not run overlapping ticks. | ||
@@ -153,0 +159,0 @@ ### Bounded Shutdown |
120178
5.93%2213
6.24%210
2.94%4
33.33%Updated