queue-jobs-worker 1.0.2 → 1.0.3
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/CHANGELOG.md +155 -128
- package/README.md +133 -291
- package/assets/queue-jobs-worker-github.png +0 -0
- package/dist/core/worker.d.ts.map +1 -1
- package/dist/index.cjs +321 -92
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +321 -92
- package/dist/index.js.map +1 -1
- package/dist/storage/in-memory.adapter.d.ts +3 -2
- package/dist/storage/in-memory.adapter.d.ts.map +1 -1
- package/dist/storage/mysql.adapter.d.ts +3 -2
- package/dist/storage/mysql.adapter.d.ts.map +1 -1
- package/dist/storage/postgres.adapter.d.ts +3 -2
- package/dist/storage/postgres.adapter.d.ts.map +1 -1
- package/dist/storage/redis.adapter.d.ts +3 -2
- package/dist/storage/redis.adapter.d.ts.map +1 -1
- package/dist/types/storage.types.d.ts +20 -2
- package/dist/types/storage.types.d.ts.map +1 -1
- package/dist/types/worker.types.d.ts +1 -1
- package/dist/types/worker.types.d.ts.map +1 -1
- package/package.json +110 -109
package/CHANGELOG.md
CHANGED
|
@@ -1,128 +1,155 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to **queue-jobs-worker** will be documented in this file.
|
|
4
|
-
|
|
5
|
-
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
6
|
-
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
the
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
- `
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to **queue-jobs-worker** will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|
6
|
+
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.3] — 2026-09-09
|
|
9
|
+
|
|
10
|
+
### Core
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **`Worker` — Job timeout cooperative cancellation via `AbortSignal`** ([#12](https://github.com/rafidahmed870/queue-jobs-worker/issues/12))
|
|
15
|
+
|
|
16
|
+
Previously, when a job attempt reached its configured `timeout`, the worker rejected the internal execution promise and marked the attempt as failed (or scheduled a retry), but the underlying processor `Promise` continued running in the background. This could lead to duplicate side effects when retries overlapped with timed-out attempts.
|
|
17
|
+
|
|
18
|
+
After the fix:
|
|
19
|
+
|
|
20
|
+
- `Processor` type signature is updated: `type Processor<TPayload = unknown> = (job: Job<TPayload>, signal: AbortSignal) => Promise<void>`.
|
|
21
|
+
- An `AbortController` is created for each job attempt.
|
|
22
|
+
- When job execution times out, the worker aborts the `AbortSignal` with a timeout error before rejecting the wrapper promise.
|
|
23
|
+
- User processors can monitor `signal.aborted` or pass `signal` to async operations (e.g. `fetch`, database queries, timers) for cooperative cancellation.
|
|
24
|
+
|
|
25
|
+
### Package
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
|
|
29
|
+
- **`package.json` — Added `assets` to npm package `files` distribution**
|
|
30
|
+
|
|
31
|
+
Added `"assets"` to the `"files"` list in `package.json` so header banner graphics in `README.md` display properly on npmjs.com.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## [1.0.2] — 2026-09-05
|
|
36
|
+
|
|
37
|
+
### Core
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- **`Worker` — croner added as a required dependency; invalid expressions no longer fall back to a 1-minute interval** ([#5](https://github.com/rafidahmed870/queue-jobs-worker/issues/5))
|
|
42
|
+
|
|
43
|
+
`enqueueCronNext()` previously attempted a dynamic `import("croner")` inside
|
|
44
|
+
a try/catch. If the import failed — or if the resolved `Cron` class was not a
|
|
45
|
+
function — the code silently fell back to `Date.now() + 60_000`, scheduling
|
|
46
|
+
the next run 60 seconds later regardless of the configured cron expression.
|
|
47
|
+
The same silent fallback was also triggered for invalid cron expressions that
|
|
48
|
+
caused the `Cron` constructor to throw.
|
|
49
|
+
|
|
50
|
+
After the fix:
|
|
51
|
+
|
|
52
|
+
- `croner` is now declared as a proper `dependency` in `package.json`
|
|
53
|
+
(`^10.0.1`) and imported statically, so it is always available without any
|
|
54
|
+
dynamic-import dance.
|
|
55
|
+
- If the `Cron` constructor throws (invalid expression), a descriptive
|
|
56
|
+
`worker:error` event is emitted and re-enqueue is skipped. The worker
|
|
57
|
+
remains running.
|
|
58
|
+
- If `cronInstance.nextRun()` returns `null` (the schedule has no future
|
|
59
|
+
occurrences), a `worker:error` is emitted and re-enqueue is skipped. Again,
|
|
60
|
+
the worker keeps running.
|
|
61
|
+
- The 1-minute fallback path has been removed entirely — there is no silent
|
|
62
|
+
fallback under any failure condition.
|
|
63
|
+
|
|
64
|
+
- **`Worker` — rate-limit quota no longer consumed on empty-queue polls** ([#4](https://github.com/rafidahmed870/queue-jobs-worker/issues/4))
|
|
65
|
+
|
|
66
|
+
`claimNext()` previously called `checkAndIncrementRateLimit()` before
|
|
67
|
+
attempting to claim a job. This meant every poll cycle against an empty queue
|
|
68
|
+
burned a quota slot, potentially exhausting the configured window budget
|
|
69
|
+
before any real work was done. After the fix, the storage `claim()` call
|
|
70
|
+
happens first; the rate-limit counter is only incremented when a job is
|
|
71
|
+
actually claimed for processing. If the rate limit is reached at that point
|
|
72
|
+
the lock is immediately released via `releaseLock()` so the job remains
|
|
73
|
+
reclaimable on the next window.
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
### Events
|
|
78
|
+
|
|
79
|
+
### Added
|
|
80
|
+
|
|
81
|
+
- `QueueEventEmitter` — strongly-typed lifecycle event bus shared across all components.
|
|
82
|
+
- Emits events for the full job lifecycle: enqueued, started, completed, failed, retrying, dead, stalled.
|
|
83
|
+
- All event payloads fully typed via `events.types.ts`.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
<!-- Links -->
|
|
88
|
+
[1.0.0]: https://github.com/rafidahmed870/queue-jobs-worker/releases/tag/v1.0.0
|
|
89
|
+
|
|
90
|
+
### Storage
|
|
91
|
+
|
|
92
|
+
### Fixed
|
|
93
|
+
|
|
94
|
+
- **`recoverStalledJobs()` race condition — stale recovery overwrites a live job** ([#6](https://github.com/rafidahmed870/queue-jobs-worker/issues/6))
|
|
95
|
+
|
|
96
|
+
The previous implementation used a two-phase read-then-write pattern:
|
|
97
|
+
|
|
98
|
+
1. A fetch pipeline read `lockExpiresAt` and `priority` for all active jobs.
|
|
99
|
+
2. A separate write pipeline recovered every job whose lock appeared expired.
|
|
100
|
+
|
|
101
|
+
Between those two phases a worker could complete the job, fail it, or renew
|
|
102
|
+
its lock. The write pipeline had no knowledge of that change and would
|
|
103
|
+
unconditionally overwrite the job back to `"waiting"`, causing duplicate
|
|
104
|
+
processing or data loss.
|
|
105
|
+
|
|
106
|
+
**`RedisStorageAdapter`** — the write pipeline has been replaced with a
|
|
107
|
+
per-job Lua script (`RECOVER_STALLED_LUA`) that implements a
|
|
108
|
+
**compare-and-swap (CAS)** guard. The script atomically re-reads
|
|
109
|
+
`lockExpiresAt`, `lockId`, and `status` from the hash and aborts if any of
|
|
110
|
+
the three values differ from what the caller observed in the read phase.
|
|
111
|
+
Because Redis executes Lua scripts as a single indivisible command, no
|
|
112
|
+
concurrent write can slip between the re-read and the state update. The
|
|
113
|
+
pre-filter (skip jobs whose lock has not yet expired) is preserved as an
|
|
114
|
+
optimisation to avoid unnecessary Lua round-trips.
|
|
115
|
+
|
|
116
|
+
**`InMemoryStorageAdapter`** — all operations run within a single event-loop
|
|
117
|
+
tick so the race is theoretical, but an equivalent CAS guard has been added
|
|
118
|
+
for consistency: `lockId` and `lockExpiresAt` are snapshotted at decision
|
|
119
|
+
time and re-validated immediately before the write. Any interleaving that
|
|
120
|
+
mutated those fields will cause the recovery to be skipped.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## [1.0.1] — 2026-08-31
|
|
125
|
+
|
|
126
|
+
### Core
|
|
127
|
+
|
|
128
|
+
### Fixed
|
|
129
|
+
|
|
130
|
+
- **`Worker.stop()` — clarified `releaseLock()` behavior in shutdown comment** ([#1](https://github.com/rafidahmed870/queue-jobs-worker/issues/1))
|
|
131
|
+
|
|
132
|
+
The inline comment in `worker.ts` now correctly explains that `releaseLock()`
|
|
133
|
+
sets `lockExpiresAt` to an already-expired timestamp (not null/empty), so
|
|
134
|
+
`recoverStalledJobs()` on any worker will immediately reclaim the job on the
|
|
135
|
+
next stall-check cycle.
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
### Events
|
|
140
|
+
|
|
141
|
+
### Added
|
|
142
|
+
|
|
143
|
+
- `QueueEventEmitter` — strongly-typed lifecycle event bus shared across all components.
|
|
144
|
+
- Emits events for the full job lifecycle: enqueued, started, completed, failed, retrying, dead, stalled.
|
|
145
|
+
- All event payloads fully typed via `events.types.ts`.
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
<!-- Links -->
|
|
150
|
+
|
|
151
|
+
[1.0.3]: https://github.com/rafidahmed870/queue-jobs-worker/compare/v1.0.2...v1.0.3
|
|
152
|
+
[1.0.2]: https://github.com/rafidahmed870/queue-jobs-worker/compare/v1.0.1...v1.0.2
|
|
153
|
+
[1.0.0]: https://github.com/rafidahmed870/queue-jobs-worker/releases/tag/v1.0.0
|
|
154
|
+
[1.0.1]: https://github.com/rafidahmed870/queue-jobs-worker/compare/v1.0.0...v1.0.1
|
|
155
|
+
[1.0.0]: https://github.com/rafidahmed870/queue-jobs-worker/releases/tag/v1.0.0
|