@gravito/horizon 3.0.0 → 3.2.0
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 +93 -80
- package/README.zh-TW.md +102 -8
- package/dist/index.cjs +634 -243
- package/dist/index.d.cts +661 -137
- package/dist/index.d.ts +661 -137
- package/dist/index.js +637 -246
- package/package.json +7 -5
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**:
|
|
8
|
-
- **Distributed Locking**: Prevents duplicate task execution across multiple servers (supports Memory, Cache, Redis).
|
|
9
|
-
- **
|
|
10
|
-
- **
|
|
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
|
|
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
|
|
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:
|
|
41
|
+
lock: { driver: 'cache' }, // Uses @gravito/stasis cache
|
|
42
|
+
nodeRole: process.env.NODE_ROLE || 'worker'
|
|
31
43
|
}
|
|
32
44
|
},
|
|
33
45
|
orbits: [
|
|
34
|
-
OrbitCache,
|
|
46
|
+
OrbitCache,
|
|
35
47
|
OrbitHorizon
|
|
36
48
|
]
|
|
37
49
|
})
|
|
38
50
|
```
|
|
39
51
|
|
|
40
|
-
2. Schedule
|
|
52
|
+
### 2. Schedule Tasks
|
|
53
|
+
|
|
54
|
+
Define tasks in your service providers or bootstrap files:
|
|
41
55
|
|
|
42
56
|
```typescript
|
|
43
|
-
|
|
44
|
-
|
|
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
|
-
|
|
63
|
+
await db.cleanup()
|
|
48
64
|
})
|
|
49
|
-
.
|
|
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
|
-
##
|
|
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
|
-
|
|
57
|
-
|
|
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
|
|
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
|
|
127
|
+
Add this to your system crontab to trigger the scheduler every minute:
|
|
71
128
|
```bash
|
|
72
|
-
* * * * * cd /
|
|
129
|
+
* * * * * cd /app && bun run gravito schedule:run
|
|
73
130
|
```
|
|
74
131
|
|
|
75
132
|
### Run (Daemon Mode)
|
|
76
|
-
|
|
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
|
-
##
|
|
138
|
+
## Monitoring Hooks
|
|
89
139
|
|
|
90
|
-
|
|
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
|
|
95
|
-
| `scheduler:run:complete` | `{ date, dueCount }` | Scheduler
|
|
96
|
-
| `scheduler:task:start` | `{ name, startTime }` | Individual task
|
|
97
|
-
| `scheduler:task:
|
|
98
|
-
| `scheduler:task:
|
|
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
|
-
##
|
|
152
|
+
## License
|
|
101
153
|
|
|
102
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
.
|
|
38
|
-
.
|
|
39
|
-
|
|
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
|