@boringnode/queue 0.1.0 → 0.3.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 +230 -358
- package/build/{chunk-US7THLSZ.js → chunk-LI2ZMCNO.js} +17 -3
- package/build/chunk-LI2ZMCNO.js.map +1 -0
- package/build/{chunk-NPQKBCCY.js → chunk-PBGPIFI5.js} +15 -1
- package/build/chunk-PBGPIFI5.js.map +1 -0
- package/build/{index-2Ng_OpVK.d.ts → index-PDfE6h8d.d.ts} +454 -36
- package/build/index.d.ts +8 -4
- package/build/index.js +313 -46
- package/build/index.js.map +1 -1
- package/build/src/contracts/adapter.d.ts +1 -1
- package/build/src/drivers/knex_adapter.d.ts +6 -3
- package/build/src/drivers/knex_adapter.js +94 -12
- package/build/src/drivers/knex_adapter.js.map +1 -1
- package/build/src/drivers/redis_adapter.d.ts +6 -3
- package/build/src/drivers/redis_adapter.js +337 -96
- package/build/src/drivers/redis_adapter.js.map +1 -1
- package/build/src/drivers/sync_adapter.d.ts +6 -3
- package/build/src/drivers/sync_adapter.js +18 -6
- package/build/src/drivers/sync_adapter.js.map +1 -1
- package/build/src/types/index.d.ts +1 -1
- package/build/src/types/main.d.ts +1 -1
- package/package.json +3 -2
- package/build/chunk-NPQKBCCY.js.map +0 -1
- package/build/chunk-US7THLSZ.js.map +0 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
calculateScore
|
|
3
|
-
|
|
2
|
+
calculateScore,
|
|
3
|
+
resolveRetention
|
|
4
|
+
} from "../../chunk-PBGPIFI5.js";
|
|
4
5
|
import {
|
|
5
6
|
DEFAULT_PRIORITY
|
|
6
7
|
} from "../../chunk-SMOKFZ46.js";
|
|
@@ -11,26 +12,51 @@ import { Redis } from "ioredis";
|
|
|
11
12
|
var redisKey = "jobs";
|
|
12
13
|
var schedulesKey = "schedules";
|
|
13
14
|
var schedulesIndexKey = "schedules::index";
|
|
15
|
+
var PUSH_JOB_SCRIPT = `
|
|
16
|
+
local data_key = KEYS[1]
|
|
17
|
+
local pending_key = KEYS[2]
|
|
18
|
+
local job_id = ARGV[1]
|
|
19
|
+
local job_data = ARGV[2]
|
|
20
|
+
local score = tonumber(ARGV[3])
|
|
21
|
+
|
|
22
|
+
redis.call('HSET', data_key, job_id, job_data)
|
|
23
|
+
redis.call('ZADD', pending_key, score, job_id)
|
|
24
|
+
|
|
25
|
+
return 1
|
|
26
|
+
`;
|
|
27
|
+
var PUSH_DELAYED_JOB_SCRIPT = `
|
|
28
|
+
local data_key = KEYS[1]
|
|
29
|
+
local delayed_key = KEYS[2]
|
|
30
|
+
local job_id = ARGV[1]
|
|
31
|
+
local job_data = ARGV[2]
|
|
32
|
+
local execute_at = tonumber(ARGV[3])
|
|
33
|
+
|
|
34
|
+
redis.call('HSET', data_key, job_id, job_data)
|
|
35
|
+
redis.call('ZADD', delayed_key, execute_at, job_id)
|
|
36
|
+
|
|
37
|
+
return 1
|
|
38
|
+
`;
|
|
14
39
|
var ACQUIRE_JOB_SCRIPT = `
|
|
15
|
-
local
|
|
16
|
-
local
|
|
17
|
-
local
|
|
40
|
+
local data_key = KEYS[1]
|
|
41
|
+
local pending_key = KEYS[2]
|
|
42
|
+
local active_key = KEYS[3]
|
|
43
|
+
local delayed_key = KEYS[4]
|
|
18
44
|
local worker_id = ARGV[1]
|
|
19
|
-
local now = ARGV[2]
|
|
20
|
-
|
|
21
|
-
--
|
|
22
|
-
local
|
|
23
|
-
if #
|
|
24
|
-
for i = 1, #
|
|
25
|
-
local
|
|
26
|
-
local
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
45
|
+
local now = tonumber(ARGV[2])
|
|
46
|
+
|
|
47
|
+
-- Process delayed jobs: move ready jobs to pending
|
|
48
|
+
local ready_job_ids = redis.call('ZRANGEBYSCORE', delayed_key, 0, now)
|
|
49
|
+
if #ready_job_ids > 0 then
|
|
50
|
+
for i = 1, #ready_job_ids do
|
|
51
|
+
local job_id = ready_job_ids[i]
|
|
52
|
+
local job_data = redis.call('HGET', data_key, job_id)
|
|
53
|
+
if job_data then
|
|
54
|
+
local job = cjson.decode(job_data)
|
|
55
|
+
local priority = job.priority or 5
|
|
56
|
+
local score = priority * 10000000000000 + now
|
|
57
|
+
redis.call('ZADD', pending_key, score, job_id)
|
|
58
|
+
redis.call('ZREM', delayed_key, job_id)
|
|
59
|
+
end
|
|
34
60
|
end
|
|
35
61
|
end
|
|
36
62
|
|
|
@@ -40,76 +66,139 @@ var ACQUIRE_JOB_SCRIPT = `
|
|
|
40
66
|
return nil
|
|
41
67
|
end
|
|
42
68
|
|
|
43
|
-
local
|
|
44
|
-
local
|
|
69
|
+
local job_id = result[1]
|
|
70
|
+
local job_data = redis.call('HGET', data_key, job_id)
|
|
71
|
+
if not job_data then
|
|
72
|
+
return nil
|
|
73
|
+
end
|
|
45
74
|
|
|
46
|
-
-- Store in active hash
|
|
75
|
+
-- Store in active hash (without data, it's in data_key)
|
|
47
76
|
local active_data = cjson.encode({
|
|
48
77
|
workerId = worker_id,
|
|
49
|
-
acquiredAt =
|
|
50
|
-
data = job
|
|
78
|
+
acquiredAt = now
|
|
51
79
|
})
|
|
52
|
-
redis.call('HSET', active_key,
|
|
80
|
+
redis.call('HSET', active_key, job_id, active_data)
|
|
53
81
|
|
|
54
82
|
-- Return job with acquiredAt
|
|
55
|
-
job
|
|
83
|
+
local job = cjson.decode(job_data)
|
|
84
|
+
job.acquiredAt = now
|
|
56
85
|
return cjson.encode(job)
|
|
57
86
|
`;
|
|
58
|
-
var
|
|
59
|
-
local
|
|
87
|
+
var REMOVE_JOB_SCRIPT = `
|
|
88
|
+
local data_key = KEYS[1]
|
|
89
|
+
local active_key = KEYS[2]
|
|
60
90
|
local job_id = ARGV[1]
|
|
61
91
|
|
|
92
|
+
if redis.call('HEXISTS', active_key, job_id) == 0 then
|
|
93
|
+
return 0
|
|
94
|
+
end
|
|
95
|
+
|
|
62
96
|
redis.call('HDEL', active_key, job_id)
|
|
97
|
+
redis.call('HDEL', data_key, job_id)
|
|
98
|
+
|
|
63
99
|
return 1
|
|
64
100
|
`;
|
|
65
|
-
var
|
|
66
|
-
local
|
|
101
|
+
var FINALIZE_JOB_SCRIPT = `
|
|
102
|
+
local data_key = KEYS[1]
|
|
103
|
+
local active_key = KEYS[2]
|
|
104
|
+
local history_key = KEYS[3]
|
|
105
|
+
local index_key = KEYS[4]
|
|
67
106
|
local job_id = ARGV[1]
|
|
107
|
+
local now = tonumber(ARGV[2])
|
|
108
|
+
local max_age = tonumber(ARGV[3])
|
|
109
|
+
local max_count = tonumber(ARGV[4])
|
|
110
|
+
local error_message = ARGV[5]
|
|
111
|
+
|
|
112
|
+
-- Verify job is active
|
|
113
|
+
if redis.call('HEXISTS', active_key, job_id) == 0 then
|
|
114
|
+
return 0
|
|
115
|
+
end
|
|
68
116
|
|
|
117
|
+
-- Remove from active
|
|
69
118
|
redis.call('HDEL', active_key, job_id)
|
|
119
|
+
|
|
120
|
+
-- Store finalization info (data stays in data_key)
|
|
121
|
+
local record = {
|
|
122
|
+
finishedAt = now
|
|
123
|
+
}
|
|
124
|
+
if error_message and error_message ~= '' then
|
|
125
|
+
record.error = error_message
|
|
126
|
+
end
|
|
127
|
+
redis.call('HSET', history_key, job_id, cjson.encode(record))
|
|
128
|
+
redis.call('ZADD', index_key, now, job_id)
|
|
129
|
+
|
|
130
|
+
-- Prune by age
|
|
131
|
+
if max_age and max_age > 0 then
|
|
132
|
+
local cutoff = now - max_age
|
|
133
|
+
local expired = redis.call('ZRANGEBYSCORE', index_key, 0, cutoff)
|
|
134
|
+
if #expired > 0 then
|
|
135
|
+
redis.call('ZREM', index_key, unpack(expired))
|
|
136
|
+
redis.call('HDEL', history_key, unpack(expired))
|
|
137
|
+
redis.call('HDEL', data_key, unpack(expired))
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
-- Prune by count
|
|
142
|
+
if max_count and max_count > 0 then
|
|
143
|
+
local size = tonumber(redis.call('ZCARD', index_key))
|
|
144
|
+
if size > max_count then
|
|
145
|
+
local excess = size - max_count
|
|
146
|
+
local stale = redis.call('ZRANGE', index_key, 0, excess - 1)
|
|
147
|
+
if #stale > 0 then
|
|
148
|
+
redis.call('ZREM', index_key, unpack(stale))
|
|
149
|
+
redis.call('HDEL', history_key, unpack(stale))
|
|
150
|
+
redis.call('HDEL', data_key, unpack(stale))
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
70
155
|
return 1
|
|
71
156
|
`;
|
|
72
157
|
var RETRY_JOB_SCRIPT = `
|
|
73
|
-
local
|
|
74
|
-
local
|
|
75
|
-
local
|
|
158
|
+
local data_key = KEYS[1]
|
|
159
|
+
local active_key = KEYS[2]
|
|
160
|
+
local pending_key = KEYS[3]
|
|
161
|
+
local delayed_key = KEYS[4]
|
|
76
162
|
local job_id = ARGV[1]
|
|
77
163
|
local retry_at = tonumber(ARGV[2])
|
|
78
164
|
local now = tonumber(ARGV[3])
|
|
79
165
|
|
|
80
|
-
--
|
|
81
|
-
|
|
82
|
-
if not active_data then
|
|
166
|
+
-- Verify job is active
|
|
167
|
+
if redis.call('HEXISTS', active_key, job_id) == 0 then
|
|
83
168
|
return 0
|
|
84
169
|
end
|
|
85
170
|
|
|
86
|
-
|
|
87
|
-
local
|
|
171
|
+
-- Get job data
|
|
172
|
+
local job_data = redis.call('HGET', data_key, job_id)
|
|
173
|
+
if not job_data then
|
|
174
|
+
return 0
|
|
175
|
+
end
|
|
88
176
|
|
|
89
177
|
-- Remove from active
|
|
90
178
|
redis.call('HDEL', active_key, job_id)
|
|
91
179
|
|
|
92
|
-
-- Increment attempts
|
|
180
|
+
-- Increment attempts and update data
|
|
181
|
+
local job = cjson.decode(job_data)
|
|
93
182
|
job.attempts = (job.attempts or 0) + 1
|
|
94
|
-
|
|
95
|
-
local job_data = cjson.encode(job)
|
|
183
|
+
redis.call('HSET', data_key, job_id, cjson.encode(job))
|
|
96
184
|
|
|
97
185
|
-- Add back to pending or delayed
|
|
98
186
|
if retry_at and retry_at > now then
|
|
99
|
-
redis.call('ZADD', delayed_key, retry_at,
|
|
187
|
+
redis.call('ZADD', delayed_key, retry_at, job_id)
|
|
100
188
|
else
|
|
101
189
|
-- Score = priority * 1e13 + timestamp
|
|
102
190
|
-- Lower score = higher priority, FIFO within same priority
|
|
103
191
|
local priority = job.priority or 5
|
|
104
192
|
local score = priority * 10000000000000 + now
|
|
105
|
-
redis.call('ZADD', pending_key, score,
|
|
193
|
+
redis.call('ZADD', pending_key, score, job_id)
|
|
106
194
|
end
|
|
107
195
|
|
|
108
196
|
return 1
|
|
109
197
|
`;
|
|
110
198
|
var RECOVER_STALLED_JOBS_SCRIPT = `
|
|
111
|
-
local
|
|
112
|
-
local
|
|
199
|
+
local data_key = KEYS[1]
|
|
200
|
+
local active_key = KEYS[2]
|
|
201
|
+
local pending_key = KEYS[3]
|
|
113
202
|
local now = tonumber(ARGV[1])
|
|
114
203
|
local stalled_threshold = tonumber(ARGV[2])
|
|
115
204
|
local max_stalled_count = tonumber(ARGV[3])
|
|
@@ -128,31 +217,87 @@ var RECOVER_STALLED_JOBS_SCRIPT = `
|
|
|
128
217
|
|
|
129
218
|
-- Check if job is stalled
|
|
130
219
|
if active.acquiredAt < stalled_cutoff then
|
|
131
|
-
local
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
--
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
220
|
+
local job_data = redis.call('HGET', data_key, job_id)
|
|
221
|
+
if job_data then
|
|
222
|
+
local job = cjson.decode(job_data)
|
|
223
|
+
local current_stalled_count = job.stalledCount or 0
|
|
224
|
+
|
|
225
|
+
-- Remove from active hash
|
|
226
|
+
redis.call('HDEL', active_key, job_id)
|
|
227
|
+
|
|
228
|
+
-- Check if job has exceeded max stalled count
|
|
229
|
+
if current_stalled_count >= max_stalled_count then
|
|
230
|
+
-- Job failed permanently, remove data too
|
|
231
|
+
redis.call('HDEL', data_key, job_id)
|
|
232
|
+
else
|
|
233
|
+
-- Recover: increment stalledCount and put back in pending
|
|
234
|
+
job.stalledCount = current_stalled_count + 1
|
|
235
|
+
redis.call('HSET', data_key, job_id, cjson.encode(job))
|
|
236
|
+
-- Score = priority * 1e13 + timestamp
|
|
237
|
+
local priority = job.priority or 5
|
|
238
|
+
local score = priority * 10000000000000 + now
|
|
239
|
+
redis.call('ZADD', pending_key, score, job_id)
|
|
240
|
+
recovered = recovered + 1
|
|
241
|
+
end
|
|
150
242
|
end
|
|
151
243
|
end
|
|
152
244
|
end
|
|
153
245
|
|
|
154
246
|
return recovered
|
|
155
247
|
`;
|
|
248
|
+
var GET_JOB_SCRIPT = `
|
|
249
|
+
local data_key = KEYS[1]
|
|
250
|
+
local pending_key = KEYS[2]
|
|
251
|
+
local delayed_key = KEYS[3]
|
|
252
|
+
local active_key = KEYS[4]
|
|
253
|
+
local completed_key = KEYS[5]
|
|
254
|
+
local failed_key = KEYS[6]
|
|
255
|
+
local job_id = ARGV[1]
|
|
256
|
+
|
|
257
|
+
local job_data = redis.call('HGET', data_key, job_id)
|
|
258
|
+
if not job_data then
|
|
259
|
+
return nil
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
local status = nil
|
|
263
|
+
local finished_at = nil
|
|
264
|
+
local error_msg = nil
|
|
265
|
+
|
|
266
|
+
-- Check status in order
|
|
267
|
+
if redis.call('HEXISTS', active_key, job_id) == 1 then
|
|
268
|
+
status = 'active'
|
|
269
|
+
elseif redis.call('ZSCORE', pending_key, job_id) then
|
|
270
|
+
status = 'pending'
|
|
271
|
+
elseif redis.call('ZSCORE', delayed_key, job_id) then
|
|
272
|
+
status = 'delayed'
|
|
273
|
+
else
|
|
274
|
+
local completed_data = redis.call('HGET', completed_key, job_id)
|
|
275
|
+
if completed_data then
|
|
276
|
+
status = 'completed'
|
|
277
|
+
local record = cjson.decode(completed_data)
|
|
278
|
+
finished_at = record.finishedAt
|
|
279
|
+
else
|
|
280
|
+
local failed_data = redis.call('HGET', failed_key, job_id)
|
|
281
|
+
if failed_data then
|
|
282
|
+
status = 'failed'
|
|
283
|
+
local record = cjson.decode(failed_data)
|
|
284
|
+
finished_at = record.finishedAt
|
|
285
|
+
error_msg = record.error
|
|
286
|
+
end
|
|
287
|
+
end
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
if not status then
|
|
291
|
+
return nil
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
return cjson.encode({
|
|
295
|
+
status = status,
|
|
296
|
+
data = cjson.decode(job_data),
|
|
297
|
+
finishedAt = finished_at,
|
|
298
|
+
error = error_msg
|
|
299
|
+
})
|
|
300
|
+
`;
|
|
156
301
|
var CLAIM_SCHEDULE_SCRIPT = `
|
|
157
302
|
local schedule_key = KEYS[1]
|
|
158
303
|
local now = tonumber(ARGV[1])
|
|
@@ -246,6 +391,18 @@ var RedisAdapter = class {
|
|
|
246
391
|
this.#connection = connection;
|
|
247
392
|
this.#ownsConnection = ownsConnection;
|
|
248
393
|
}
|
|
394
|
+
#getKeys(queue) {
|
|
395
|
+
return {
|
|
396
|
+
data: `${redisKey}::${queue}::data`,
|
|
397
|
+
pending: `${redisKey}::${queue}::pending`,
|
|
398
|
+
delayed: `${redisKey}::${queue}::delayed`,
|
|
399
|
+
active: `${redisKey}::${queue}::active`,
|
|
400
|
+
completed: `${redisKey}::${queue}::completed`,
|
|
401
|
+
completedIndex: `${redisKey}::${queue}::completed::index`,
|
|
402
|
+
failed: `${redisKey}::${queue}::failed`,
|
|
403
|
+
failedIndex: `${redisKey}::${queue}::failed::index`
|
|
404
|
+
};
|
|
405
|
+
}
|
|
249
406
|
setWorkerId(workerId) {
|
|
250
407
|
this.#workerId = workerId;
|
|
251
408
|
}
|
|
@@ -258,16 +415,15 @@ var RedisAdapter = class {
|
|
|
258
415
|
return this.popFrom("default");
|
|
259
416
|
}
|
|
260
417
|
async popFrom(queue) {
|
|
418
|
+
const keys = this.#getKeys(queue);
|
|
261
419
|
const now = Date.now();
|
|
262
|
-
const pendingKey = `${redisKey}::${queue}`;
|
|
263
|
-
const activeKey = `${redisKey}::${queue}::active`;
|
|
264
|
-
const delayedKey = `${redisKey}::delayed::${queue}`;
|
|
265
420
|
const result = await this.#connection.eval(
|
|
266
421
|
ACQUIRE_JOB_SCRIPT,
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
422
|
+
4,
|
|
423
|
+
keys.data,
|
|
424
|
+
keys.pending,
|
|
425
|
+
keys.active,
|
|
426
|
+
keys.delayed,
|
|
271
427
|
this.#workerId,
|
|
272
428
|
now.toString()
|
|
273
429
|
);
|
|
@@ -276,30 +432,81 @@ var RedisAdapter = class {
|
|
|
276
432
|
}
|
|
277
433
|
return JSON.parse(result);
|
|
278
434
|
}
|
|
279
|
-
async completeJob(jobId, queue) {
|
|
280
|
-
const
|
|
281
|
-
|
|
435
|
+
async completeJob(jobId, queue, removeOnComplete) {
|
|
436
|
+
const keys = this.#getKeys(queue);
|
|
437
|
+
const { keep, maxAge, maxCount } = resolveRetention(removeOnComplete);
|
|
438
|
+
if (!keep) {
|
|
439
|
+
await this.#connection.eval(REMOVE_JOB_SCRIPT, 2, keys.data, keys.active, jobId);
|
|
440
|
+
return;
|
|
441
|
+
}
|
|
442
|
+
await this.#connection.eval(
|
|
443
|
+
FINALIZE_JOB_SCRIPT,
|
|
444
|
+
4,
|
|
445
|
+
keys.data,
|
|
446
|
+
keys.active,
|
|
447
|
+
keys.completed,
|
|
448
|
+
keys.completedIndex,
|
|
449
|
+
jobId,
|
|
450
|
+
Date.now().toString(),
|
|
451
|
+
maxAge.toString(),
|
|
452
|
+
maxCount.toString(),
|
|
453
|
+
""
|
|
454
|
+
);
|
|
282
455
|
}
|
|
283
|
-
async failJob(jobId, queue,
|
|
284
|
-
const
|
|
285
|
-
|
|
456
|
+
async failJob(jobId, queue, error, removeOnFail) {
|
|
457
|
+
const keys = this.#getKeys(queue);
|
|
458
|
+
const { keep, maxAge, maxCount } = resolveRetention(removeOnFail);
|
|
459
|
+
if (!keep) {
|
|
460
|
+
await this.#connection.eval(REMOVE_JOB_SCRIPT, 2, keys.data, keys.active, jobId);
|
|
461
|
+
return;
|
|
462
|
+
}
|
|
463
|
+
await this.#connection.eval(
|
|
464
|
+
FINALIZE_JOB_SCRIPT,
|
|
465
|
+
4,
|
|
466
|
+
keys.data,
|
|
467
|
+
keys.active,
|
|
468
|
+
keys.failed,
|
|
469
|
+
keys.failedIndex,
|
|
470
|
+
jobId,
|
|
471
|
+
Date.now().toString(),
|
|
472
|
+
maxAge.toString(),
|
|
473
|
+
maxCount.toString(),
|
|
474
|
+
error?.message || ""
|
|
475
|
+
);
|
|
286
476
|
}
|
|
287
477
|
async retryJob(jobId, queue, retryAt) {
|
|
478
|
+
const keys = this.#getKeys(queue);
|
|
288
479
|
const now = Date.now();
|
|
289
|
-
const activeKey = `${redisKey}::${queue}::active`;
|
|
290
|
-
const pendingKey = `${redisKey}::${queue}`;
|
|
291
|
-
const delayedKey = `${redisKey}::delayed::${queue}`;
|
|
292
480
|
await this.#connection.eval(
|
|
293
481
|
RETRY_JOB_SCRIPT,
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
482
|
+
4,
|
|
483
|
+
keys.data,
|
|
484
|
+
keys.active,
|
|
485
|
+
keys.pending,
|
|
486
|
+
keys.delayed,
|
|
298
487
|
jobId,
|
|
299
488
|
retryAt ? retryAt.getTime().toString() : "0",
|
|
300
489
|
now.toString()
|
|
301
490
|
);
|
|
302
491
|
}
|
|
492
|
+
async getJob(jobId, queue) {
|
|
493
|
+
const keys = this.#getKeys(queue);
|
|
494
|
+
const result = await this.#connection.eval(
|
|
495
|
+
GET_JOB_SCRIPT,
|
|
496
|
+
6,
|
|
497
|
+
keys.data,
|
|
498
|
+
keys.pending,
|
|
499
|
+
keys.delayed,
|
|
500
|
+
keys.active,
|
|
501
|
+
keys.completed,
|
|
502
|
+
keys.failed,
|
|
503
|
+
jobId
|
|
504
|
+
);
|
|
505
|
+
if (!result) {
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
return JSON.parse(result);
|
|
509
|
+
}
|
|
303
510
|
push(jobData) {
|
|
304
511
|
return this.pushOn("default", jobData);
|
|
305
512
|
}
|
|
@@ -307,31 +514,65 @@ var RedisAdapter = class {
|
|
|
307
514
|
return this.pushLaterOn("default", jobData, delay);
|
|
308
515
|
}
|
|
309
516
|
async pushLaterOn(queue, jobData, delay) {
|
|
517
|
+
const keys = this.#getKeys(queue);
|
|
310
518
|
const executeAt = Date.now() + delay;
|
|
311
|
-
|
|
312
|
-
|
|
519
|
+
await this.#connection.eval(
|
|
520
|
+
PUSH_DELAYED_JOB_SCRIPT,
|
|
521
|
+
2,
|
|
522
|
+
keys.data,
|
|
523
|
+
keys.delayed,
|
|
524
|
+
jobData.id,
|
|
525
|
+
JSON.stringify(jobData),
|
|
526
|
+
executeAt.toString()
|
|
527
|
+
);
|
|
313
528
|
}
|
|
314
529
|
async pushOn(queue, jobData) {
|
|
530
|
+
const keys = this.#getKeys(queue);
|
|
315
531
|
const priority = jobData.priority ?? DEFAULT_PRIORITY;
|
|
316
532
|
const timestamp = Date.now();
|
|
317
533
|
const score = calculateScore(priority, timestamp);
|
|
318
|
-
await this.#connection.
|
|
534
|
+
await this.#connection.eval(
|
|
535
|
+
PUSH_JOB_SCRIPT,
|
|
536
|
+
2,
|
|
537
|
+
keys.data,
|
|
538
|
+
keys.pending,
|
|
539
|
+
jobData.id,
|
|
540
|
+
JSON.stringify(jobData),
|
|
541
|
+
score.toString()
|
|
542
|
+
);
|
|
543
|
+
}
|
|
544
|
+
pushMany(jobs) {
|
|
545
|
+
return this.pushManyOn("default", jobs);
|
|
546
|
+
}
|
|
547
|
+
async pushManyOn(queue, jobs) {
|
|
548
|
+
if (jobs.length === 0) return;
|
|
549
|
+
const keys = this.#getKeys(queue);
|
|
550
|
+
const now = Date.now();
|
|
551
|
+
const multi = this.#connection.multi();
|
|
552
|
+
for (const job of jobs) {
|
|
553
|
+
const priority = job.priority ?? DEFAULT_PRIORITY;
|
|
554
|
+
const score = calculateScore(priority, now);
|
|
555
|
+
multi.hset(keys.data, job.id, JSON.stringify(job));
|
|
556
|
+
multi.zadd(keys.pending, score, job.id);
|
|
557
|
+
}
|
|
558
|
+
await multi.exec();
|
|
319
559
|
}
|
|
320
560
|
size() {
|
|
321
561
|
return this.sizeOf("default");
|
|
322
562
|
}
|
|
323
563
|
sizeOf(queue) {
|
|
324
|
-
|
|
564
|
+
const keys = this.#getKeys(queue);
|
|
565
|
+
return this.#connection.zcard(keys.pending);
|
|
325
566
|
}
|
|
326
567
|
async recoverStalledJobs(queue, stalledThreshold, maxStalledCount) {
|
|
568
|
+
const keys = this.#getKeys(queue);
|
|
327
569
|
const now = Date.now();
|
|
328
|
-
const activeKey = `${redisKey}::${queue}::active`;
|
|
329
|
-
const pendingKey = `${redisKey}::${queue}`;
|
|
330
570
|
const recovered = await this.#connection.eval(
|
|
331
571
|
RECOVER_STALLED_JOBS_SCRIPT,
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
572
|
+
3,
|
|
573
|
+
keys.data,
|
|
574
|
+
keys.active,
|
|
575
|
+
keys.pending,
|
|
335
576
|
now.toString(),
|
|
336
577
|
stalledThreshold.toString(),
|
|
337
578
|
maxStalledCount.toString()
|
|
@@ -343,7 +584,7 @@ var RedisAdapter = class {
|
|
|
343
584
|
const now = Date.now();
|
|
344
585
|
const scheduleData = {
|
|
345
586
|
id,
|
|
346
|
-
|
|
587
|
+
name: config.name,
|
|
347
588
|
payload: JSON.stringify(config.payload),
|
|
348
589
|
timezone: config.timezone,
|
|
349
590
|
status: "active",
|
|
@@ -442,7 +683,7 @@ var RedisAdapter = class {
|
|
|
442
683
|
#hashToScheduleData(data) {
|
|
443
684
|
return {
|
|
444
685
|
id: data.id,
|
|
445
|
-
|
|
686
|
+
name: data.name,
|
|
446
687
|
payload: JSON.parse(data.payload || "{}"),
|
|
447
688
|
cronExpression: data.cron_expression || null,
|
|
448
689
|
everyMs: data.every_ms ? Number.parseInt(data.every_ms, 10) : null,
|