@h-ai/scheduler 0.1.0-alpha.65 → 0.1.0-alpha.67

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.en.md CHANGED
@@ -4,30 +4,24 @@
4
4
 
5
5
  [Framework home](../../README.en.md) · Related modules: [@h-ai/cache](../cache/README.en.md) · [@h-ai/core](../core/README.en.md) · [@h-ai/reldb](../reldb/README.en.md)
6
6
 
7
- A scheduled task module with a unified task model, cron scheduling, JS function strings / HTTP API execution, persistence, execution log queries, and global task lifecycle hooks.
8
-
9
- With the database enabled, nodes refresh persisted definitions every `tickInterval` (default 1000 ms); `tasks` is the latest successful snapshot. Registration, updates, deletion, manual triggers, and each attempt after acquiring a scheduling lock reread definitions. Deleted, disabled, or rescheduled old tasks stop executing; read failures abort the attempt. Already-started business side effects are not rolled back by remote changes. `init({ tasks })` remains node-local; use persisted `register()` for dynamic tasks across nodes. Cross-node deduplication requires shared Redis; memory cache is single-process only.
10
-
11
- Call `await scheduler.refreshTasks()` to refresh without execution; concurrent refreshes of the same snapshot share one read. Registration, updates, and deletion change memory only after successful persistence. Invalid full updates leave cron unchanged. Pass `retry: null` or `handler: null` to clear retries or built-in handlers.
12
-
13
- Hook business errors are recorded as `failed` and apply `retry`; missing handlers, cancellation, or host-thrown `SchedulerTaskSkippedError` are `interrupted`. `event.context.occurrenceId` identifies an occurrence: the same scheduled minute shares it across nodes, each manual trigger is independent, and retries reuse the original. Hosts can persist idempotency receipts with this ID; long tasks spanning periods still need a host-maintained lease for mutual exclusion. `deleteAfterRun` deletes only after success. Definitions are checked before retry and successful cleanup; one database DELETE atomically matches the executed definition, preserving concurrent updates.
14
-
15
- Multi-node regression tests build current source and start two real Node processes sharing SQLite and Testcontainers Redis, validating definition convergence and same-minute lock contention. A working Docker/Podman service is required before `pnpm --filter @h-ai/scheduler test`.
7
+ ## Introduction
16
8
 
17
- `await scheduler.close()` stops task intake, cancels HTTP, JS workers, and retry waits, then awaits execution chain exit before clearing state. Reinitialization waits for closure. All lifecycle events expose `signal`; hooks must observe cancellation and stop external side effects. The framework cannot terminate business code on the same thread; do not block the event loop in hooks. Cancelled old executions no longer retry, delete tasks, or call new-instance hooks. Keep the database and cache available until scheduler closure completes, then close dependencies.
9
+ A scheduled task module with a unified task model, cron scheduling, JS function strings / HTTP API execution, persistence, execution log queries, and global task lifecycle hooks.
18
10
 
19
- Each JS string runs in its own worker; `timeout` defaults to 30000 ms including startup. On timeout, terminate the thread and await exit before logging failure or retrying. Synchronous infinite loops cannot block the main event loop. Context and results must support structured cloning. A worker's `vm` is still not a security sandbox; code must come from trusted server-side sources.
11
+ ## Core features
20
12
 
21
- API task `timeout` (default 30000 ms) covers the request and full response body read. `ApiTaskConfig.maxResponseBytes` limits received body bytes (positive safe integer; default 1048576, or 1 MiB). `maxResponseBytes: 10 * 1024 * 1024` allows 10 MiB. Invalid values fail before the request. Timeouts, size limits, and non-success HTTP statuses are logged as failures and apply task retries.
13
+ - Run tasks on a cron schedule or trigger them from application code.
14
+ - Execute HTTP requests, trusted JavaScript and application hooks, with retry and cancellation.
15
+ - Optionally persist tasks and logs; use shared cache for scheduling locks across nodes.
22
16
 
23
- `init()` succeeds only after loading all tasks. Empty or duplicate IDs (including persisted duplicates), invalid cron, database read failures, and persisted-task parse failures return failure and clear initialization state. Correct configuration or data before retrying. Do not redeclare existing database tasks in `tasks`.
17
+ ## Getting started
24
18
 
25
- ## Dependencies
19
+ ### Dependencies
26
20
 
27
21
  - `@h-ai/reldb`: task and execution log persistence; initialize first when `enableDb: true`.
28
22
  - `@h-ai/cache`: optional distributed locks; automatically participates in scheduled lock acquisition when initialized.
29
23
 
30
- ## Quick start
24
+ ### Quick start
31
25
 
32
26
  ```ts
33
27
  import { cache } from '@h-ai/cache'
@@ -89,7 +83,7 @@ scheduler.stop()
89
83
  await scheduler.close()
90
84
  ```
91
85
 
92
- ## Unified task model
86
+ ### Unified task model
93
87
 
94
88
  ```ts
95
89
  interface TaskDefinition {
@@ -132,7 +126,7 @@ Notes:
132
126
 
133
127
  > ⚠️ **Security warning**: `kind: 'js'` is only for trusted server-side code. Node.js `vm` provides convenient execution and **is not a security sandbox**. Never accept arbitrary JS strings from users, tenants, or admin inputs. Prefer `kind: 'api'` or `hooks.onTaskExecute` for configurable tasks.
134
128
 
135
- ## Task lifecycle hooks
129
+ ### Task lifecycle hooks
136
130
 
137
131
  Provide hooks at initialization or set them at runtime with `setHooks()`:
138
132
 
@@ -156,7 +150,7 @@ await scheduler.init({
156
150
  })
157
151
  ```
158
152
 
159
- ## Execution logs
153
+ ### Execution logs
160
154
 
161
155
  Filter logs by trigger source:
162
156
 
@@ -176,7 +170,7 @@ Execution logs support automatic cleanup policies configured at initialization:
176
170
  - `maxLogs`: retain at most N logs.
177
171
  - `retentionDays`: retain at most the last N days.
178
172
 
179
- ## API overview
173
+ ### API overview
180
174
 
181
175
  - `init(config?)`
182
176
  - `register(task)`
@@ -190,7 +184,7 @@ Execution logs support automatic cleanup policies configured at initialization:
190
184
  - `tasks` / `hooks` / `config` / `isInitialized` / `isRunning`
191
185
  - `close()`
192
186
 
193
- ## Error handling
187
+ ### Error handling
194
188
 
195
189
  All public APIs except `close()` return `HaiResult<T>`.
196
190
 
@@ -214,7 +208,29 @@ Common error codes:
214
208
  | `HaiSchedulerError.JS_COMPILE_FAILED` | `hai:scheduler:030` | JS compilation failed |
215
209
  | `HaiSchedulerError.HOOK_EXECUTION_FAILED` | `hai:scheduler:031` | Hook execution failed |
216
210
 
217
- ## Tests
211
+ ### Runtime behavior and limits
212
+
213
+ With the database enabled, nodes refresh persisted definitions every `tickInterval` (default 1000 ms); `tasks` is the latest successful snapshot. Registration, updates, deletion, manual triggers, and each attempt after acquiring a scheduling lock reread definitions. Deleted, disabled, or rescheduled old tasks stop executing; read failures abort the attempt. Already-started business side effects are not rolled back by remote changes. `init({ tasks })` remains node-local; use persisted `register()` for dynamic tasks across nodes. Cross-node deduplication requires shared Redis; memory cache is single-process only.
214
+
215
+ Call `await scheduler.refreshTasks()` to refresh without execution; concurrent refreshes of the same snapshot share one read. Registration, updates, and deletion change memory only after successful persistence. Invalid full updates leave cron unchanged. Pass `retry: null` or `handler: null` to clear retries or built-in handlers.
216
+
217
+ Hook business errors are recorded as `failed` and apply `retry`; missing handlers, cancellation, or host-thrown `SchedulerTaskSkippedError` are `interrupted`. `event.context.occurrenceId` identifies an occurrence: the same scheduled minute shares it across nodes, each manual trigger is independent, and retries reuse the original. Hosts can persist idempotency receipts with this ID; long tasks spanning periods still need a host-maintained lease for mutual exclusion. `deleteAfterRun` deletes only after success. Definitions are checked before retry and successful cleanup; one database DELETE atomically matches the executed definition, preserving concurrent updates.
218
+
219
+ Multi-node regression tests build current source and start two real Node processes sharing SQLite and Testcontainers Redis, validating definition convergence and same-minute lock contention. A working Docker/Podman service is required before `pnpm --filter @h-ai/scheduler test`.
220
+
221
+ `await scheduler.close()` stops task intake, cancels HTTP, JS workers, and retry waits, then awaits execution chain exit before clearing state. Reinitialization waits for closure. All lifecycle events expose `signal`; hooks must observe cancellation and stop external side effects. The framework cannot terminate business code on the same thread; do not block the event loop in hooks. Cancelled old executions no longer retry, delete tasks, or call new-instance hooks. Keep the database and cache available until scheduler closure completes, then close dependencies.
222
+
223
+ Each JS string runs in its own worker; `timeout` defaults to 30000 ms including startup. On timeout, terminate the thread and await exit before logging failure or retrying. Synchronous infinite loops cannot block the main event loop. Context and results must support structured cloning. A worker's `vm` is still not a security sandbox; code must come from trusted server-side sources.
224
+
225
+ API task `timeout` (default 30000 ms) covers the request and full response body read. `ApiTaskConfig.maxResponseBytes` limits received body bytes (positive safe integer; default 1048576, or 1 MiB). `maxResponseBytes: 10 * 1024 * 1024` allows 10 MiB. Invalid values fail before the request. Timeouts, size limits, and non-success HTTP statuses are logged as failures and apply task retries.
226
+
227
+ `init()` succeeds only after loading all tasks. Empty or duplicate IDs (including persisted duplicates), invalid cron, database read failures, and persisted-task parse failures return failure and clear initialization state. Correct configuration or data before retrying. Do not redeclare existing database tasks in `tasks`.
228
+
229
+ ### Optional integrations
230
+
231
+ Cron is matched by minute, and distributed lock keys use that same minute. Startup or ticks delayed beyond second zero still check the current minute; a running scheduler does not reschedule it on every tick.
232
+
233
+ ## Development and testing
218
234
 
219
235
  ```bash
220
236
  pnpm --filter @h-ai/scheduler test
@@ -223,5 +239,3 @@ pnpm --filter @h-ai/scheduler test
223
239
  ## License
224
240
 
225
241
  Apache-2.0
226
-
227
- Cron is matched by minute, and distributed lock keys use that same minute. Startup or ticks delayed beyond second zero still check the current minute; a running scheduler does not reschedule it on every tick.
package/README.ja.md CHANGED
@@ -4,30 +4,24 @@
4
4
 
5
5
  [フレームワークのホーム](../../README.ja.md) · 関連モジュール:[@h-ai/cache](../cache/README.ja.md) · [@h-ai/core](../core/README.ja.md) · [@h-ai/reldb](../reldb/README.ja.md)
6
6
 
7
- 共通のタスクモデル、cron スケジュール、JS 関数文字列・HTTP API の実行、永続化、実行ログ検索、全体のライフサイクル Hook を提供するスケジューラーモジュールです。
8
-
9
- DB を有効にすると、各ノードは `tickInterval`(既定 1000 ms)ごとに保存された定義を更新し、`tasks` は直近の成功したスナップショットになります。登録・更新・削除・手動実行、およびロック取得後の各試行で定義を再読します。削除・無効化・再スケジュールされた古いタスクは実行せず、読み取り失敗時は試行を中断します。開始済みの副作用は遠隔の変更で自動ロールバックしません。`init({ tasks })` はノード固有設定です。ノード間の動的タスクには永続化する `register()`、重複防止には共有 Redis を使います。メモリーキャッシュは単一プロセスのみです。
10
-
11
- `await scheduler.refreshTasks()` でタスクを実行せず定義を更新できます。同じスナップショットの並行更新は 1 回の読み取りを共有します。登録・更新・削除は永続化成功後にメモリーへ反映し、全体更新の検証失敗では cron を変更しません。再試行や組み込み handler の解除には `retry: null`、`handler: null` を渡します。
12
-
13
- Hook が投げる業務エラーは `failed` として記録し `retry` を適用します。handler の不足、キャンセル、ホストが投げる `SchedulerTaskSkippedError` は `interrupted` です。`event.context.occurrenceId` は 1 回の発生を識別し、同じスケジュール分ではノード間で共通、手動実行では毎回独立、再試行では元の ID を共有します。ホストはこの ID で冪等性の受領記録を保存できます。周期をまたぐ長時間タスクの排他制御にはホスト側の継続リースが必要です。`deleteAfterRun` は成功時のみ削除します。再試行前と成功後の削除前に現在の定義を確認し、DB の削除は 1 つの DELETE 内で実行定義をアトミックに照合して並行更新を保持します。
14
-
15
- 複数ノードの回帰テストは現在のソースをビルドし、SQLite と Testcontainers Redis を共有する実際の Node プロセスを 2 つ起動して、定義の収束と同じ分のロック競合を検証します。`pnpm --filter @h-ai/scheduler test` の前に、利用可能な Docker/Podman サービスが必要です。
7
+ ## 概要
16
8
 
17
- `await scheduler.close()` は受付を止め、HTTP、JS Worker、再試行待機をキャンセルし、実行チェーンの終了後に状態を解放します。再初期化は終了完了を待ちます。すべてのイベントが `signal` を提供し、Hook はキャンセルを監視して外部への副作用を停止する必要があります。同じスレッドの業務コードは強制終了できないため、Hook でイベントループをブロックしないでください。キャンセルされた旧実行は再試行・自動削除・新インスタンスの Hook 呼び出しを行いません。scheduler の終了完了まで DB とキャッシュを維持し、その後に依存モジュールを終了します。
9
+ 共通のタスクモデル、cron スケジュール、JS 関数文字列・HTTP API の実行、永続化、実行ログ検索、全体のライフサイクル Hook を提供するスケジューラーモジュールです。
18
10
 
19
- JS 文字列は呼び出しごとの独立 Worker で実行します。`timeout` は起動を含めて既定 30000 ms です。時間超過時はスレッドを終了し、終了を待ってから失敗記録・再試行を行います。同期の無限ループでもメインイベントループをブロックしません。コンテキストと戻り値は構造化クローンに対応する必要があります。Worker 内の `vm` も安全なサンドボックスではなく、信頼できるサーバー側コードのみを使用します。
11
+ ## 主な機能
20
12
 
21
- API タスクの `timeout`(既定 30000 ms)はリクエストと応答本文全体の読み取りを含みます。`ApiTaskConfig.maxResponseBytes` は受信バイト上限で、正の安全な整数、既定は 1048576(1 MiB)です。`maxResponseBytes: 10 * 1024 * 1024` なら最大 10 MiB を許可し、不正値は送信前に失敗します。タイムアウト・上限超過・非成功 HTTP 応答は失敗として記録し、タスクの再試行方針を適用します。
13
+ - cron による定期実行とアプリケーションからの手動実行。
14
+ - HTTP、信頼できる JavaScript、アプリ Hook に対応し、再試行とキャンセルを提供。
15
+ - タスク・ログの永続化と、共有キャッシュによるノード間の実行ロック。
22
16
 
23
- `init()` は全タスクの読み込み完了後にのみ成功します。空 ID、重複 ID(保存済みタスクとの重複を含む)、不正な cron、DB 読み取り失敗、保存タスクの解析失敗では失敗を返し、初期化状態を消去します。設定・データを修正してから再初期化し、既存の DB タスクを `tasks` に重複宣言しないでください。
17
+ ## 使い始める
24
18
 
25
- ## 依存関係
19
+ ### 依存関係
26
20
 
27
21
  - `@h-ai/reldb`:タスク定義と実行ログの永続化。`enableDb: true` の場合は先に初期化。
28
22
  - `@h-ai/cache`:任意の分散ロック。初期化済みの場合、定時実行時のロック取得に自動参加。
29
23
 
30
- ## クイックスタート
24
+ ### クイックスタート
31
25
 
32
26
  ```ts
33
27
  import { cache } from '@h-ai/cache'
@@ -89,7 +83,7 @@ scheduler.stop()
89
83
  await scheduler.close()
90
84
  ```
91
85
 
92
- ## 共通タスクモデル
86
+ ### 共通タスクモデル
93
87
 
94
88
  ```ts
95
89
  interface TaskDefinition {
@@ -132,7 +126,7 @@ interface JsTaskConfig {
132
126
 
133
127
  > ⚠️ **セキュリティ注意**:`kind: 'js'` は信頼できるサーバー側コード専用です。Node.js の `vm` による実行は**安全なサンドボックスではありません**。ユーザー、テナント、管理画面から任意の JS 文字列を直接受け付けないでください。設定可能なタスクには `kind: 'api'` または `hooks.onTaskExecute` を優先してください。
134
128
 
135
- ## タスクのライフサイクル Hook
129
+ ### タスクのライフサイクル Hook
136
130
 
137
131
  初期化時に渡すか、実行時に `setHooks()` で設定します:
138
132
 
@@ -156,7 +150,7 @@ await scheduler.init({
156
150
  })
157
151
  ```
158
152
 
159
- ## 実行ログ
153
+ ### 実行ログ
160
154
 
161
155
  実行元でログを絞り込めます:
162
156
 
@@ -176,7 +170,7 @@ const logs = await scheduler.getLogs({
176
170
  - `maxLogs`:最大 N 件を保持。
177
171
  - `retentionDays`:直近 N 日分のみを保持。
178
172
 
179
- ## API 一覧
173
+ ### API 一覧
180
174
 
181
175
  - `init(config?)`
182
176
  - `register(task)`
@@ -190,7 +184,7 @@ const logs = await scheduler.getLogs({
190
184
  - `tasks` / `hooks` / `config` / `isInitialized` / `isRunning`
191
185
  - `close()`
192
186
 
193
- ## エラー処理
187
+ ### エラー処理
194
188
 
195
189
  `close()` を除くすべての公開 API は `HaiResult<T>` を返します。
196
190
 
@@ -214,14 +208,34 @@ const logs = await scheduler.getLogs({
214
208
  | `HaiSchedulerError.JS_COMPILE_FAILED` | `hai:scheduler:030` | JS コンパイルに失敗 |
215
209
  | `HaiSchedulerError.HOOK_EXECUTION_FAILED` | `hai:scheduler:031` | Hook 実行に失敗 |
216
210
 
217
- ## テスト
211
+ ### 実行時の動作と制約
212
+
213
+ DB を有効にすると、各ノードは `tickInterval`(既定 1000 ms)ごとに保存された定義を更新し、`tasks` は直近の成功したスナップショットになります。登録・更新・削除・手動実行、およびロック取得後の各試行で定義を再読します。削除・無効化・再スケジュールされた古いタスクは実行せず、読み取り失敗時は試行を中断します。開始済みの副作用は遠隔の変更で自動ロールバックしません。`init({ tasks })` はノード固有設定です。ノード間の動的タスクには永続化する `register()`、重複防止には共有 Redis を使います。メモリーキャッシュは単一プロセスのみです。
214
+
215
+ `await scheduler.refreshTasks()` でタスクを実行せず定義を更新できます。同じスナップショットの並行更新は 1 回の読み取りを共有します。登録・更新・削除は永続化成功後にメモリーへ反映し、全体更新の検証失敗では cron を変更しません。再試行や組み込み handler の解除には `retry: null`、`handler: null` を渡します。
216
+
217
+ Hook が投げる業務エラーは `failed` として記録し `retry` を適用します。handler の不足、キャンセル、ホストが投げる `SchedulerTaskSkippedError` は `interrupted` です。`event.context.occurrenceId` は 1 回の発生を識別し、同じスケジュール分ではノード間で共通、手動実行では毎回独立、再試行では元の ID を共有します。ホストはこの ID で冪等性の受領記録を保存できます。周期をまたぐ長時間タスクの排他制御にはホスト側の継続リースが必要です。`deleteAfterRun` は成功時のみ削除します。再試行前と成功後の削除前に現在の定義を確認し、DB の削除は 1 つの DELETE 内で実行定義をアトミックに照合して並行更新を保持します。
218
+
219
+ 複数ノードの回帰テストは現在のソースをビルドし、SQLite と Testcontainers Redis を共有する実際の Node プロセスを 2 つ起動して、定義の収束と同じ分のロック競合を検証します。`pnpm --filter @h-ai/scheduler test` の前に、利用可能な Docker/Podman サービスが必要です。
220
+
221
+ `await scheduler.close()` は受付を止め、HTTP、JS Worker、再試行待機をキャンセルし、実行チェーンの終了後に状態を解放します。再初期化は終了完了を待ちます。すべてのイベントが `signal` を提供し、Hook はキャンセルを監視して外部への副作用を停止する必要があります。同じスレッドの業務コードは強制終了できないため、Hook でイベントループをブロックしないでください。キャンセルされた旧実行は再試行・自動削除・新インスタンスの Hook 呼び出しを行いません。scheduler の終了完了まで DB とキャッシュを維持し、その後に依存モジュールを終了します。
222
+
223
+ JS 文字列は呼び出しごとの独立 Worker で実行します。`timeout` は起動を含めて既定 30000 ms です。時間超過時はスレッドを終了し、終了を待ってから失敗記録・再試行を行います。同期の無限ループでもメインイベントループをブロックしません。コンテキストと戻り値は構造化クローンに対応する必要があります。Worker 内の `vm` も安全なサンドボックスではなく、信頼できるサーバー側コードのみを使用します。
224
+
225
+ API タスクの `timeout`(既定 30000 ms)はリクエストと応答本文全体の読み取りを含みます。`ApiTaskConfig.maxResponseBytes` は受信バイト上限で、正の安全な整数、既定は 1048576(1 MiB)です。`maxResponseBytes: 10 * 1024 * 1024` なら最大 10 MiB を許可し、不正値は送信前に失敗します。タイムアウト・上限超過・非成功 HTTP 応答は失敗として記録し、タスクの再試行方針を適用します。
226
+
227
+ `init()` は全タスクの読み込み完了後にのみ成功します。空 ID、重複 ID(保存済みタスクとの重複を含む)、不正な cron、DB 読み取り失敗、保存タスクの解析失敗では失敗を返し、初期化状態を消去します。設定・データを修正してから再初期化し、既存の DB タスクを `tasks` に重複宣言しないでください。
228
+
229
+ ### 任意の連携
230
+
231
+ スケジュールループは分単位で cron を照合し、同じ分から分散ロックのキーを生成します。起動や tick が 0 秒を過ぎても現在の分を確認し、同じ実行中に毎 tick 重複してスケジュールしません。
232
+
233
+ ## 開発とテスト
218
234
 
219
235
  ```bash
220
236
  pnpm --filter @h-ai/scheduler test
221
237
  ```
222
238
 
223
- ## License
239
+ ## ライセンス
224
240
 
225
241
  Apache-2.0
226
-
227
- スケジュールループは分単位で cron を照合し、同じ分から分散ロックのキーを生成します。起動や tick が 0 秒を過ぎても現在の分を確認し、同じ実行中に毎 tick 重複してスケジュールしません。
package/README.md CHANGED
@@ -4,30 +4,24 @@
4
4
 
5
5
  [框架首页](../../README.md) · 相关模块:[@h-ai/cache](../cache/README.md) · [@h-ai/core](../core/README.md) · [@h-ai/reldb](../reldb/README.md)
6
6
 
7
- 定时任务调度模块,支持统一任务模型、cron 调度、JS 函数字符串 / HTTP API 执行、任务持久化、执行日志查询,以及全局任务生命周期回调。
8
-
9
- 启用数据库时,运行中的节点每个 `tickInterval`(默认 1000 毫秒)刷新持久化定义;`tasks` 是最近一次成功刷新的快照。注册、修改、删除、手工触发和获得调度锁后的每次尝试均重新读取定义,删除/禁用/已改调度时间的旧任务不会继续执行,读取失败则中断本次尝试。已经开始的业务副作用不因远端定义变化自动回滚。`init({ tasks })` 仍是节点本地配置,跨节点动态任务请用持久化的 `register()` 管理;跨节点防重需要共享 Redis,内存缓存仅限单进程。
10
-
11
- 可调用 `await scheduler.refreshTasks()` 主动刷新而不执行任务;同一快照的并发刷新共用一次读取。注册、更新与删除成功持久化后才更新内存投影,整份更新验证失败不改变 cron。清除重试或内置处理器时分别传 `retry: null`、`handler: null`。
12
-
13
- Hook 抛出的业务失败记录为 `failed` 并应用 `retry`;缺少处理器、取消或宿主抛出的 `SchedulerTaskSkippedError` 记录为 `interrupted`。`event.context.occurrenceId` 标识一次发生:同一调度分钟跨节点一致,手工触发每次独立,重试共享原标识。宿主可据此持久化业务幂等回执;跨周期的长任务互斥仍需宿主持续租约。`deleteAfterRun` 仅在成功后删除,失败任务保留;重试前与成功清理前核对当前定义,数据库清理在同一条 DELETE 内原子匹配执行定义,保留期间提交的并发更新。
14
-
15
- 多节点回归测试会构建当前源码、启动两个真实 Node 进程共享 SQLite 和 Testcontainers Redis,验证定义收敛及同分钟锁竞争;运行 `pnpm --filter @h-ai/scheduler test` 前须有可用的 Docker/Podman 服务。
7
+ ## 简介
16
8
 
17
- `await scheduler.close()` 先停止接收任务,取消 HTTP、JS Worker 与重试等待,再等待执行链退出后清理状态。重新初始化等待关闭完成。所有生命周期事件提供 `signal`;Hook 必须监听取消并停止自己的外部副作用(框架无法强制终止同一线程中的业务代码),不要在 Hook 中阻塞事件循环。取消后的旧执行不再重试、不自动删除任务,也不会调用新实例的 Hook。关闭期间保持数据库和缓存可用,待 scheduler 关闭后再关闭依赖。
9
+ 定时任务调度模块,支持统一任务模型、cron 调度、JS 函数字符串 / HTTP API 执行、任务持久化、执行日志查询,以及全局任务生命周期回调。
18
10
 
19
- JS 字符串在每次调用的独立 Worker 中执行,`timeout` 默认 30000 毫秒(含启动)。到期后终止并等待线程退出,再记录失败或重试;同步死循环也不会阻塞主事件循环。上下文及返回值必须支持结构化克隆。Worker 中的 `vm` 仍不是安全沙箱,代码必须来自受信任的服务端来源。
11
+ ## 核心功能清单
20
12
 
21
- API 任务的 `timeout`(默认 30000 毫秒)覆盖请求和完整响应体读取。`ApiTaskConfig.maxResponseBytes` 指定响应体实际接收字节上限(正安全整数,默认 1048576 即 1 MiB);例如 `maxResponseBytes: 10 * 1024 * 1024` 允许最大 10 MiB,非法值在请求前失败;超时、超限或非成功 HTTP 状态均记录失败并应用任务重试策略。
13
+ - 按 cron 定时执行,也可从业务代码手动触发。
14
+ - 支持 HTTP、受信任的 JavaScript 和应用 Hook,提供重试与取消。
15
+ - 可持久化任务和执行日志;共享缓存用于多节点抢锁。
22
16
 
23
- `init()` 必须完整加载全部任务才返回成功。配置中的空 ID、重复 ID(含与持久化任务重复)、无效 cron,以及数据库读取或持久化任务解析失败,均返回失败并清空初始化状态;修正配置或数据后重新初始化。不要同时在 `tasks` 中重复声明已有的数据库任务。
17
+ ## 使用入门
24
18
 
25
- ## 依赖
19
+ ### 依赖
26
20
 
27
21
  - `@h-ai/reldb` — 任务定义与执行日志持久化,`enableDb: true` 时需先初始化
28
22
  - `@h-ai/cache` — 分布式锁,可选;初始化后自动参与定时触发抢锁
29
23
 
30
- ## 快速开始
24
+ ### 快速开始
31
25
 
32
26
  ```ts
33
27
  import { cache } from '@h-ai/cache'
@@ -89,7 +83,7 @@ scheduler.stop()
89
83
  await scheduler.close()
90
84
  ```
91
85
 
92
- ## 统一任务模型
86
+ ### 统一任务模型
93
87
 
94
88
  ```ts
95
89
  interface TaskDefinition {
@@ -132,7 +126,7 @@ interface JsTaskConfig {
132
126
 
133
127
  > ⚠️ **安全警示**:`kind: 'js'` 仅适用于受信任的服务端代码。当前实现基于 Node.js `vm` 做便捷执行,**不是安全沙箱**;禁止直接接收用户、租户或后台任意输入的 JS 字符串。可配置任务优先使用 `kind: 'api'` 或 `hooks.onTaskExecute`。
134
128
 
135
- ## 任务生命周期回调
129
+ ### 任务生命周期回调
136
130
 
137
131
  初始化时传入或运行时通过 `setHooks()` 设置:
138
132
 
@@ -156,7 +150,7 @@ await scheduler.init({
156
150
  })
157
151
  ```
158
152
 
159
- ## 执行日志
153
+ ### 执行日志
160
154
 
161
155
  查询日志时可按触发来源过滤:
162
156
 
@@ -176,7 +170,7 @@ const logs = await scheduler.getLogs({
176
170
  - `maxLogs`:最多保留 N 条日志
177
171
  - `retentionDays`:最多保留最近 N 天日志
178
172
 
179
- ## API 概览
173
+ ### API 概览
180
174
 
181
175
  - `init(config?)`
182
176
  - `register(task)`
@@ -190,7 +184,7 @@ const logs = await scheduler.getLogs({
190
184
  - `tasks` / `hooks` / `config` / `isInitialized` / `isRunning`
191
185
  - `close()`
192
186
 
193
- ## 错误处理
187
+ ### 错误处理
194
188
 
195
189
  所有公共 API(除 `close()`)返回 `HaiResult<T>`。
196
190
 
@@ -214,14 +208,34 @@ const logs = await scheduler.getLogs({
214
208
  | `HaiSchedulerError.JS_COMPILE_FAILED` | `hai:scheduler:030` | JS 编译失败 |
215
209
  | `HaiSchedulerError.HOOK_EXECUTION_FAILED` | `hai:scheduler:031` | Hook 执行失败 |
216
210
 
217
- ## 测试
211
+ ### 运行行为与边界
212
+
213
+ 启用数据库时,运行中的节点每个 `tickInterval`(默认 1000 毫秒)刷新持久化定义;`tasks` 是最近一次成功刷新的快照。注册、修改、删除、手工触发和获得调度锁后的每次尝试均重新读取定义,删除/禁用/已改调度时间的旧任务不会继续执行,读取失败则中断本次尝试。已经开始的业务副作用不因远端定义变化自动回滚。`init({ tasks })` 仍是节点本地配置,跨节点动态任务请用持久化的 `register()` 管理;跨节点防重需要共享 Redis,内存缓存仅限单进程。
214
+
215
+ 可调用 `await scheduler.refreshTasks()` 主动刷新而不执行任务;同一快照的并发刷新共用一次读取。注册、更新与删除成功持久化后才更新内存投影,整份更新验证失败不改变 cron。清除重试或内置处理器时分别传 `retry: null`、`handler: null`。
216
+
217
+ Hook 抛出的业务失败记录为 `failed` 并应用 `retry`;缺少处理器、取消或宿主抛出的 `SchedulerTaskSkippedError` 记录为 `interrupted`。`event.context.occurrenceId` 标识一次发生:同一调度分钟跨节点一致,手工触发每次独立,重试共享原标识。宿主可据此持久化业务幂等回执;跨周期的长任务互斥仍需宿主持续租约。`deleteAfterRun` 仅在成功后删除,失败任务保留;重试前与成功清理前核对当前定义,数据库清理在同一条 DELETE 内原子匹配执行定义,保留期间提交的并发更新。
218
+
219
+ 多节点回归测试会构建当前源码、启动两个真实 Node 进程共享 SQLite 和 Testcontainers Redis,验证定义收敛及同分钟锁竞争;运行 `pnpm --filter @h-ai/scheduler test` 前须有可用的 Docker/Podman 服务。
220
+
221
+ `await scheduler.close()` 先停止接收任务,取消 HTTP、JS Worker 与重试等待,再等待执行链退出后清理状态。重新初始化等待关闭完成。所有生命周期事件提供 `signal`;Hook 必须监听取消并停止自己的外部副作用(框架无法强制终止同一线程中的业务代码),不要在 Hook 中阻塞事件循环。取消后的旧执行不再重试、不自动删除任务,也不会调用新实例的 Hook。关闭期间保持数据库和缓存可用,待 scheduler 关闭后再关闭依赖。
222
+
223
+ JS 字符串在每次调用的独立 Worker 中执行,`timeout` 默认 30000 毫秒(含启动)。到期后终止并等待线程退出,再记录失败或重试;同步死循环也不会阻塞主事件循环。上下文及返回值必须支持结构化克隆。Worker 中的 `vm` 仍不是安全沙箱,代码必须来自受信任的服务端来源。
224
+
225
+ API 任务的 `timeout`(默认 30000 毫秒)覆盖请求和完整响应体读取。`ApiTaskConfig.maxResponseBytes` 指定响应体实际接收字节上限(正安全整数,默认 1048576 即 1 MiB);例如 `maxResponseBytes: 10 * 1024 * 1024` 允许最大 10 MiB,非法值在请求前失败;超时、超限或非成功 HTTP 状态均记录失败并应用任务重试策略。
226
+
227
+ `init()` 必须完整加载全部任务才返回成功。配置中的空 ID、重复 ID(含与持久化任务重复)、无效 cron,以及数据库读取或持久化任务解析失败,均返回失败并清空初始化状态;修正配置或数据后重新初始化。不要同时在 `tasks` 中重复声明已有的数据库任务。
228
+
229
+ ### 可选集成
230
+
231
+ 调度循环按分钟时间点匹配 cron,并用同一分钟生成分布式锁键;启动或 tick 延迟至非零秒仍检查当前分钟,同一轮运行不会每个 tick 重复调度。
232
+
233
+ ## 开发测试
218
234
 
219
235
  ```bash
220
236
  pnpm --filter @h-ai/scheduler test
221
237
  ```
222
238
 
223
- ## License
239
+ ## 许可证
224
240
 
225
241
  Apache-2.0
226
-
227
- 调度循环按分钟时间点匹配 cron,并用同一分钟生成分布式锁键;启动或 tick 延迟至非零秒仍检查当前分钟,同一轮运行不会每个 tick 重复调度。
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@h-ai/scheduler",
3
3
  "type": "module",
4
- "version": "0.1.0-alpha.65",
4
+ "version": "0.1.0-alpha.67",
5
5
  "description": "Hai Framework task scheduling module with cron, function, and API jobs.",
6
6
  "license": "Apache-2.0",
7
7
  "exports": {
@@ -24,9 +24,9 @@
24
24
  "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
25
25
  },
26
26
  "dependencies": {
27
- "@h-ai/cache": "0.1.0-alpha.65",
28
- "@h-ai/core": "0.1.0-alpha.65",
29
- "@h-ai/reldb": "0.1.0-alpha.65",
27
+ "@h-ai/cache": "0.1.0-alpha.67",
28
+ "@h-ai/core": "0.1.0-alpha.67",
29
+ "@h-ai/reldb": "0.1.0-alpha.67",
30
30
  "croner": "^10.0.1",
31
31
  "zod": "^4.6.5"
32
32
  },