@boringnode/queue 0.5.2 → 0.6.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 +94 -14
- package/build/chunk-6IO4P6RB.js +145 -0
- package/build/chunk-6IO4P6RB.js.map +1 -0
- package/build/{chunk-VRXHCWNK.js → chunk-AHUVTAI7.js} +220 -15
- package/build/chunk-AHUVTAI7.js.map +1 -0
- package/build/chunk-S37X3CBO.js +500 -0
- package/build/chunk-S37X3CBO.js.map +1 -0
- package/build/index.d.ts +10 -2
- package/build/index.js +187 -31
- package/build/index.js.map +1 -1
- package/build/{job-Z5fBSzRX.d.ts → job-C4oyCVxR.d.ts} +131 -10
- package/build/src/contracts/adapter.d.ts +1 -1
- package/build/src/drivers/fake_adapter.d.ts +7 -6
- package/build/src/drivers/fake_adapter.js +1 -1
- package/build/src/drivers/knex_adapter.d.ts +6 -5
- package/build/src/drivers/knex_adapter.js +112 -0
- package/build/src/drivers/knex_adapter.js.map +1 -1
- package/build/src/drivers/redis_adapter.d.ts +6 -5
- package/build/src/drivers/redis_adapter.js +134 -368
- package/build/src/drivers/redis_adapter.js.map +1 -1
- package/build/src/drivers/redis_job_storage.d.ts +17 -0
- package/build/src/drivers/redis_job_storage.js +14 -0
- package/build/src/drivers/redis_job_storage.js.map +1 -0
- package/build/src/drivers/redis_scripts.d.ts +87 -0
- package/build/src/drivers/redis_scripts.js +29 -0
- package/build/src/drivers/redis_scripts.js.map +1 -0
- package/build/src/drivers/sync_adapter.d.ts +2 -1
- package/build/src/drivers/sync_adapter.js +7 -1
- package/build/src/drivers/sync_adapter.js.map +1 -1
- package/build/src/otel.d.ts +2 -2
- package/build/src/types/index.d.ts +1 -1
- package/build/src/types/main.d.ts +1 -1
- package/build/src/types/tracing_channels.d.ts +7 -1
- package/package.json +17 -17
- package/build/chunk-VRXHCWNK.js.map +0 -1
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
import {
|
|
2
|
+
REDIS_DEDUP_LUA,
|
|
3
|
+
REDIS_JOB_STORAGE_LUA
|
|
4
|
+
} from "./chunk-6IO4P6RB.js";
|
|
5
|
+
|
|
6
|
+
// src/drivers/redis_scripts.ts
|
|
7
|
+
var PUSH_JOB_SCRIPT = `
|
|
8
|
+
local data_key = KEYS[1]
|
|
9
|
+
local pending_key = KEYS[2]
|
|
10
|
+
local overlay_key = KEYS[3]
|
|
11
|
+
local job_id = ARGV[1]
|
|
12
|
+
local job_data = ARGV[2]
|
|
13
|
+
local score = tonumber(ARGV[3])
|
|
14
|
+
|
|
15
|
+
${REDIS_JOB_STORAGE_LUA}
|
|
16
|
+
|
|
17
|
+
store_job_data(data_key, overlay_key, job_id, job_data)
|
|
18
|
+
redis.call('ZADD', pending_key, score, job_id)
|
|
19
|
+
|
|
20
|
+
return 1
|
|
21
|
+
`;
|
|
22
|
+
var PUSH_DEDUP_JOB_SCRIPT = `
|
|
23
|
+
local data_key = KEYS[1]
|
|
24
|
+
local target_key = KEYS[2]
|
|
25
|
+
local dedup_key = KEYS[3]
|
|
26
|
+
local other_state_key = KEYS[4]
|
|
27
|
+
local overlay_key = KEYS[5]
|
|
28
|
+
local job_id = ARGV[1]
|
|
29
|
+
local job_data = ARGV[2]
|
|
30
|
+
local score = tonumber(ARGV[3])
|
|
31
|
+
local ttl = tonumber(ARGV[4])
|
|
32
|
+
local extend = tonumber(ARGV[5])
|
|
33
|
+
local replace = tonumber(ARGV[6])
|
|
34
|
+
local payload_data = ARGV[7]
|
|
35
|
+
local payload_is_undefined = tonumber(ARGV[8])
|
|
36
|
+
|
|
37
|
+
${REDIS_JOB_STORAGE_LUA}
|
|
38
|
+
${REDIS_DEDUP_LUA}
|
|
39
|
+
|
|
40
|
+
local existing_result = resolve_dedup_existing_job(
|
|
41
|
+
data_key,
|
|
42
|
+
target_key,
|
|
43
|
+
other_state_key,
|
|
44
|
+
overlay_key,
|
|
45
|
+
dedup_key,
|
|
46
|
+
extend,
|
|
47
|
+
replace,
|
|
48
|
+
payload_data,
|
|
49
|
+
payload_is_undefined
|
|
50
|
+
)
|
|
51
|
+
if existing_result then
|
|
52
|
+
return existing_result
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
store_job_data(data_key, overlay_key, job_id, job_data)
|
|
56
|
+
redis.call('ZADD', target_key, score, job_id)
|
|
57
|
+
redis.call('SET', dedup_key, job_id)
|
|
58
|
+
if ttl > 0 then
|
|
59
|
+
redis.call('PEXPIRE', dedup_key, ttl)
|
|
60
|
+
end
|
|
61
|
+
return {'added', job_id}
|
|
62
|
+
`;
|
|
63
|
+
var PUSH_DELAYED_JOB_SCRIPT = `
|
|
64
|
+
local data_key = KEYS[1]
|
|
65
|
+
local delayed_key = KEYS[2]
|
|
66
|
+
local overlay_key = KEYS[3]
|
|
67
|
+
local job_id = ARGV[1]
|
|
68
|
+
local job_data = ARGV[2]
|
|
69
|
+
local execute_at = tonumber(ARGV[3])
|
|
70
|
+
|
|
71
|
+
${REDIS_JOB_STORAGE_LUA}
|
|
72
|
+
|
|
73
|
+
store_job_data(data_key, overlay_key, job_id, job_data)
|
|
74
|
+
redis.call('ZADD', delayed_key, execute_at, job_id)
|
|
75
|
+
|
|
76
|
+
return 1
|
|
77
|
+
`;
|
|
78
|
+
var ACQUIRE_JOB_SCRIPT = `
|
|
79
|
+
local data_key = KEYS[1]
|
|
80
|
+
local pending_key = KEYS[2]
|
|
81
|
+
local active_key = KEYS[3]
|
|
82
|
+
local delayed_key = KEYS[4]
|
|
83
|
+
local overlay_key = KEYS[5]
|
|
84
|
+
local worker_id = ARGV[1]
|
|
85
|
+
local now = tonumber(ARGV[2])
|
|
86
|
+
|
|
87
|
+
${REDIS_JOB_STORAGE_LUA}
|
|
88
|
+
|
|
89
|
+
-- Process delayed jobs: move ready jobs to pending
|
|
90
|
+
local ready_job_ids = redis.call('ZRANGEBYSCORE', delayed_key, 0, now)
|
|
91
|
+
if #ready_job_ids > 0 then
|
|
92
|
+
for i = 1, #ready_job_ids do
|
|
93
|
+
local job_id = ready_job_ids[i]
|
|
94
|
+
local job_data = redis.call('HGET', data_key, job_id)
|
|
95
|
+
if job_data then
|
|
96
|
+
local job = cjson.decode(job_data)
|
|
97
|
+
local priority = job.priority or 5
|
|
98
|
+
local score = priority * 10000000000000 + now
|
|
99
|
+
redis.call('ZADD', pending_key, score, job_id)
|
|
100
|
+
redis.call('ZREM', delayed_key, job_id)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
-- Pop highest priority job (lowest score)
|
|
106
|
+
local result = redis.call('ZPOPMIN', pending_key)
|
|
107
|
+
if not result or #result == 0 then
|
|
108
|
+
return nil
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
local job_id = result[1]
|
|
112
|
+
local job_data = redis.call('HGET', data_key, job_id)
|
|
113
|
+
if not job_data then
|
|
114
|
+
return nil
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
-- Store in active hash (without data, it's in data_key)
|
|
118
|
+
local active_data = cjson.encode({
|
|
119
|
+
workerId = worker_id,
|
|
120
|
+
acquiredAt = now
|
|
121
|
+
})
|
|
122
|
+
redis.call('HSET', active_key, job_id, active_data)
|
|
123
|
+
|
|
124
|
+
return encode_job_result(job_data, overlay_key, job_id, {
|
|
125
|
+
acquiredAt = now
|
|
126
|
+
})
|
|
127
|
+
`;
|
|
128
|
+
var REMOVE_JOB_SCRIPT = `
|
|
129
|
+
local data_key = KEYS[1]
|
|
130
|
+
local active_key = KEYS[2]
|
|
131
|
+
local overlay_key = KEYS[3]
|
|
132
|
+
local job_id = ARGV[1]
|
|
133
|
+
local dedup_prefix = ARGV[2]
|
|
134
|
+
|
|
135
|
+
${REDIS_JOB_STORAGE_LUA}
|
|
136
|
+
|
|
137
|
+
if redis.call('HEXISTS', active_key, job_id) == 0 then
|
|
138
|
+
return 0
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
-- Read job data to extract dedup.id before deleting
|
|
142
|
+
local job_data = redis.call('HGET', data_key, job_id)
|
|
143
|
+
if job_data then
|
|
144
|
+
local ok, job = pcall(cjson.decode, job_data)
|
|
145
|
+
if ok and job and job.dedup and job.dedup.id then
|
|
146
|
+
local dkey = dedup_prefix .. job.dedup.id
|
|
147
|
+
if redis.call('GET', dkey) == job_id then
|
|
148
|
+
redis.call('DEL', dkey)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
redis.call('HDEL', active_key, job_id)
|
|
154
|
+
delete_job_data(data_key, overlay_key, job_id)
|
|
155
|
+
|
|
156
|
+
return 1
|
|
157
|
+
`;
|
|
158
|
+
var FINALIZE_JOB_SCRIPT = `
|
|
159
|
+
local data_key = KEYS[1]
|
|
160
|
+
local active_key = KEYS[2]
|
|
161
|
+
local history_key = KEYS[3]
|
|
162
|
+
local index_key = KEYS[4]
|
|
163
|
+
local overlay_key = KEYS[5]
|
|
164
|
+
local job_id = ARGV[1]
|
|
165
|
+
local now = tonumber(ARGV[2])
|
|
166
|
+
local max_age = tonumber(ARGV[3])
|
|
167
|
+
local max_count = tonumber(ARGV[4])
|
|
168
|
+
local error_message = ARGV[5]
|
|
169
|
+
local dedup_prefix = ARGV[6]
|
|
170
|
+
|
|
171
|
+
${REDIS_JOB_STORAGE_LUA}
|
|
172
|
+
|
|
173
|
+
-- Verify job is active
|
|
174
|
+
if redis.call('HEXISTS', active_key, job_id) == 0 then
|
|
175
|
+
return 0
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
-- Remove from active
|
|
179
|
+
redis.call('HDEL', active_key, job_id)
|
|
180
|
+
|
|
181
|
+
-- Store finalization info (data stays in data_key)
|
|
182
|
+
local record = {
|
|
183
|
+
finishedAt = now
|
|
184
|
+
}
|
|
185
|
+
if error_message and error_message ~= '' then
|
|
186
|
+
record.error = error_message
|
|
187
|
+
end
|
|
188
|
+
redis.call('HSET', history_key, job_id, cjson.encode(record))
|
|
189
|
+
redis.call('ZADD', index_key, now, job_id)
|
|
190
|
+
|
|
191
|
+
local function delete_dedup_for(ids)
|
|
192
|
+
for i = 1, #ids do
|
|
193
|
+
local id = ids[i]
|
|
194
|
+
local d = redis.call('HGET', data_key, id)
|
|
195
|
+
if d then
|
|
196
|
+
local ok, job = pcall(cjson.decode, d)
|
|
197
|
+
if ok and job and job.dedup and job.dedup.id then
|
|
198
|
+
local dkey = dedup_prefix .. job.dedup.id
|
|
199
|
+
if redis.call('GET', dkey) == id then
|
|
200
|
+
redis.call('DEL', dkey)
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
-- Prune by age
|
|
208
|
+
if max_age and max_age > 0 then
|
|
209
|
+
local cutoff = now - max_age
|
|
210
|
+
local expired = redis.call('ZRANGEBYSCORE', index_key, 0, cutoff)
|
|
211
|
+
if #expired > 0 then
|
|
212
|
+
delete_dedup_for(expired)
|
|
213
|
+
redis.call('ZREM', index_key, unpack(expired))
|
|
214
|
+
redis.call('HDEL', history_key, unpack(expired))
|
|
215
|
+
delete_jobs_data(data_key, overlay_key, expired)
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
-- Prune by count
|
|
220
|
+
if max_count and max_count > 0 then
|
|
221
|
+
local size = tonumber(redis.call('ZCARD', index_key))
|
|
222
|
+
if size > max_count then
|
|
223
|
+
local excess = size - max_count
|
|
224
|
+
local stale = redis.call('ZRANGE', index_key, 0, excess - 1)
|
|
225
|
+
if #stale > 0 then
|
|
226
|
+
delete_dedup_for(stale)
|
|
227
|
+
redis.call('ZREM', index_key, unpack(stale))
|
|
228
|
+
redis.call('HDEL', history_key, unpack(stale))
|
|
229
|
+
delete_jobs_data(data_key, overlay_key, stale)
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
return 1
|
|
235
|
+
`;
|
|
236
|
+
var RETRY_JOB_SCRIPT = `
|
|
237
|
+
local data_key = KEYS[1]
|
|
238
|
+
local active_key = KEYS[2]
|
|
239
|
+
local pending_key = KEYS[3]
|
|
240
|
+
local delayed_key = KEYS[4]
|
|
241
|
+
local overlay_key = KEYS[5]
|
|
242
|
+
local job_id = ARGV[1]
|
|
243
|
+
local retry_at = tonumber(ARGV[2])
|
|
244
|
+
local now = tonumber(ARGV[3])
|
|
245
|
+
|
|
246
|
+
${REDIS_JOB_STORAGE_LUA}
|
|
247
|
+
|
|
248
|
+
-- Verify job is active
|
|
249
|
+
if redis.call('HEXISTS', active_key, job_id) == 0 then
|
|
250
|
+
return 0
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
-- Get job data
|
|
254
|
+
local job_data = redis.call('HGET', data_key, job_id)
|
|
255
|
+
if not job_data then
|
|
256
|
+
return 0
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
-- Remove from active
|
|
260
|
+
redis.call('HDEL', active_key, job_id)
|
|
261
|
+
|
|
262
|
+
-- Increment attempts without rewriting opaque job JSON.
|
|
263
|
+
local job = cjson.decode(job_data)
|
|
264
|
+
local overlay = read_job_overlay(overlay_key, job_id)
|
|
265
|
+
overlay.attempts = (overlay.attempts or job.attempts or 0) + 1
|
|
266
|
+
write_job_overlay(overlay_key, job_id, overlay)
|
|
267
|
+
|
|
268
|
+
-- Add back to pending or delayed
|
|
269
|
+
if retry_at and retry_at > now then
|
|
270
|
+
redis.call('ZADD', delayed_key, retry_at, job_id)
|
|
271
|
+
else
|
|
272
|
+
-- Score = priority * 1e13 + timestamp
|
|
273
|
+
-- Lower score = higher priority, FIFO within same priority
|
|
274
|
+
local priority = job.priority or 5
|
|
275
|
+
local score = priority * 10000000000000 + now
|
|
276
|
+
redis.call('ZADD', pending_key, score, job_id)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
return 1
|
|
280
|
+
`;
|
|
281
|
+
var RECOVER_STALLED_JOBS_SCRIPT = `
|
|
282
|
+
local data_key = KEYS[1]
|
|
283
|
+
local active_key = KEYS[2]
|
|
284
|
+
local pending_key = KEYS[3]
|
|
285
|
+
local overlay_key = KEYS[4]
|
|
286
|
+
local now = tonumber(ARGV[1])
|
|
287
|
+
local stalled_threshold = tonumber(ARGV[2])
|
|
288
|
+
local max_stalled_count = tonumber(ARGV[3])
|
|
289
|
+
local dedup_prefix = ARGV[4]
|
|
290
|
+
|
|
291
|
+
${REDIS_JOB_STORAGE_LUA}
|
|
292
|
+
|
|
293
|
+
local recovered = 0
|
|
294
|
+
local stalled_cutoff = now - stalled_threshold
|
|
295
|
+
|
|
296
|
+
-- Get all active jobs
|
|
297
|
+
local active_jobs = redis.call('HGETALL', active_key)
|
|
298
|
+
|
|
299
|
+
-- HGETALL returns [field1, value1, field2, value2, ...]
|
|
300
|
+
for i = 1, #active_jobs, 2 do
|
|
301
|
+
local job_id = active_jobs[i]
|
|
302
|
+
local active_data = active_jobs[i + 1]
|
|
303
|
+
local active = cjson.decode(active_data)
|
|
304
|
+
|
|
305
|
+
-- Check if job is stalled
|
|
306
|
+
if active.acquiredAt < stalled_cutoff then
|
|
307
|
+
local job_data = redis.call('HGET', data_key, job_id)
|
|
308
|
+
if job_data then
|
|
309
|
+
local job = cjson.decode(job_data)
|
|
310
|
+
local overlay = read_job_overlay(overlay_key, job_id)
|
|
311
|
+
local current_stalled_count = overlay.stalledCount or job.stalledCount or 0
|
|
312
|
+
|
|
313
|
+
-- Remove from active hash
|
|
314
|
+
redis.call('HDEL', active_key, job_id)
|
|
315
|
+
|
|
316
|
+
-- Check if job has exceeded max stalled count
|
|
317
|
+
if current_stalled_count >= max_stalled_count then
|
|
318
|
+
-- Job failed permanently, remove data + dedup key (only if pointer still ours)
|
|
319
|
+
if job.dedup and job.dedup.id then
|
|
320
|
+
local dkey = dedup_prefix .. job.dedup.id
|
|
321
|
+
if redis.call('GET', dkey) == job_id then
|
|
322
|
+
redis.call('DEL', dkey)
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
delete_job_data(data_key, overlay_key, job_id)
|
|
326
|
+
else
|
|
327
|
+
-- Recover: increment stalledCount without rewriting opaque job JSON.
|
|
328
|
+
overlay.stalledCount = current_stalled_count + 1
|
|
329
|
+
write_job_overlay(overlay_key, job_id, overlay)
|
|
330
|
+
-- Score = priority * 1e13 + timestamp
|
|
331
|
+
local priority = job.priority or 5
|
|
332
|
+
local score = priority * 10000000000000 + now
|
|
333
|
+
redis.call('ZADD', pending_key, score, job_id)
|
|
334
|
+
recovered = recovered + 1
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
end
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
return recovered
|
|
341
|
+
`;
|
|
342
|
+
var RENEW_JOBS_SCRIPT = `
|
|
343
|
+
local active_key = KEYS[1]
|
|
344
|
+
local now = tonumber(ARGV[1])
|
|
345
|
+
local worker_id = ARGV[2]
|
|
346
|
+
|
|
347
|
+
local renewed = 0
|
|
348
|
+
for i = 3, #ARGV do
|
|
349
|
+
local job_id = ARGV[i]
|
|
350
|
+
local active_data = redis.call('HGET', active_key, job_id)
|
|
351
|
+
if active_data then
|
|
352
|
+
local active = cjson.decode(active_data)
|
|
353
|
+
-- Only the worker that currently owns the lease may renew it.
|
|
354
|
+
if active.workerId == worker_id then
|
|
355
|
+
active.acquiredAt = now
|
|
356
|
+
redis.call('HSET', active_key, job_id, cjson.encode(active))
|
|
357
|
+
renewed = renewed + 1
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
end
|
|
361
|
+
|
|
362
|
+
return renewed
|
|
363
|
+
`;
|
|
364
|
+
var GET_JOB_SCRIPT = `
|
|
365
|
+
local data_key = KEYS[1]
|
|
366
|
+
local pending_key = KEYS[2]
|
|
367
|
+
local delayed_key = KEYS[3]
|
|
368
|
+
local active_key = KEYS[4]
|
|
369
|
+
local completed_key = KEYS[5]
|
|
370
|
+
local failed_key = KEYS[6]
|
|
371
|
+
local overlay_key = KEYS[7]
|
|
372
|
+
local job_id = ARGV[1]
|
|
373
|
+
|
|
374
|
+
${REDIS_JOB_STORAGE_LUA}
|
|
375
|
+
|
|
376
|
+
local job_data = redis.call('HGET', data_key, job_id)
|
|
377
|
+
if not job_data then
|
|
378
|
+
return nil
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
local status = nil
|
|
382
|
+
local finished_at = nil
|
|
383
|
+
local error_msg = nil
|
|
384
|
+
|
|
385
|
+
-- Check status in order
|
|
386
|
+
if redis.call('HEXISTS', active_key, job_id) == 1 then
|
|
387
|
+
status = 'active'
|
|
388
|
+
elseif redis.call('ZSCORE', pending_key, job_id) then
|
|
389
|
+
status = 'pending'
|
|
390
|
+
elseif redis.call('ZSCORE', delayed_key, job_id) then
|
|
391
|
+
status = 'delayed'
|
|
392
|
+
else
|
|
393
|
+
local completed_data = redis.call('HGET', completed_key, job_id)
|
|
394
|
+
if completed_data then
|
|
395
|
+
status = 'completed'
|
|
396
|
+
local record = cjson.decode(completed_data)
|
|
397
|
+
finished_at = record.finishedAt
|
|
398
|
+
else
|
|
399
|
+
local failed_data = redis.call('HGET', failed_key, job_id)
|
|
400
|
+
if failed_data then
|
|
401
|
+
status = 'failed'
|
|
402
|
+
local record = cjson.decode(failed_data)
|
|
403
|
+
finished_at = record.finishedAt
|
|
404
|
+
error_msg = record.error
|
|
405
|
+
end
|
|
406
|
+
end
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
if not status then
|
|
410
|
+
return nil
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
return encode_job_result(job_data, overlay_key, job_id, {
|
|
414
|
+
status = status,
|
|
415
|
+
finishedAt = finished_at,
|
|
416
|
+
error = error_msg
|
|
417
|
+
})
|
|
418
|
+
`;
|
|
419
|
+
var CLAIM_SCHEDULE_SCRIPT = `
|
|
420
|
+
local schedules_index_key = KEYS[1]
|
|
421
|
+
local schedule_key_prefix = KEYS[2]
|
|
422
|
+
local now = tonumber(ARGV[1])
|
|
423
|
+
|
|
424
|
+
local ids = redis.call('SMEMBERS', schedules_index_key)
|
|
425
|
+
|
|
426
|
+
for i = 1, #ids do
|
|
427
|
+
local schedule_key = schedule_key_prefix .. ids[i]
|
|
428
|
+
|
|
429
|
+
-- Get schedule data
|
|
430
|
+
local data = redis.call('HGETALL', schedule_key)
|
|
431
|
+
if #data > 0 then
|
|
432
|
+
-- Convert HGETALL result to table
|
|
433
|
+
local schedule = {}
|
|
434
|
+
for j = 1, #data, 2 do
|
|
435
|
+
schedule[data[j]] = data[j + 1]
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
-- Check if schedule is due
|
|
439
|
+
if schedule.status == 'active' then
|
|
440
|
+
local next_run_at = tonumber(schedule.next_run_at)
|
|
441
|
+
|
|
442
|
+
if next_run_at and next_run_at <= now then
|
|
443
|
+
local run_count = tonumber(schedule.run_count or '0')
|
|
444
|
+
local run_limit = schedule.run_limit and tonumber(schedule.run_limit) or nil
|
|
445
|
+
local to_date = schedule.to_date and tonumber(schedule.to_date) or nil
|
|
446
|
+
|
|
447
|
+
-- Check limits
|
|
448
|
+
if not (run_limit and run_count >= run_limit) and not (to_date and now > to_date) then
|
|
449
|
+
-- This schedule is claimable - atomically update it
|
|
450
|
+
local new_run_count = run_count + 1
|
|
451
|
+
|
|
452
|
+
-- Calculate new next_run_at (simple interval-based for now)
|
|
453
|
+
-- Complex cron calculation happens in the caller
|
|
454
|
+
local new_next_run_at = ''
|
|
455
|
+
local every_ms = schedule.every_ms and tonumber(schedule.every_ms) or nil
|
|
456
|
+
if every_ms then
|
|
457
|
+
new_next_run_at = tostring(now + every_ms)
|
|
458
|
+
end
|
|
459
|
+
|
|
460
|
+
-- Check if we've hit the limit after this run
|
|
461
|
+
if run_limit and new_run_count >= run_limit then
|
|
462
|
+
new_next_run_at = ''
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
-- Check if past end date
|
|
466
|
+
if to_date and new_next_run_at ~= '' and tonumber(new_next_run_at) > to_date then
|
|
467
|
+
new_next_run_at = ''
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
-- Update the schedule atomically
|
|
471
|
+
redis.call('HSET', schedule_key,
|
|
472
|
+
'next_run_at', new_next_run_at,
|
|
473
|
+
'last_run_at', tostring(now),
|
|
474
|
+
'run_count', tostring(new_run_count))
|
|
475
|
+
|
|
476
|
+
-- Return the schedule data (before update) as JSON
|
|
477
|
+
return cjson.encode(schedule)
|
|
478
|
+
end
|
|
479
|
+
end
|
|
480
|
+
end
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
return nil
|
|
485
|
+
`;
|
|
486
|
+
|
|
487
|
+
export {
|
|
488
|
+
PUSH_JOB_SCRIPT,
|
|
489
|
+
PUSH_DEDUP_JOB_SCRIPT,
|
|
490
|
+
PUSH_DELAYED_JOB_SCRIPT,
|
|
491
|
+
ACQUIRE_JOB_SCRIPT,
|
|
492
|
+
REMOVE_JOB_SCRIPT,
|
|
493
|
+
FINALIZE_JOB_SCRIPT,
|
|
494
|
+
RETRY_JOB_SCRIPT,
|
|
495
|
+
RECOVER_STALLED_JOBS_SCRIPT,
|
|
496
|
+
RENEW_JOBS_SCRIPT,
|
|
497
|
+
GET_JOB_SCRIPT,
|
|
498
|
+
CLAIM_SCHEDULE_SCRIPT
|
|
499
|
+
};
|
|
500
|
+
//# sourceMappingURL=chunk-S37X3CBO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/drivers/redis_scripts.ts"],"sourcesContent":["import { REDIS_DEDUP_LUA, REDIS_JOB_STORAGE_LUA } from './redis_job_storage.js'\n\n/**\n * Lua script for pushing a job to the queue.\n * Stores job data in the central hash and adds jobId to pending ZSET.\n */\nexport const PUSH_JOB_SCRIPT = `\n local data_key = KEYS[1]\n local pending_key = KEYS[2]\n local overlay_key = KEYS[3]\n local job_id = ARGV[1]\n local job_data = ARGV[2]\n local score = tonumber(ARGV[3])\n\n${REDIS_JOB_STORAGE_LUA}\n\n store_job_data(data_key, overlay_key, job_id, job_data)\n redis.call('ZADD', pending_key, score, job_id)\n\n return 1\n`\n\n/**\n * Lua script for pushing a dedup job.\n *\n * Behavior:\n * - If dedup key exists AND job still exists AND within TTL: apply replace/extend, skip insert.\n * - If dedup key exists but job data missing (orphan): proceed to insert new.\n * - If TTL expired or no prior entry: insert new job, record dedup key with TTL.\n *\n * Replace only applies to jobs in pending or delayed state. Active and\n * retained completed/failed jobs are left untouched (returns 'skipped').\n * Replace swaps the payload only — priority/queue/delay/groupId/dedup\n * options of the existing job are preserved.\n *\n * Extend uses the ORIGINAL ttl recorded on the existing job (stored in\n * its dedup field), not the ttl arg of the current dispatch. Matches\n * Knex/Fake behavior: extend resets the clock but never changes the\n * window length.\n *\n * Returns {outcome, job_id}: outcome ∈ 'added' | 'skipped' | 'replaced' | 'extended'.\n */\nexport const PUSH_DEDUP_JOB_SCRIPT = `\n local data_key = KEYS[1]\n local target_key = KEYS[2]\n local dedup_key = KEYS[3]\n local other_state_key = KEYS[4]\n local overlay_key = KEYS[5]\n local job_id = ARGV[1]\n local job_data = ARGV[2]\n local score = tonumber(ARGV[3])\n local ttl = tonumber(ARGV[4])\n local extend = tonumber(ARGV[5])\n local replace = tonumber(ARGV[6])\n local payload_data = ARGV[7]\n local payload_is_undefined = tonumber(ARGV[8])\n\n${REDIS_JOB_STORAGE_LUA}\n${REDIS_DEDUP_LUA}\n\n local existing_result = resolve_dedup_existing_job(\n data_key,\n target_key,\n other_state_key,\n overlay_key,\n dedup_key,\n extend,\n replace,\n payload_data,\n payload_is_undefined\n )\n if existing_result then\n return existing_result\n end\n\n store_job_data(data_key, overlay_key, job_id, job_data)\n redis.call('ZADD', target_key, score, job_id)\n redis.call('SET', dedup_key, job_id)\n if ttl > 0 then\n redis.call('PEXPIRE', dedup_key, ttl)\n end\n return {'added', job_id}\n`\n\n/**\n * Lua script for pushing a delayed job.\n * Stores job data in the central hash and adds jobId to delayed ZSET.\n */\nexport const PUSH_DELAYED_JOB_SCRIPT = `\n local data_key = KEYS[1]\n local delayed_key = KEYS[2]\n local overlay_key = KEYS[3]\n local job_id = ARGV[1]\n local job_data = ARGV[2]\n local execute_at = tonumber(ARGV[3])\n\n${REDIS_JOB_STORAGE_LUA}\n\n store_job_data(data_key, overlay_key, job_id, job_data)\n redis.call('ZADD', delayed_key, execute_at, job_id)\n\n return 1\n`\n\n/**\n * Lua script for atomic job acquisition.\n * 1. Check and process delayed jobs\n * 2. Pop from pending queue\n * 3. Add to active hash with worker info\n * 4. Return job data\n */\nexport const ACQUIRE_JOB_SCRIPT = `\n local data_key = KEYS[1]\n local pending_key = KEYS[2]\n local active_key = KEYS[3]\n local delayed_key = KEYS[4]\n local overlay_key = KEYS[5]\n local worker_id = ARGV[1]\n local now = tonumber(ARGV[2])\n\n${REDIS_JOB_STORAGE_LUA}\n\n -- Process delayed jobs: move ready jobs to pending\n local ready_job_ids = redis.call('ZRANGEBYSCORE', delayed_key, 0, now)\n if #ready_job_ids > 0 then\n for i = 1, #ready_job_ids do\n local job_id = ready_job_ids[i]\n local job_data = redis.call('HGET', data_key, job_id)\n if job_data then\n local job = cjson.decode(job_data)\n local priority = job.priority or 5\n local score = priority * 10000000000000 + now\n redis.call('ZADD', pending_key, score, job_id)\n redis.call('ZREM', delayed_key, job_id)\n end\n end\n end\n\n -- Pop highest priority job (lowest score)\n local result = redis.call('ZPOPMIN', pending_key)\n if not result or #result == 0 then\n return nil\n end\n\n local job_id = result[1]\n local job_data = redis.call('HGET', data_key, job_id)\n if not job_data then\n return nil\n end\n\n -- Store in active hash (without data, it's in data_key)\n local active_data = cjson.encode({\n workerId = worker_id,\n acquiredAt = now\n })\n redis.call('HSET', active_key, job_id, active_data)\n\n return encode_job_result(job_data, overlay_key, job_id, {\n acquiredAt = now\n })\n`\n\n/**\n * Lua script for removing a job completely (no history).\n * Also cleans up the dedup key if the job had dedup metadata.\n */\nexport const REMOVE_JOB_SCRIPT = `\n local data_key = KEYS[1]\n local active_key = KEYS[2]\n local overlay_key = KEYS[3]\n local job_id = ARGV[1]\n local dedup_prefix = ARGV[2]\n\n${REDIS_JOB_STORAGE_LUA}\n\n if redis.call('HEXISTS', active_key, job_id) == 0 then\n return 0\n end\n\n -- Read job data to extract dedup.id before deleting\n local job_data = redis.call('HGET', data_key, job_id)\n if job_data then\n local ok, job = pcall(cjson.decode, job_data)\n if ok and job and job.dedup and job.dedup.id then\n local dkey = dedup_prefix .. job.dedup.id\n if redis.call('GET', dkey) == job_id then\n redis.call('DEL', dkey)\n end\n end\n end\n\n redis.call('HDEL', active_key, job_id)\n delete_job_data(data_key, overlay_key, job_id)\n\n return 1\n`\n\n/**\n * Lua script for finalizing a job in history.\n * Removes from active, stores finalization info, and prunes old records.\n * When pruning removes job data, also deletes the associated dedup key.\n */\nexport const FINALIZE_JOB_SCRIPT = `\n local data_key = KEYS[1]\n local active_key = KEYS[2]\n local history_key = KEYS[3]\n local index_key = KEYS[4]\n local overlay_key = KEYS[5]\n local job_id = ARGV[1]\n local now = tonumber(ARGV[2])\n local max_age = tonumber(ARGV[3])\n local max_count = tonumber(ARGV[4])\n local error_message = ARGV[5]\n local dedup_prefix = ARGV[6]\n\n${REDIS_JOB_STORAGE_LUA}\n\n -- Verify job is active\n if redis.call('HEXISTS', active_key, job_id) == 0 then\n return 0\n end\n\n -- Remove from active\n redis.call('HDEL', active_key, job_id)\n\n -- Store finalization info (data stays in data_key)\n local record = {\n finishedAt = now\n }\n if error_message and error_message ~= '' then\n record.error = error_message\n end\n redis.call('HSET', history_key, job_id, cjson.encode(record))\n redis.call('ZADD', index_key, now, job_id)\n\n local function delete_dedup_for(ids)\n for i = 1, #ids do\n local id = ids[i]\n local d = redis.call('HGET', data_key, id)\n if d then\n local ok, job = pcall(cjson.decode, d)\n if ok and job and job.dedup and job.dedup.id then\n local dkey = dedup_prefix .. job.dedup.id\n if redis.call('GET', dkey) == id then\n redis.call('DEL', dkey)\n end\n end\n end\n end\n end\n\n -- Prune by age\n if max_age and max_age > 0 then\n local cutoff = now - max_age\n local expired = redis.call('ZRANGEBYSCORE', index_key, 0, cutoff)\n if #expired > 0 then\n delete_dedup_for(expired)\n redis.call('ZREM', index_key, unpack(expired))\n redis.call('HDEL', history_key, unpack(expired))\n delete_jobs_data(data_key, overlay_key, expired)\n end\n end\n\n -- Prune by count\n if max_count and max_count > 0 then\n local size = tonumber(redis.call('ZCARD', index_key))\n if size > max_count then\n local excess = size - max_count\n local stale = redis.call('ZRANGE', index_key, 0, excess - 1)\n if #stale > 0 then\n delete_dedup_for(stale)\n redis.call('ZREM', index_key, unpack(stale))\n redis.call('HDEL', history_key, unpack(stale))\n delete_jobs_data(data_key, overlay_key, stale)\n end\n end\n end\n\n return 1\n`\n\n/**\n * Lua script for retrying a job.\n * 1. Verify job is active\n * 2. Remove from active hash\n * 3. Increment attempts in data\n * 4. Add back to pending (or delayed if retryAt is set)\n */\nexport const RETRY_JOB_SCRIPT = `\n local data_key = KEYS[1]\n local active_key = KEYS[2]\n local pending_key = KEYS[3]\n local delayed_key = KEYS[4]\n local overlay_key = KEYS[5]\n local job_id = ARGV[1]\n local retry_at = tonumber(ARGV[2])\n local now = tonumber(ARGV[3])\n\n${REDIS_JOB_STORAGE_LUA}\n\n -- Verify job is active\n if redis.call('HEXISTS', active_key, job_id) == 0 then\n return 0\n end\n\n -- Get job data\n local job_data = redis.call('HGET', data_key, job_id)\n if not job_data then\n return 0\n end\n\n -- Remove from active\n redis.call('HDEL', active_key, job_id)\n\n -- Increment attempts without rewriting opaque job JSON.\n local job = cjson.decode(job_data)\n local overlay = read_job_overlay(overlay_key, job_id)\n overlay.attempts = (overlay.attempts or job.attempts or 0) + 1\n write_job_overlay(overlay_key, job_id, overlay)\n\n -- Add back to pending or delayed\n if retry_at and retry_at > now then\n redis.call('ZADD', delayed_key, retry_at, job_id)\n else\n -- Score = priority * 1e13 + timestamp\n -- Lower score = higher priority, FIFO within same priority\n local priority = job.priority or 5\n local score = priority * 10000000000000 + now\n redis.call('ZADD', pending_key, score, job_id)\n end\n\n return 1\n`\n\n/**\n * Lua script for recovering stalled jobs.\n * Scans the active hash for jobs that have been active too long.\n * - Jobs within maxStalledCount: move back to pending with incremented stalledCount\n * - Jobs exceeding maxStalledCount: remove permanently (fail)\n * Returns the number of recovered jobs (not including failed ones).\n */\nexport const RECOVER_STALLED_JOBS_SCRIPT = `\n local data_key = KEYS[1]\n local active_key = KEYS[2]\n local pending_key = KEYS[3]\n local overlay_key = KEYS[4]\n local now = tonumber(ARGV[1])\n local stalled_threshold = tonumber(ARGV[2])\n local max_stalled_count = tonumber(ARGV[3])\n local dedup_prefix = ARGV[4]\n\n${REDIS_JOB_STORAGE_LUA}\n\n local recovered = 0\n local stalled_cutoff = now - stalled_threshold\n\n -- Get all active jobs\n local active_jobs = redis.call('HGETALL', active_key)\n\n -- HGETALL returns [field1, value1, field2, value2, ...]\n for i = 1, #active_jobs, 2 do\n local job_id = active_jobs[i]\n local active_data = active_jobs[i + 1]\n local active = cjson.decode(active_data)\n\n -- Check if job is stalled\n if active.acquiredAt < stalled_cutoff then\n local job_data = redis.call('HGET', data_key, job_id)\n if job_data then\n local job = cjson.decode(job_data)\n local overlay = read_job_overlay(overlay_key, job_id)\n local current_stalled_count = overlay.stalledCount or job.stalledCount or 0\n\n -- Remove from active hash\n redis.call('HDEL', active_key, job_id)\n\n -- Check if job has exceeded max stalled count\n if current_stalled_count >= max_stalled_count then\n -- Job failed permanently, remove data + dedup key (only if pointer still ours)\n if job.dedup and job.dedup.id then\n local dkey = dedup_prefix .. job.dedup.id\n if redis.call('GET', dkey) == job_id then\n redis.call('DEL', dkey)\n end\n end\n delete_job_data(data_key, overlay_key, job_id)\n else\n -- Recover: increment stalledCount without rewriting opaque job JSON.\n overlay.stalledCount = current_stalled_count + 1\n write_job_overlay(overlay_key, job_id, overlay)\n -- Score = priority * 1e13 + timestamp\n local priority = job.priority or 5\n local score = priority * 10000000000000 + now\n redis.call('ZADD', pending_key, score, job_id)\n recovered = recovered + 1\n end\n end\n end\n end\n\n return recovered\n`\n\n/**\n * Lua script for renewing the acquired timestamp of in-flight jobs (heartbeat).\n * Only entries still present in the active hash AND still owned by the calling\n * worker are renewed, so a job that was already recovered, finalized, or\n * re-acquired by another worker is never resurrected by a late heartbeat.\n * Preserves the existing worker info, updating only acquiredAt.\n * Returns the number of jobs renewed.\n */\nexport const RENEW_JOBS_SCRIPT = `\n local active_key = KEYS[1]\n local now = tonumber(ARGV[1])\n local worker_id = ARGV[2]\n\n local renewed = 0\n for i = 3, #ARGV do\n local job_id = ARGV[i]\n local active_data = redis.call('HGET', active_key, job_id)\n if active_data then\n local active = cjson.decode(active_data)\n -- Only the worker that currently owns the lease may renew it.\n if active.workerId == worker_id then\n active.acquiredAt = now\n redis.call('HSET', active_key, job_id, cjson.encode(active))\n renewed = renewed + 1\n end\n end\n end\n\n return renewed\n`\n\n/**\n * Lua script for getting a job record with its status.\n */\nexport const GET_JOB_SCRIPT = `\n local data_key = KEYS[1]\n local pending_key = KEYS[2]\n local delayed_key = KEYS[3]\n local active_key = KEYS[4]\n local completed_key = KEYS[5]\n local failed_key = KEYS[6]\n local overlay_key = KEYS[7]\n local job_id = ARGV[1]\n\n${REDIS_JOB_STORAGE_LUA}\n\n local job_data = redis.call('HGET', data_key, job_id)\n if not job_data then\n return nil\n end\n\n local status = nil\n local finished_at = nil\n local error_msg = nil\n\n -- Check status in order\n if redis.call('HEXISTS', active_key, job_id) == 1 then\n status = 'active'\n elseif redis.call('ZSCORE', pending_key, job_id) then\n status = 'pending'\n elseif redis.call('ZSCORE', delayed_key, job_id) then\n status = 'delayed'\n else\n local completed_data = redis.call('HGET', completed_key, job_id)\n if completed_data then\n status = 'completed'\n local record = cjson.decode(completed_data)\n finished_at = record.finishedAt\n else\n local failed_data = redis.call('HGET', failed_key, job_id)\n if failed_data then\n status = 'failed'\n local record = cjson.decode(failed_data)\n finished_at = record.finishedAt\n error_msg = record.error\n end\n end\n end\n\n if not status then\n return nil\n end\n\n return encode_job_result(job_data, overlay_key, job_id, {\n status = status,\n finishedAt = finished_at,\n error = error_msg\n })\n`\n\n/**\n * Lua script for atomically claiming a due schedule.\n * Iterates the schedule index server-side and claims the first due schedule.\n * Returns the schedule data if claimed, nil otherwise.\n */\nexport const CLAIM_SCHEDULE_SCRIPT = `\n local schedules_index_key = KEYS[1]\n local schedule_key_prefix = KEYS[2]\n local now = tonumber(ARGV[1])\n\n local ids = redis.call('SMEMBERS', schedules_index_key)\n\n for i = 1, #ids do\n local schedule_key = schedule_key_prefix .. ids[i]\n\n -- Get schedule data\n local data = redis.call('HGETALL', schedule_key)\n if #data > 0 then\n -- Convert HGETALL result to table\n local schedule = {}\n for j = 1, #data, 2 do\n schedule[data[j]] = data[j + 1]\n end\n\n -- Check if schedule is due\n if schedule.status == 'active' then\n local next_run_at = tonumber(schedule.next_run_at)\n\n if next_run_at and next_run_at <= now then\n local run_count = tonumber(schedule.run_count or '0')\n local run_limit = schedule.run_limit and tonumber(schedule.run_limit) or nil\n local to_date = schedule.to_date and tonumber(schedule.to_date) or nil\n\n -- Check limits\n if not (run_limit and run_count >= run_limit) and not (to_date and now > to_date) then\n -- This schedule is claimable - atomically update it\n local new_run_count = run_count + 1\n\n -- Calculate new next_run_at (simple interval-based for now)\n -- Complex cron calculation happens in the caller\n local new_next_run_at = ''\n local every_ms = schedule.every_ms and tonumber(schedule.every_ms) or nil\n if every_ms then\n new_next_run_at = tostring(now + every_ms)\n end\n\n -- Check if we've hit the limit after this run\n if run_limit and new_run_count >= run_limit then\n new_next_run_at = ''\n end\n\n -- Check if past end date\n if to_date and new_next_run_at ~= '' and tonumber(new_next_run_at) > to_date then\n new_next_run_at = ''\n end\n\n -- Update the schedule atomically\n redis.call('HSET', schedule_key,\n 'next_run_at', new_next_run_at,\n 'last_run_at', tostring(now),\n 'run_count', tostring(new_run_count))\n\n -- Return the schedule data (before update) as JSON\n return cjson.encode(schedule)\n end\n end\n end\n end\n end\n\n return nil\n`\n"],"mappings":"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","names":[]}
|
package/build/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Q as QueueManagerConfig, W as WorkerCycle, R as RetryConfig, g as JobOptions, h as QueueConfig,
|
|
2
|
-
export { m as JobBatchDispatcher,
|
|
1
|
+
import { Q as QueueManagerConfig, W as WorkerCycle, R as RetryConfig, g as JobOptions, h as QueueConfig, i as Duration, A as Adapter, j as JobFactory, L as Logger, b as AcquiredJob, k as Job, a as JobClass, e as ScheduleData, l as ScheduleStatus, f as ScheduleListOptions } from './job-C4oyCVxR.js';
|
|
2
|
+
export { m as JobBatchDispatcher, n as ScheduleBuilder, o as customBackoff, p as exponentialBackoff, q as fixedBackoff, r as linearBackoff } from './job-C4oyCVxR.js';
|
|
3
3
|
import { FakeAdapter } from './src/drivers/fake_adapter.js';
|
|
4
4
|
import { Knex } from 'knex';
|
|
5
5
|
import * as _poppinss_utils_exception from '@poppinss/utils/exception';
|
|
@@ -513,6 +513,14 @@ declare class QueueSchemaService {
|
|
|
513
513
|
* The optional callback allows adding custom columns.
|
|
514
514
|
*/
|
|
515
515
|
createJobsTable(tableName?: string, extend?: (table: Knex.CreateTableBuilder) => void): Promise<void>;
|
|
516
|
+
/**
|
|
517
|
+
* Idempotent migration: adds dedup columns (dedup_id, dedup_at, dedup_ttl)
|
|
518
|
+
* and a (queue, dedup_id) index to an existing jobs table.
|
|
519
|
+
*
|
|
520
|
+
* Safe to run multiple times. Uses hasColumn checks so it won't fail on re-runs.
|
|
521
|
+
* For large Postgres tables, consider pausing workers during the run.
|
|
522
|
+
*/
|
|
523
|
+
addDedupColumns(tableName?: string): Promise<void>;
|
|
516
524
|
/**
|
|
517
525
|
* Creates the schedules table with the default schema.
|
|
518
526
|
* The optional callback allows adding custom columns.
|