@chidchanun/bcp 0.2.8 → 0.2.10
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 +164 -163
- package/docs/README.md +48 -139
- package/docs/api-manifest.json +4 -2
- package/docs/api-reference.md +92 -242
- package/docs/docs-web-manifest.json +7 -3
- package/docs/durable-jobs.md +359 -0
- package/docs/job-scheduling.md +357 -0
- package/docs/platform-manifest.json +23 -4
- package/docs/releases/0.2.10.md +148 -0
- package/docs/releases/0.2.9.md +162 -0
- package/package.json +2 -2
- package/packages/client/src/jobs.mjs +2289 -0
- package/packages/client/src/jobs.ts +34 -0
- package/packages/server/src/job-scheduler.ts +1144 -0
- package/packages/server/src/jobs-redis.ts +1226 -0
- package/packages/server/src/jobs.ts +768 -128
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"framework": "bcp",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.10",
|
|
5
5
|
"releaseState": "unreleased",
|
|
6
|
-
"baseline": "
|
|
6
|
+
"baseline": "durable-jobs-platform",
|
|
7
7
|
"runtime": {
|
|
8
8
|
"node": ">=24.11.0",
|
|
9
9
|
"react": "19",
|
|
@@ -73,6 +73,23 @@
|
|
|
73
73
|
"jobRetries": true,
|
|
74
74
|
"jobWorkerConcurrency": true,
|
|
75
75
|
"jobCancellation": true,
|
|
76
|
+
"jobSchedulingPlatform": true,
|
|
77
|
+
"recurringJobs": true,
|
|
78
|
+
"cronScheduling": true,
|
|
79
|
+
"intervalScheduling": true,
|
|
80
|
+
"jobScheduleStoreContract": true,
|
|
81
|
+
"schedulerLeases": true,
|
|
82
|
+
"scheduledRunDeduplication": true,
|
|
83
|
+
"durableJobsPlatform": true,
|
|
84
|
+
"redisJobQueueAdapter": true,
|
|
85
|
+
"redisJobScheduleStore": true,
|
|
86
|
+
"jobVisibilityTimeout": true,
|
|
87
|
+
"jobHeartbeats": true,
|
|
88
|
+
"staleJobRecovery": true,
|
|
89
|
+
"deadLetterQueue": true,
|
|
90
|
+
"deadLetterRequeue": true,
|
|
91
|
+
"jobRetentionCleanup": true,
|
|
92
|
+
"jobQueueStatistics": true,
|
|
76
93
|
"databaseMigrations": true,
|
|
77
94
|
"databaseAdapterContract": true,
|
|
78
95
|
"databasePostgresql": true,
|
|
@@ -112,7 +129,7 @@
|
|
|
112
129
|
"s3-compatible"
|
|
113
130
|
],
|
|
114
131
|
"compatibility": {
|
|
115
|
-
"previousBaseline": "0.2.
|
|
132
|
+
"previousBaseline": "0.2.9",
|
|
116
133
|
"intentionalBreakingChangesFromPreviousBaseline": false,
|
|
117
134
|
"migrationGuide": "migration-0.2.md"
|
|
118
135
|
},
|
|
@@ -130,7 +147,9 @@
|
|
|
130
147
|
"authorizationSecurity": "authorization-security.md",
|
|
131
148
|
"observability": "observability.md",
|
|
132
149
|
"backgroundJobs": "background-jobs.md",
|
|
150
|
+
"jobScheduling": "job-scheduling.md",
|
|
151
|
+
"durableJobs": "durable-jobs.md",
|
|
133
152
|
"migrationGuide": "migration-0.2.md",
|
|
134
|
-
"releaseNotes": "releases/0.2.
|
|
153
|
+
"releaseNotes": "releases/0.2.10.md"
|
|
135
154
|
}
|
|
136
155
|
}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# 0.2.10 — Durable Jobs Platform
|
|
2
|
+
|
|
3
|
+
**State:** unreleased development target
|
|
4
|
+
|
|
5
|
+
BCP Framework `0.2.10` extends the Background Jobs and Job Scheduling platforms with durable worker lifecycle contracts, dead-letter handling, retention/statistics APIs and Redis-compatible reference adapters.
|
|
6
|
+
|
|
7
|
+
## Highlights
|
|
8
|
+
|
|
9
|
+
### Durable worker leases
|
|
10
|
+
|
|
11
|
+
Workers can reserve jobs with a visibility timeout and stable worker identity.
|
|
12
|
+
|
|
13
|
+
New job record metadata:
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
leaseOwner
|
|
17
|
+
leaseUntil
|
|
18
|
+
heartbeatAt
|
|
19
|
+
recoveredAt
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
When an adapter supports `heartbeat()`, BCP workers renew the lease while a handler is running.
|
|
23
|
+
|
|
24
|
+
When an adapter supports `recoverStale()`, expired running jobs can be returned to the queue for at-least-once recovery.
|
|
25
|
+
|
|
26
|
+
### Dead-letter queue
|
|
27
|
+
|
|
28
|
+
Retry-exhausted jobs remain terminal `failed` jobs and can also be indexed in the adapter DLQ.
|
|
29
|
+
|
|
30
|
+
New queue APIs:
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
jobs.deadLetters()
|
|
34
|
+
jobs.requeueDeadLetter()
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The built-in memory adapter and Redis-compatible adapter implement both APIs.
|
|
38
|
+
|
|
39
|
+
### Queue maintenance
|
|
40
|
+
|
|
41
|
+
New operational APIs:
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
jobs.stats()
|
|
45
|
+
jobs.cleanup()
|
|
46
|
+
jobs.recoverStale()
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Statistics include queue state totals and DLQ depth.
|
|
50
|
+
|
|
51
|
+
Cleanup can remove old succeeded, failed and cancelled records after an application-defined retention period.
|
|
52
|
+
|
|
53
|
+
### Redis-compatible queue adapter
|
|
54
|
+
|
|
55
|
+
New public API:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
createRedisJobQueueAdapter()
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
The adapter consumes a minimal application-owned Redis command client exposing:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
sendCommand(command: string[]): Promise<unknown>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
BCP does not add a Redis client runtime dependency.
|
|
68
|
+
|
|
69
|
+
The Redis adapter uses atomic Lua operations for:
|
|
70
|
+
|
|
71
|
+
- duplicate-safe enqueue,
|
|
72
|
+
- job reservation,
|
|
73
|
+
- complete/fail/cancel transitions,
|
|
74
|
+
- heartbeat lease renewal,
|
|
75
|
+
- stale-job recovery,
|
|
76
|
+
- DLQ requeue,
|
|
77
|
+
- retention cleanup.
|
|
78
|
+
|
|
79
|
+
The default Redis namespace is `bcp:{jobs}` so all adapter keys share one Redis Cluster hash slot.
|
|
80
|
+
|
|
81
|
+
### Redis-compatible scheduler store
|
|
82
|
+
|
|
83
|
+
New public API:
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
createRedisJobScheduleStore()
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
The scheduler store implements the `0.2.9` `JobScheduleStore` contract on shared Redis state and uses atomic schedule leasing for multi-instance schedulers.
|
|
90
|
+
|
|
91
|
+
### Compiled npm runtime
|
|
92
|
+
|
|
93
|
+
The published `bcp/jobs` default runtime continues to use generated `jobs.mjs` rather than requiring consumers to execute framework TypeScript directly.
|
|
94
|
+
|
|
95
|
+
The Durable Jobs package smoke imports that compiled runtime and verifies the new public APIs.
|
|
96
|
+
|
|
97
|
+
## Public API additions
|
|
98
|
+
|
|
99
|
+
`bcp/jobs` adds:
|
|
100
|
+
|
|
101
|
+
```text
|
|
102
|
+
createRedisJobQueueAdapter
|
|
103
|
+
createRedisJobScheduleStore
|
|
104
|
+
RedisCommandClient
|
|
105
|
+
RedisJobQueueAdapter
|
|
106
|
+
RedisJobScheduleStore
|
|
107
|
+
RedisJobsAdapterOptions
|
|
108
|
+
|
|
109
|
+
CleanupJobsOptions
|
|
110
|
+
DeadLetterJobRecord
|
|
111
|
+
HeartbeatJobOptions
|
|
112
|
+
JobQueueStats
|
|
113
|
+
ProcessNextJobOptions
|
|
114
|
+
RecoverStaleJobsOptions
|
|
115
|
+
RequeueDeadLetterOptions
|
|
116
|
+
ReserveJobOptions
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Existing queue and scheduling APIs remain available.
|
|
120
|
+
|
|
121
|
+
## Compatibility
|
|
122
|
+
|
|
123
|
+
The release is intended to be backward compatible with `0.2.9`.
|
|
124
|
+
|
|
125
|
+
Existing `JobQueueAdapter` implementations remain valid because durable capabilities are optional and the new reservation metadata is provided as an optional second parameter to `reserve()`.
|
|
126
|
+
|
|
127
|
+
Adapters that need production visibility-timeout semantics should implement the new durable methods.
|
|
128
|
+
|
|
129
|
+
## Delivery semantics
|
|
130
|
+
|
|
131
|
+
Durable jobs use at-least-once processing semantics.
|
|
132
|
+
|
|
133
|
+
Applications should make side-effecting handlers idempotent when duplicate execution would be unsafe.
|
|
134
|
+
|
|
135
|
+
## Validation target
|
|
136
|
+
|
|
137
|
+
Before tagging/publishing `0.2.10`, run:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npm run typecheck
|
|
141
|
+
npm run test:unit
|
|
142
|
+
npm run test:integration
|
|
143
|
+
npm run test:e2e
|
|
144
|
+
npm run test:package
|
|
145
|
+
npm run rc:check
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The final release tag must point to the exact commit that passed the complete RC sequence.
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# BCP Framework 0.2.9 — Job Scheduling Platform
|
|
2
|
+
|
|
3
|
+
> Release state: unreleased until the complete RC workflow passes, the release commit is tagged `v0.2.9`, and npm publication completes.
|
|
4
|
+
|
|
5
|
+
BCP Framework `0.2.9` extends the `0.2.8` Background Jobs Platform with recurring schedules, dependency-free UTC cron expressions and a lease-aware schedule-store contract.
|
|
6
|
+
|
|
7
|
+
## Highlights
|
|
8
|
+
|
|
9
|
+
- Added `createJobScheduler()` through `bcp/jobs`.
|
|
10
|
+
- Added interval-based recurring jobs with `everyMs`.
|
|
11
|
+
- Added five-field UTC cron expressions.
|
|
12
|
+
- Added `nextCronTime()` and `nextScheduleTime()` helpers.
|
|
13
|
+
- Added `JobScheduleStore` for durable/shared scheduler state.
|
|
14
|
+
- Added `createMemoryJobScheduleStore()` for local development and tests.
|
|
15
|
+
- Added atomic-style `acquireDue()` lease semantics to the schedule-store contract.
|
|
16
|
+
- Added deterministic scheduled queue ids to reduce duplicate enqueue risk.
|
|
17
|
+
- Added scheduler polling lifecycle with `start()`, `stop()` and `close()`.
|
|
18
|
+
- Added schedule inspection, removal and stable-id upsert behavior.
|
|
19
|
+
- Added unit, package-smoke and platform-contract coverage.
|
|
20
|
+
|
|
21
|
+
## Scheduling API
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import {
|
|
25
|
+
createJobQueue,
|
|
26
|
+
createJobScheduler,
|
|
27
|
+
} from "bcp/jobs";
|
|
28
|
+
|
|
29
|
+
const jobs =
|
|
30
|
+
createJobQueue();
|
|
31
|
+
|
|
32
|
+
const scheduler =
|
|
33
|
+
createJobScheduler({
|
|
34
|
+
queue: jobs,
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Interval:
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
await scheduler.schedule(
|
|
42
|
+
"cache.cleanup",
|
|
43
|
+
{},
|
|
44
|
+
{
|
|
45
|
+
id: "cache-cleanup",
|
|
46
|
+
everyMs: 300_000,
|
|
47
|
+
}
|
|
48
|
+
);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Cron:
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
await scheduler.schedule(
|
|
55
|
+
"report.weekday",
|
|
56
|
+
{},
|
|
57
|
+
{
|
|
58
|
+
cron: "30 9 * * 1-5",
|
|
59
|
+
}
|
|
60
|
+
);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Cron evaluation is UTC in `0.2.9`.
|
|
64
|
+
|
|
65
|
+
## Cron syntax
|
|
66
|
+
|
|
67
|
+
Five fields are supported:
|
|
68
|
+
|
|
69
|
+
```text
|
|
70
|
+
minute hour day-of-month month day-of-week
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Supported tokens:
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
*
|
|
77
|
+
*/n
|
|
78
|
+
comma-separated values
|
|
79
|
+
ranges
|
|
80
|
+
range steps
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Day of week accepts `0` and `7` for Sunday. When both day-of-month and day-of-week are restricted, standard cron OR semantics are used.
|
|
84
|
+
|
|
85
|
+
## Scheduler lifecycle
|
|
86
|
+
|
|
87
|
+
Long-running scheduler:
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
const runner =
|
|
91
|
+
scheduler.start({
|
|
92
|
+
pollIntervalMs: 1_000,
|
|
93
|
+
leaseMs: 30_000,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// shutdown
|
|
97
|
+
await runner.stop();
|
|
98
|
+
await scheduler.close();
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`runDue()` is also available for tests, external control loops and deterministic execution.
|
|
102
|
+
|
|
103
|
+
## Multi-instance contract
|
|
104
|
+
|
|
105
|
+
`JobScheduleStore.acquireDue()` is the scheduler concurrency boundary. Durable implementations should atomically claim due schedules with a lease owner and expiry.
|
|
106
|
+
|
|
107
|
+
The default memory store is process-local and is not suitable for schedules that must survive restarts or coordinate across multiple containers.
|
|
108
|
+
|
|
109
|
+
Production multi-instance systems will normally pair:
|
|
110
|
+
|
|
111
|
+
```text
|
|
112
|
+
shared JobScheduleStore
|
|
113
|
+
+
|
|
114
|
+
shared JobQueueAdapter
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
`0.2.9` intentionally leaves provider choice to the application instead of forcing Redis, PostgreSQL or another queue/scheduler backend.
|
|
118
|
+
|
|
119
|
+
## Duplicate protection
|
|
120
|
+
|
|
121
|
+
Each scheduled occurrence uses a deterministic queue id:
|
|
122
|
+
|
|
123
|
+
```text
|
|
124
|
+
schedule:<schedule-id>:<scheduled-for-timestamp>
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
This complements store leasing and protects the built-in queue from enqueuing the same occurrence twice if the same schedule occurrence is retried.
|
|
128
|
+
|
|
129
|
+
## Compatibility
|
|
130
|
+
|
|
131
|
+
This milestone is additive. There are no intentional breaking changes from `0.2.8`.
|
|
132
|
+
|
|
133
|
+
Existing `createJobQueue()`, handlers, delayed jobs, retries, workers and adapters continue to work without scheduling enabled.
|
|
134
|
+
|
|
135
|
+
## Documentation
|
|
136
|
+
|
|
137
|
+
New guide:
|
|
138
|
+
|
|
139
|
+
```text
|
|
140
|
+
docs/job-scheduling.md
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Background jobs remain documented in:
|
|
144
|
+
|
|
145
|
+
```text
|
|
146
|
+
docs/background-jobs.md
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Validation
|
|
150
|
+
|
|
151
|
+
Before tagging/publishing `0.2.9`, run:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
npm run typecheck
|
|
155
|
+
npm run test:unit
|
|
156
|
+
npm run test:integration
|
|
157
|
+
npm run test:e2e
|
|
158
|
+
npm run test:package
|
|
159
|
+
npm run rc:check
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The release tag must point to the exact commit that passed the complete RC sequence.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chidchanun/bcp",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.10",
|
|
4
4
|
"description": "BCP Framework - a React full-stack framework with file-based routing, SSR, APIs, middleware, islands, caching and standalone production builds.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"./jobs": {
|
|
66
66
|
"types": "./packages/client/src/jobs.ts",
|
|
67
67
|
"browser": "./packages/client/src/server-only.browser.mjs",
|
|
68
|
-
"default": "./packages/client/src/jobs.
|
|
68
|
+
"default": "./packages/client/src/jobs.mjs"
|
|
69
69
|
},
|
|
70
70
|
"./observability": {
|
|
71
71
|
"types": "./packages/client/src/observability.ts",
|