@gravito/horizon 3.0.1 → 3.2.1

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.md CHANGED
@@ -1,13 +1,18 @@
1
1
  # @gravito/horizon
2
2
 
3
- Enterprise-grade distributed task scheduler for Gravito framework.
3
+ Enterprise-grade distributed task scheduler for the Gravito framework.
4
+
5
+ `@gravito/horizon` provides a robust, fluent, and highly configurable system for managing scheduled tasks (Cron jobs) in a distributed environment. It supports multiple locking mechanisms to prevent duplicate execution, node role filtering, retries, and comprehensive monitoring hooks.
4
6
 
5
7
  ## Features
6
8
 
7
- - **Fluent API**: Expressive syntax for defining task schedules (e.g. `.daily().at('14:00')`).
8
- - **Distributed Locking**: Prevents duplicate task execution across multiple servers (supports Memory, Cache, Redis).
9
- - **Cron Integration**: Designed to be triggered by a single system cron entry.
10
- - **Hooks**: Events for task success/failure.
9
+ - **Fluent API**: Human-readable syntax for defining task schedules (e.g., `.daily().at('14:00')`).
10
+ - **Distributed Locking**: Prevents duplicate task execution across multiple servers (supports Memory, Cache, and Redis).
11
+ - **Node Role Awareness**: Restrict tasks to specific nodes (e.g., only run on `worker` nodes) or broadcast maintenance tasks to all matching nodes.
12
+ - **Reliability Features**: Built-in support for task timeouts and automatic retries with configurable delays.
13
+ - **Shell Command Support**: Schedule raw shell commands alongside TypeScript callbacks.
14
+ - **Lazy Cron Parsing**: Lightweight `SimpleCronParser` for standard expressions, with `cron-parser` only loaded when complex logic is required.
15
+ - **Comprehensive Hooks**: Lifecycle events for monitoring task success, failure, retries, and scheduler activity.
11
16
 
12
17
  ## Installation
13
18
 
@@ -15,127 +20,135 @@ Enterprise-grade distributed task scheduler for Gravito framework.
15
20
  bun add @gravito/horizon
16
21
  ```
17
22
 
23
+ ### Peer Dependencies
24
+ - `@gravito/core` (Required)
25
+ - `@gravito/stasis` (Optional, for Cache/Redis distributed locking)
26
+
18
27
  ## Quick Start
19
28
 
20
- 1. Register the orbit in your application boot:
29
+ ### 1. Register the Orbit
30
+
31
+ Initialize Horizon in your application bootstrap process:
21
32
 
22
33
  ```typescript
34
+ import { PlanetCore } from '@gravito/core'
23
35
  import { OrbitHorizon } from '@gravito/horizon'
24
- import { OrbitCache } from '@gravito/stasis' // Optional, for cache lock
36
+ import { OrbitCache } from '@gravito/stasis' // Optional, for distributed locking
25
37
 
26
38
  await PlanetCore.boot({
27
39
  config: {
28
40
  scheduler: {
29
- lock: { driver: 'cache' },
30
- nodeRole: 'worker' // 'api', 'web', etc.
41
+ lock: { driver: 'cache' }, // Uses @gravito/stasis cache
42
+ nodeRole: process.env.NODE_ROLE || 'worker'
31
43
  }
32
44
  },
33
45
  orbits: [
34
- OrbitCache, // Load cache first if using cache driver
46
+ OrbitCache,
35
47
  OrbitHorizon
36
48
  ]
37
49
  })
38
50
  ```
39
51
 
40
- 2. Schedule tasks:
52
+ ### 2. Schedule Tasks
53
+
54
+ Define tasks in your service providers or bootstrap files:
41
55
 
42
56
  ```typescript
43
- // Access scheduler from core services or context
44
- const scheduler = core.services.get('scheduler')
57
+ import { SchedulerManager } from '@gravito/horizon'
58
+
59
+ const scheduler = core.services.get<SchedulerManager>('scheduler')
45
60
 
61
+ // Basic callback task
46
62
  scheduler.task('daily-cleanup', async () => {
47
- await db.cleanup()
63
+ await db.cleanup()
48
64
  })
49
- .daily()
50
- .at('02:00')
65
+ .dailyAt('02:00')
51
66
  .onOneServer() // Distributed lock
67
+
68
+ // Shell command execution
69
+ scheduler.exec('sync-storage', 'aws s3 sync ./local s3://bucket')
70
+ .everyFiveMinutes()
71
+ .onNode('worker')
72
+ .retry(3, 5000) // Retry 3 times with 5s delay
52
73
  ```
53
74
 
54
- ## Distributed Locking
75
+ ## Scheduling API
76
+
77
+ ### Frequency Methods
78
+ - `everyMinute()`: Every minute (`* * * * *`)
79
+ - `everyFiveMinutes()`, `everyTenMinutes()`, `everyFifteenMinutes()`, `everyThirtyMinutes()`
80
+ - `hourly()`, `hourlyAt(minute)`: Every hour at a specific minute.
81
+ - `daily()`, `dailyAt('HH:mm')`: Once per day.
82
+ - `weekly()`, `weeklyOn(day, 'HH:mm')`: Once per week (0=Sunday).
83
+ - `monthly()`, `monthlyOn(date, 'HH:mm')`: Once per month.
84
+ - `cron('expression')`: Custom 5-part cron expression.
85
+
86
+ ### Constraints & Execution Control
87
+ - `timezone('Asia/Taipei')`: Set execution timezone (defaults to UTC).
88
+ - `onOneServer(lockTtl?)`: Ensure only one instance of this task runs globally at a time (time-window lock).
89
+ - `withoutOverlapping(ttl?)`: Prevent task from executing if previous instance is still running (execution lock).
90
+ - `onNode(role)`: Only run this task if the current node's role matches.
91
+ - `runInBackground()`: Don't wait for task completion in the main scheduler loop.
92
+
93
+ #### Overlapping Control vs. OnOneServer
55
94
 
56
- To prevent tasks from running simultaneously on multiple servers, use `onOneServer()` (or `withoutOverlapping()`).
57
- The default lock driver is `memory` (single server), but you should configure `cache` or `redis` for distributed setups.
95
+ - **`onOneServer()`**: Uses a **time-window lock** to prevent multiple servers from executing the same task in the same minute window. Lock key is based on `task:{name}:{timestamp_minute}`.
96
+ - **`withoutOverlapping()`**: Uses an **execution lock** to prevent a task from running if the previous execution hasn't completed yet. Lock key is `task:running:{name}`.
97
+
98
+ You can use both together for comprehensive protection:
99
+
100
+ ```typescript
101
+ scheduler.task('heavy-sync', async () => {
102
+ // Long-running task (may take 5+ minutes)
103
+ await syncLargeDataset()
104
+ })
105
+ .everyMinute()
106
+ .onOneServer() // Prevent multi-server execution
107
+ .withoutOverlapping() // Prevent overlapping executions
108
+ .timeout(600000) // 10 minute timeout
109
+ ```
110
+
111
+ ### Reliability & Lifecycle
112
+ - `timeout(ms)`: Set maximum execution time (defaults to 1 hour).
113
+ - `retry(attempts, delayMs)`: Number of retries on failure.
114
+ - `onSuccess(callback)`: Execute a callback when the task succeeds.
115
+ - `onFailure(callback)`: Execute a callback when the task fails.
58
116
 
59
117
  ## CLI Usage
60
118
 
61
- The Gravito CLI provides commands to run and manage your scheduled tasks.
119
+ The Gravito CLI allows you to manage and run your scheduler:
62
120
 
63
- ### List Tasks
64
- View all registered tasks and their schedules:
121
+ ### List Registered Tasks
65
122
  ```bash
66
123
  bun run gravito schedule:list
67
124
  ```
68
125
 
69
126
  ### Run (Cron Mode)
70
- Add this command to your system's crontab (e.g., `crontab -e`) to run every minute:
127
+ Add this to your system crontab to trigger the scheduler every minute:
71
128
  ```bash
72
- * * * * * cd /path/to/project && bun run gravito schedule:run
129
+ * * * * * cd /app && bun run gravito schedule:run
73
130
  ```
74
131
 
75
132
  ### Run (Daemon Mode)
76
- If you prefer a long-running process (e.g., in Docker or specific worker environments):
133
+ Poll every minute in a long-running process (ideal for Docker):
77
134
  ```bash
78
135
  bun run gravito schedule:work
79
136
  ```
80
- This will poll every minute to execute due tasks.
81
-
82
- ### Entry Point
83
- By default, commands look for `src/index.ts`. If your bootstrap file is elsewhere, use `--entry`:
84
- ```bash
85
- gravito schedule:list --entry src/bootstrap.ts
86
- ```
87
137
 
88
- ## Performance Monitoring
138
+ ## Monitoring Hooks
89
139
 
90
- The scheduler emits hooks providing execution metrics. You can listen to these events via `core.hooks`:
140
+ Subscribe to hooks via `core.hooks` to build monitoring dashboards or alerts:
91
141
 
92
142
  | Hook | Payload | Description |
93
143
  |------|---------|-------------|
94
- | `scheduler:run:start` | `{ date }` | Scheduler checks started |
95
- | `scheduler:run:complete` | `{ date, dueCount }` | Scheduler checks completed |
96
- | `scheduler:task:start` | `{ name, startTime }` | Individual task execution started |
97
- | `scheduler:task:success` | `{ name, duration }` | Task completed successfully |
98
- | `scheduler:task:failure` | `{ name, error, duration }` | Task failed |
144
+ | `scheduler:run:start` | `{ date }` | Scheduler evaluation started. |
145
+ | `scheduler:run:complete` | `{ date, dueCount }` | Scheduler evaluation finished. |
146
+ | `scheduler:task:start` | `{ name, startTime }` | Individual task started. |
147
+ | `scheduler:task:skipped` | `{ name, reason, timestamp }` | Task was skipped (e.g., due to overlapping execution). |
148
+ | `scheduler:task:retry` | `{ name, attempt, error }` | Task failed and is retrying. |
149
+ | `scheduler:task:success` | `{ name, duration, attempts }` | Task finished successfully. |
150
+ | `scheduler:task:failure` | `{ name, error, duration }` | Task failed after all retries. |
99
151
 
100
- ## Optimizations
152
+ ## License
101
153
 
102
- ### Lightweight Execution
103
- This package includes a lightweight, dependency-free cron parser (`SimpleCronParser`) for standard cron expressions (e.g. `* * * * *`, `0 0 * * *`). The heavy `cron-parser` library is only lazy-loaded when complex expressions are encountered, keeping your runtime memory footprint minimal.
104
-
105
- ## Process Execution & Node Roles
106
-
107
- You can also run shell commands and restrict tasks to specific node roles (e.g., `api` vs `worker`).
108
-
109
- ### Configuration
110
-
111
- Add `nodeRole` to your configuration:
112
-
113
- ```typescript
114
- config: {
115
- scheduler: {
116
- nodeRole: 'worker'
117
- }
118
- }
119
- ```
120
-
121
- ### Mode A: Broadcast (Maintenance)
122
-
123
- Run on EVERY node that matches the role. Useful for machine-specific maintenance.
124
-
125
- ```typescript
126
- // Clean temp files on ALL 'api' nodes
127
- scheduler.exec('clean-tmp', 'rm -rf /tmp/*')
128
- .daily()
129
- .onNode('api')
130
- ```
131
-
132
- ### Mode B: Single-point (Task)
133
-
134
- Run on ONE matching node only. Useful for centralized jobs like DB migrations or reports.
135
-
136
- ```typescript
137
- // Run migration on ONE 'worker' node
138
- scheduler.exec('migrate', 'bun run db:migrate')
139
- .onNode('worker')
140
- .onOneServer()
141
- ```
154
+ MIT
package/README.zh-TW.md CHANGED
@@ -1,6 +1,18 @@
1
1
  # @gravito/horizon
2
2
 
3
- > Gravito 的分散式排程模組,支援任務排程與分散式鎖。
3
+ Gravito 框架的企業級分散式任務排程器。
4
+
5
+ `@gravito/horizon` 為管理分散式環境中的定時任務(Cron jobs)提供了一個強大、流暢且高度可配置的系統。它支援多種鎖定機制以防止重複執行、節點角色過濾、重試機制以及完整的監控 Hook。
6
+
7
+ ## 特色
8
+
9
+ - **Fluent API**: 使用人類可讀的語法定義任務排程(例如:`.daily().at('14:00')`)。
10
+ - **分散式鎖**: 防止任務在多台伺服器上同時執行(支援 Memory、Cache 與 Redis)。
11
+ - **節點角色過濾**: 可將任務限制在特定角色(如 `worker`)的節點上執行,或將維護任務廣播至所有匹配節點。
12
+ - **高可靠性**: 內建任務超時控制與自動重試機制(可配置延遲時間)。
13
+ - **Shell 指令支援**: 除了 TypeScript 回呼函式,也能排程執行原始 Shell 指令。
14
+ - **延遲 Cron 解析**: 對於標準表達式使用輕量級的 `SimpleCronParser`,僅在需要複雜邏輯時才加載 `cron-parser`。
15
+ - **完整的 Hook 機制**: 提供生命週期事件,用於監控任務成功、失敗、重試以及排程器狀態。
4
16
 
5
17
  ## 安裝
6
18
 
@@ -8,17 +20,26 @@
8
20
  bun add @gravito/horizon
9
21
  ```
10
22
 
23
+ ### 同儕依賴 (Peer Dependencies)
24
+ - `@gravito/core` (必需)
25
+ - `@gravito/stasis` (選填,用於 Cache/Redis 分散式鎖)
26
+
11
27
  ## 快速開始
12
28
 
29
+ ### 1. 註冊 Orbit
30
+
31
+ 在應用程式啟動流程中初始化 Horizon:
32
+
13
33
  ```typescript
34
+ import { PlanetCore } from '@gravito/core'
14
35
  import { OrbitHorizon } from '@gravito/horizon'
15
- import { OrbitCache } from '@gravito/stasis'
36
+ import { OrbitCache } from '@gravito/stasis' // 選填,用於分散式鎖
16
37
 
17
38
  await PlanetCore.boot({
18
39
  config: {
19
40
  scheduler: {
20
- lock: { driver: 'cache' },
21
- nodeRole: 'worker'
41
+ lock: { driver: 'cache' }, // 使用 @gravito/stasis 快取
42
+ nodeRole: process.env.NODE_ROLE || 'worker'
22
43
  }
23
44
  },
24
45
  orbits: [
@@ -28,13 +49,86 @@ await PlanetCore.boot({
28
49
  })
29
50
  ```
30
51
 
52
+ ### 2. 設定排程任務
53
+
54
+ 在 Service Provider 或啟動腳本中定義任務:
55
+
31
56
  ```typescript
32
- const scheduler = core.services.get('scheduler')
57
+ import { SchedulerManager } from '@gravito/horizon'
33
58
 
59
+ const scheduler = core.services.get<SchedulerManager>('scheduler')
60
+
61
+ // 基礎回呼任務
34
62
  scheduler.task('daily-cleanup', async () => {
35
63
  await db.cleanup()
36
64
  })
37
- .daily()
38
- .at('02:00')
39
- .onOneServer()
65
+ .dailyAt('02:00')
66
+ .onOneServer() // 開啟分散式鎖
67
+
68
+ // Shell 指令執行
69
+ scheduler.exec('sync-storage', 'aws s3 sync ./local s3://bucket')
70
+ .everyFiveMinutes()
71
+ .onNode('worker')
72
+ .retry(3, 5000) // 失敗重試 3 次,每次間隔 5 秒
73
+ ```
74
+
75
+ ## 排程 API
76
+
77
+ ### 頻率方法 (Frequency Methods)
78
+ - `everyMinute()`: 每分鐘執行 (`* * * * *`)
79
+ - `everyFiveMinutes()`, `everyTenMinutes()`, `everyFifteenMinutes()`, `everyThirtyMinutes()`
80
+ - `hourly()`, `hourlyAt(minute)`: 每小時的特定分鐘執行。
81
+ - `daily()`, `dailyAt('HH:mm')`: 每天執行一次。
82
+ - `weekly()`, `weeklyOn(day, 'HH:mm')`: 每週執行一次 (0=週日)。
83
+ - `monthly()`, `monthlyOn(date, 'HH:mm')`: 每月執行一次。
84
+ - `cron('expression')`: 使用自定義的 5 段式 Cron 表達式。
85
+
86
+ ### 限制與約束 (Constraints)
87
+ - `timezone('Asia/Taipei')`: 設定執行時區(預設為 UTC)。
88
+ - `onOneServer(lockTtl?)`: 確保該任務在全球環境中同一時間僅由一個實例執行。
89
+ - `onNode(role)`: 僅在當前節點角色匹配時執行。
90
+ - `runInBackground()`: 任務執行時不阻塞排程器主迴圈。
91
+
92
+ ### 可靠性與生命週期 (Reliability & Lifecycle)
93
+ - `timeout(ms)`: 設定最大執行時間(預設為 1 小時)。
94
+ - `retry(attempts, delayMs)`: 設定失敗後的重試次數。
95
+ - `onSuccess(callback)`: 任務執行成功時觸發。
96
+ - `onFailure(callback)`: 任務執行失敗時觸發。
97
+
98
+ ## CLI 使用方法
99
+
100
+ 透過 Gravito CLI 管理與運行排程器:
101
+
102
+ ### 列出所有已註冊任務
103
+ ```bash
104
+ bun run gravito schedule:list
40
105
  ```
106
+
107
+ ### 運行(Cron 模式)
108
+ 將此指令加入系統 crontab,每分鐘觸發一次排程檢查:
109
+ ```bash
110
+ * * * * * cd /app && bun run gravito schedule:run
111
+ ```
112
+
113
+ ### 運行(Daemon 模式)
114
+ 在長駐程序中每分鐘輪詢一次(適合 Docker 環境):
115
+ ```bash
116
+ bun run gravito schedule:work
117
+ ```
118
+
119
+ ## 監控 Hook
120
+
121
+ 透過 `core.hooks` 訂閱事件,以建立監控儀表板或警報系統:
122
+
123
+ | Hook | Payload | 說明 |
124
+ |------|---------|-------------|
125
+ | `scheduler:run:start` | `{ date }` | 排程檢查開始。 |
126
+ | `scheduler:run:complete` | `{ date, dueCount }` | 排程檢查結束。 |
127
+ | `scheduler:task:start` | `{ name, startTime }` | 單一任務開始執行。 |
128
+ | `scheduler:task:retry` | `{ name, attempt, error }` | 任務失敗並正在重試。 |
129
+ | `scheduler:task:success` | `{ name, duration, attempts }` | 任務成功完成。 |
130
+ | `scheduler:task:failure` | `{ name, error, duration }` | 任務在所有重試後依然失敗。 |
131
+
132
+ ## 授權條款
133
+
134
+ MIT