@fluojs/cron 1.0.0-beta.5 → 1.0.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ko.md CHANGED
@@ -35,6 +35,7 @@ npm install @fluojs/cron croner
35
35
  `CronModule`을 등록하고 데코레이터를 사용하여 메서드를 스케줄링합니다.
36
36
 
37
37
  애플리케이션 모듈의 스케줄링 등록은 `CronModule.forRoot(...)`로 구성합니다.
38
+ Cron 표현식은 다섯 필드(`minute hour day month weekday`) 또는 여섯 필드(`second minute hour day month weekday`)를 사용할 수 있습니다. 내장 `CronExpression` preset은 sub-minute 정밀도가 필요할 때 여섯 필드 표현식을 사용합니다. Cron task는 application bootstrap 이후에만 시작되고, 이미 시작된 registry에 동적으로 등록한 cron task는 등록 시점에 시작되며, fluo는 `timezone`과 no-overlap 보호를 scheduler에 전달해 같은 task instance가 자기 자신과 겹쳐 실행되지 않게 합니다.
38
39
 
39
40
  ```typescript
40
41
  import { Module } from '@fluojs/core';
@@ -94,6 +95,8 @@ class AppModule {}
94
95
 
95
96
  `distributed.lockTtlMs`는 `1_000ms` 이상이어야 합니다. fluo는 최소 지원 경계인 `1_000ms`를 포함해 TTL이 만료되기 전에 Redis 락을 갱신합니다.
96
97
 
98
+ 각 scheduler instance는 platform-neutral 기본 `distributed.ownerId`를 사용합니다. 배포 환경에 더 강한 stable-owner 규칙이 있을 때만 `distributed.ownerId`를 명시적으로 지정하세요. Lock release는 task 실행 뒤 `finally` 경로에서 수행됩니다. Redis release가 실패하면 fluo는 status snapshot의 local ownership을 유지하고 shutdown 중 다시 release를 시도합니다. Redis가 다른 owner의 key라고 응답하면 fencing이 이미 다른 곳으로 이동한 것이므로 local ownership을 정리합니다. Redis TTL과 renewal timing은 drift 영향을 받는 coordination primitive이지 강한 fencing token 자체는 아니므로, stale work가 위험한 long-running job은 idempotent하게 작성하고 application-level fencing을 함께 사용해야 합니다.
99
+
97
100
  ```typescript
98
101
  @Module({
99
102
  imports: [
@@ -120,10 +123,9 @@ class MultiRedisCronModule {}
120
123
  import { Inject } from '@fluojs/core';
121
124
  import { SCHEDULING_REGISTRY, type SchedulingRegistry } from '@fluojs/cron';
122
125
 
126
+ @Inject(SCHEDULING_REGISTRY)
123
127
  class TaskManager {
124
- constructor(
125
- @Inject(SCHEDULING_REGISTRY) private readonly registry: SchedulingRegistry
126
- ) {}
128
+ constructor(private readonly registry: SchedulingRegistry) {}
127
129
 
128
130
  addNewTask() {
129
131
  this.registry.addCron('dynamic-job', '0 * * * *', () => {
package/README.md CHANGED
@@ -35,6 +35,7 @@ npm install @fluojs/cron croner
35
35
  Register the `CronModule` and use decorators to schedule your methods.
36
36
 
37
37
  Use `CronModule.forRoot(...)` to register scheduling for an application module.
38
+ Cron expressions may use either five fields (`minute hour day month weekday`) or six fields (`second minute hour day month weekday`). The built-in `CronExpression` presets use six-field expressions when sub-minute precision is needed. Cron tasks start only after application bootstrap, dynamically registered cron tasks start when added to a started registry, and fluo forwards `timezone` plus no-overlap protection to the scheduler so one task instance does not overlap itself.
38
39
 
39
40
  ```typescript
40
41
  import { Module } from '@fluojs/core';
@@ -94,6 +95,8 @@ Leave `distributed.clientName` unset to keep using the default Redis registratio
94
95
 
95
96
  `distributed.lockTtlMs` must stay at or above `1_000ms`. fluo renews the Redis lock before that TTL expires, including the minimum supported `1_000ms` boundary.
96
97
 
98
+ Each scheduler instance uses a platform-neutral default `distributed.ownerId`; set `distributed.ownerId` explicitly only when your deployment has a stronger stable-owner convention. Lock release runs in a `finally` path after task execution. If Redis release fails, fluo keeps local ownership in status snapshots and retries during shutdown; if Redis reports that another owner holds the key, local ownership is cleared because fencing has already moved elsewhere. Redis TTL and renewal timing are still drift-sensitive coordination primitives rather than hard fencing tokens, so long-running jobs should remain idempotent and use application-level fencing when stale work would be unsafe.
99
+
97
100
  ```typescript
98
101
  @Module({
99
102
  imports: [
@@ -120,10 +123,9 @@ You can manage tasks at runtime using the `SCHEDULING_REGISTRY`.
120
123
  import { Inject } from '@fluojs/core';
121
124
  import { SCHEDULING_REGISTRY, type SchedulingRegistry } from '@fluojs/cron';
122
125
 
126
+ @Inject(SCHEDULING_REGISTRY)
123
127
  class TaskManager {
124
- constructor(
125
- @Inject(SCHEDULING_REGISTRY) private readonly registry: SchedulingRegistry
126
- ) {}
128
+ constructor(private readonly registry: SchedulingRegistry) {}
127
129
 
128
130
  addNewTask() {
129
131
  this.registry.addCron('dynamic-job', '0 * * * *', () => {
@@ -172,13 +172,13 @@ export class CronDistributedLockManager {
172
172
  const result = await redis.eval(RELEASE_LOCK_SCRIPT, 1, lockKey, this.options.distributed.ownerId);
173
173
  if (typeof result === 'number' && result <= 0) {
174
174
  this.logger.warn(`Distributed cron lock for ${taskName} was already released or owned by another node.`, 'CronLifecycleService');
175
+ this.ownedLockKeys.delete(lockKey);
175
176
  return;
176
177
  }
177
178
  this.logger.log(`Released distributed cron lock for ${taskName}.`, 'CronLifecycleService');
179
+ this.ownedLockKeys.delete(lockKey);
178
180
  } catch (error) {
179
181
  this.logger.error(`Failed to release distributed cron lock for ${taskName}.`, error, 'CronLifecycleService');
180
- } finally {
181
- this.ownedLockKeys.delete(lockKey);
182
182
  }
183
183
  }
184
184
  }
@@ -1 +1 @@
1
- {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAKhE,OAAO,KAAK,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAkDjF;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,iBAAsB,GAAG,2BAA2B,CAMvG;AAeD,iEAAiE;AACjE,qBAAa,UAAU;IACrB;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,iBAAsB,GAAG,UAAU;CAS5D"}
1
+ {"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAKhE,OAAO,KAAK,EAAE,iBAAiB,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAwDjF;;;;;GAKG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,GAAE,iBAAsB,GAAG,2BAA2B,CAMvG;AAeD,iEAAiE;AACjE,qBAAa,UAAU;IACrB;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,OAAO,CAAC,OAAO,GAAE,iBAAsB,GAAG,UAAU;CAS5D"}
package/dist/module.js CHANGED
@@ -4,7 +4,11 @@ import { defaultCronScheduler } from './scheduler.js';
4
4
  import { CRON_OPTIONS, SCHEDULING_REGISTRY } from './tokens.js';
5
5
  const DEFAULT_CRON_SHUTDOWN_TIMEOUT_MS = 10_000;
6
6
  function randomId() {
7
- return `${process.pid}-${Math.random().toString(36).slice(2, 10)}`;
7
+ const randomUUID = globalThis.crypto?.randomUUID;
8
+ if (randomUUID) {
9
+ return randomUUID.call(globalThis.crypto);
10
+ }
11
+ return `cron-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
8
12
  }
9
13
  function normalizeDistributedOptions(distributed) {
10
14
  if (distributed === undefined || distributed === false) {
package/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "timeout",
10
10
  "distributed-lock"
11
11
  ],
12
- "version": "1.0.0-beta.5",
12
+ "version": "1.0.0-beta.6",
13
13
  "private": false,
14
14
  "license": "MIT",
15
15
  "repository": {
@@ -37,10 +37,10 @@
37
37
  ],
38
38
  "dependencies": {
39
39
  "croner": "^8.1.2",
40
- "@fluojs/di": "^1.0.0-beta.6",
41
- "@fluojs/redis": "^1.0.0-beta.3",
42
- "@fluojs/core": "^1.0.0-beta.4",
43
- "@fluojs/runtime": "^1.0.0-beta.11"
40
+ "@fluojs/core": "^1.0.0-beta.5",
41
+ "@fluojs/di": "^1.0.0-beta.7",
42
+ "@fluojs/redis": "^1.0.0-beta.4",
43
+ "@fluojs/runtime": "^1.0.0-beta.12"
44
44
  },
45
45
  "devDependencies": {
46
46
  "vitest": "^3.2.4"