bullmq 1.76.2 → 1.76.5
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/dist/bullmq.d.ts +2 -1
- package/dist/cjs/classes/queue-getters.d.ts +2 -1
- package/dist/cjs/classes/queue-getters.js +23 -17
- package/dist/cjs/classes/queue-getters.js.map +1 -1
- package/dist/cjs/classes/scripts.js +27 -19
- package/dist/cjs/classes/scripts.js.map +1 -1
- package/dist/cjs/classes/worker.js.map +1 -1
- package/dist/cjs/commands/includes/moveJobFromWaitToActive.lua +83 -3
- package/dist/cjs/commands/moveToActive-8.lua +10 -66
- package/dist/cjs/commands/{moveToFinished-9.lua → moveToFinished-12.lua} +62 -58
- package/dist/esm/classes/queue-getters.d.ts +2 -1
- package/dist/esm/classes/queue-getters.js +23 -17
- package/dist/esm/classes/queue-getters.js.map +1 -1
- package/dist/esm/classes/scripts.js +27 -19
- package/dist/esm/classes/scripts.js.map +1 -1
- package/dist/esm/classes/worker.js.map +1 -1
- package/dist/esm/commands/includes/moveJobFromWaitToActive.lua +83 -3
- package/dist/esm/commands/moveToActive-8.lua +10 -66
- package/dist/esm/commands/{moveToFinished-9.lua → moveToFinished-12.lua} +62 -58
- package/package.json +1 -1
@@ -1,11 +1,91 @@
|
|
1
1
|
|
2
2
|
--[[
|
3
3
|
Function to move job from wait state to active.
|
4
|
+
Input:
|
5
|
+
keys[1] wait key
|
6
|
+
keys[2] active key
|
7
|
+
keys[3] priority key
|
8
|
+
keys[4] stream events key
|
9
|
+
keys[5] stalled key
|
10
|
+
|
11
|
+
-- Rate limiting
|
12
|
+
keys[6] rate limiter key
|
13
|
+
keys[7] delayed key
|
14
|
+
|
15
|
+
-- Delay events
|
16
|
+
keys[8] delay stream key
|
17
|
+
|
18
|
+
opts - token - lock token
|
19
|
+
opts - lockDuration
|
20
|
+
opts - limiter
|
4
21
|
]]
|
5
22
|
|
6
|
-
local function moveJobFromWaitToActive(
|
7
|
-
|
8
|
-
|
23
|
+
local function moveJobFromWaitToActive(keys, keyPrefix, jobId, processedOn, opts)
|
24
|
+
-- Check if we need to perform rate limiting.
|
25
|
+
local maxJobs = tonumber(opts['limiter'] and opts['limiter']['max'])
|
26
|
+
|
27
|
+
if(maxJobs) then
|
28
|
+
local rateLimiterKey = keys[6];
|
29
|
+
|
30
|
+
local groupKey
|
31
|
+
local groupKeyOpt = opts['limiter'] and opts['limiter']['groupKey'] or ""
|
32
|
+
if groupKeyOpt ~= "" then
|
33
|
+
groupKey = string.match(jobId, "[^:]+$")
|
34
|
+
if groupKey ~= jobId then
|
35
|
+
rateLimiterKey = rateLimiterKey .. ":" .. groupKey
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
local jobCounter
|
40
|
+
|
41
|
+
if groupKey ~= nil then
|
42
|
+
if rateLimiterKey ~= keys[6] then
|
43
|
+
jobCounter = tonumber(rcall("INCR", rateLimiterKey))
|
44
|
+
end
|
45
|
+
else
|
46
|
+
jobCounter = tonumber(rcall("INCR", rateLimiterKey))
|
47
|
+
end
|
48
|
+
|
49
|
+
local limiterDuration = opts['limiter'] and opts['limiter']['duration']
|
50
|
+
-- check if rate limit hit
|
51
|
+
if jobCounter ~= nil and jobCounter > maxJobs then
|
52
|
+
local exceedingJobs = jobCounter - maxJobs
|
53
|
+
local expireTime = tonumber(rcall("PTTL", rateLimiterKey))
|
54
|
+
local delay = expireTime + ((exceedingJobs - 1) * limiterDuration) / maxJobs;
|
55
|
+
local timestamp = delay + tonumber(processedOn)
|
56
|
+
|
57
|
+
-- put job into delayed queue
|
58
|
+
rcall("ZADD", keys[7], timestamp * 0x1000 + bit.band(jobCounter, 0xfff), jobId);
|
59
|
+
rcall("XADD", keys[4], "*", "event", "delayed", "jobId", jobId, "delay", timestamp);
|
60
|
+
rcall("XADD", keys[8], "*", "nextTimestamp", timestamp);
|
61
|
+
-- remove from active queue
|
62
|
+
rcall("LREM", keys[2], 1, jobId)
|
63
|
+
|
64
|
+
-- Return when we can process more jobs
|
65
|
+
return expireTime
|
66
|
+
else
|
67
|
+
if jobCounter == 1 then
|
68
|
+
rcall("PEXPIRE", rateLimiterKey, limiterDuration)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
local jobKey = keyPrefix .. jobId
|
74
|
+
local lockKey = jobKey .. ':lock'
|
75
|
+
|
76
|
+
-- get a lock
|
77
|
+
if opts['token'] ~= "0" then
|
78
|
+
rcall("SET", lockKey, opts['token'], "PX", opts['lockDuration'])
|
79
|
+
end
|
80
|
+
|
81
|
+
rcall("ZREM", keys[3], jobId) -- remove from priority
|
82
|
+
rcall("XADD", keys[4], "*", "event", "active", "jobId", jobId, "prev", "waiting")
|
9
83
|
rcall("HSET", jobKey, "processedOn", processedOn)
|
10
84
|
rcall("HINCRBY", jobKey, "attemptsMade", 1)
|
85
|
+
local len = rcall("LLEN", keys[1])
|
86
|
+
if len == 0 then
|
87
|
+
rcall("XADD", keys[4], "*", "event", "drained");
|
88
|
+
end
|
89
|
+
|
90
|
+
return {rcall("HGETALL", jobKey), jobId} -- get job data
|
11
91
|
end
|
@@ -22,14 +22,13 @@
|
|
22
22
|
|
23
23
|
-- Arguments
|
24
24
|
ARGV[1] key prefix
|
25
|
-
ARGV[2]
|
26
|
-
ARGV[3]
|
27
|
-
ARGV[4]
|
28
|
-
ARGV[5] optional job ID
|
25
|
+
ARGV[2] timestamp
|
26
|
+
ARGV[3] optional job ID
|
27
|
+
ARGV[4] opts
|
29
28
|
|
30
|
-
|
31
|
-
|
32
|
-
|
29
|
+
opts - token - lock token
|
30
|
+
opts - lockDuration
|
31
|
+
opts - limiter
|
33
32
|
]]
|
34
33
|
|
35
34
|
local jobId
|
@@ -38,8 +37,8 @@ local rcall = redis.call
|
|
38
37
|
-- Includes
|
39
38
|
--- @include "includes/moveJobFromWaitToActive"
|
40
39
|
|
41
|
-
if(ARGV[
|
42
|
-
jobId = ARGV[
|
40
|
+
if(ARGV[3] ~= "") then
|
41
|
+
jobId = ARGV[3]
|
43
42
|
|
44
43
|
-- clean stalled key
|
45
44
|
rcall("SREM", KEYS[5], jobId)
|
@@ -49,62 +48,7 @@ else
|
|
49
48
|
end
|
50
49
|
|
51
50
|
if jobId then
|
52
|
-
|
53
|
-
local maxJobs = tonumber(ARGV[6])
|
51
|
+
local opts = cmsgpack.unpack(ARGV[4])
|
54
52
|
|
55
|
-
|
56
|
-
local rateLimiterKey = KEYS[6];
|
57
|
-
|
58
|
-
local groupKey
|
59
|
-
if(ARGV[8]) then
|
60
|
-
groupKey = string.match(jobId, "[^:]+$")
|
61
|
-
if groupKey ~= jobId then
|
62
|
-
rateLimiterKey = rateLimiterKey .. ":" .. groupKey
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
local jobCounter
|
67
|
-
|
68
|
-
if groupKey ~= nil then
|
69
|
-
if rateLimiterKey ~= KEYS[6] then
|
70
|
-
jobCounter = tonumber(rcall("INCR", rateLimiterKey))
|
71
|
-
end
|
72
|
-
else
|
73
|
-
jobCounter = tonumber(rcall("INCR", rateLimiterKey))
|
74
|
-
end
|
75
|
-
|
76
|
-
-- check if rate limit hit
|
77
|
-
if jobCounter ~= nil and jobCounter > maxJobs then
|
78
|
-
local exceedingJobs = jobCounter - maxJobs
|
79
|
-
local expireTime = tonumber(rcall("PTTL", rateLimiterKey))
|
80
|
-
local delay = expireTime + ((exceedingJobs - 1) * ARGV[7]) / maxJobs;
|
81
|
-
local timestamp = delay + tonumber(ARGV[4])
|
82
|
-
|
83
|
-
-- put job into delayed queue
|
84
|
-
rcall("ZADD", KEYS[7], timestamp * 0x1000 + bit.band(jobCounter, 0xfff), jobId);
|
85
|
-
rcall("XADD", KEYS[4], "*", "event", "delayed", "jobId", jobId, "delay", timestamp);
|
86
|
-
rcall("XADD", KEYS[8], "*", "nextTimestamp", timestamp);
|
87
|
-
-- remove from active queue
|
88
|
-
rcall("LREM", KEYS[2], 1, jobId)
|
89
|
-
|
90
|
-
-- Return when we can process more jobs
|
91
|
-
return expireTime
|
92
|
-
else
|
93
|
-
if jobCounter == 1 then
|
94
|
-
rcall("PEXPIRE", rateLimiterKey, ARGV[7])
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
local jobKey = ARGV[1] .. jobId
|
100
|
-
local lockKey = jobKey .. ':lock'
|
101
|
-
|
102
|
-
-- get a lock
|
103
|
-
rcall("SET", lockKey, ARGV[2], "PX", ARGV[3])
|
104
|
-
|
105
|
-
moveJobFromWaitToActive(KEYS[3], KEYS[4], jobKey, jobId, ARGV[4])
|
106
|
-
|
107
|
-
return {rcall("HGETALL", jobKey), jobId} -- get job data
|
108
|
-
else
|
109
|
-
rcall("XADD", KEYS[4], "*", "event", "drained");
|
53
|
+
return moveJobFromWaitToActive(KEYS, ARGV[1], jobId, ARGV[2], opts)
|
110
54
|
end
|
@@ -5,33 +5,42 @@
|
|
5
5
|
and the lock must be released in this script.
|
6
6
|
|
7
7
|
Input:
|
8
|
-
KEYS[1]
|
9
|
-
KEYS[2]
|
10
|
-
KEYS[3]
|
11
|
-
KEYS[4]
|
12
|
-
KEYS[5]
|
13
|
-
|
14
|
-
|
15
|
-
KEYS[
|
16
|
-
KEYS[
|
8
|
+
KEYS[1] wait key
|
9
|
+
KEYS[2] active key
|
10
|
+
KEYS[3] priority key
|
11
|
+
KEYS[4] event stream key
|
12
|
+
KEYS[5] stalled key
|
13
|
+
|
14
|
+
-- Rate limiting
|
15
|
+
KEYS[6] rate limiter key
|
16
|
+
KEYS[7] delayed key
|
17
|
+
|
18
|
+
-- Delay events
|
19
|
+
KEYS[8] delay stream key
|
20
|
+
|
21
|
+
KEYS[9] completed/failed key
|
22
|
+
KEYS[10] jobId key
|
23
|
+
KEYS[11] meta key
|
24
|
+
KEYS[12] metrics key
|
17
25
|
|
18
26
|
ARGV[1] jobId
|
19
27
|
ARGV[2] timestamp
|
20
28
|
ARGV[3] msg property
|
21
29
|
ARGV[4] return value / failed reason
|
22
30
|
ARGV[5] target (completed/failed)
|
23
|
-
ARGV[6]
|
24
|
-
ARGV[7]
|
25
|
-
ARGV[8]
|
26
|
-
ARGV[9]
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
ARGV[6] event data (? maybe just send jobid).
|
32
|
+
ARGV[7] fetch next?
|
33
|
+
ARGV[8] keys prefix
|
34
|
+
ARGV[9] opts
|
35
|
+
|
36
|
+
opts - token - lock token
|
37
|
+
opts - keepJobs
|
38
|
+
opts - lockDuration - lock duration in milliseconds
|
39
|
+
opts - parent - parent data
|
40
|
+
opts - parentKey
|
41
|
+
opts - attempts max attempts
|
42
|
+
opts - attemptsMade
|
43
|
+
opts - maxMetricsSize
|
35
44
|
|
36
45
|
Output:
|
37
46
|
0 OK
|
@@ -52,13 +61,25 @@
|
|
52
61
|
--- @include "includes/updateParentDepsIfNeeded"
|
53
62
|
--- @include "includes/collectMetrics"
|
54
63
|
|
55
|
-
local jobIdKey = KEYS[
|
64
|
+
local jobIdKey = KEYS[10]
|
56
65
|
if rcall("EXISTS", jobIdKey) == 1 then -- // Make sure job exists
|
57
|
-
|
66
|
+
local opts = cmsgpack.unpack(ARGV[9])
|
67
|
+
|
68
|
+
local token = opts['token']
|
69
|
+
local parentId = opts['parent'] and opts['parent']['id'] or ""
|
70
|
+
local parentQueueKey = opts['parent'] and opts['parent']['queue'] or ""
|
71
|
+
local parentKey = opts['parentKey'] or ""
|
72
|
+
local attempts = opts['attempts']
|
73
|
+
local attemptsMade = opts['attemptsMade']
|
74
|
+
local maxMetricsSize = opts['maxMetricsSize']
|
75
|
+
local maxCount = opts['keepJobs']['count']
|
76
|
+
local maxAge = opts['keepJobs']['age']
|
77
|
+
|
78
|
+
if token ~= "0" then
|
58
79
|
local lockKey = jobIdKey .. ':lock'
|
59
|
-
if rcall("GET", lockKey) ==
|
80
|
+
if rcall("GET", lockKey) == token then
|
60
81
|
rcall("DEL", lockKey)
|
61
|
-
rcall("SREM", KEYS[
|
82
|
+
rcall("SREM", KEYS[5], ARGV[1])
|
62
83
|
else
|
63
84
|
return -2
|
64
85
|
end
|
@@ -72,12 +93,12 @@ if rcall("EXISTS", jobIdKey) == 1 then -- // Make sure job exists
|
|
72
93
|
local timestamp = ARGV[2]
|
73
94
|
|
74
95
|
-- Remove from active list (if not active we shall return error)
|
75
|
-
local numRemovedElements = rcall("LREM", KEYS[
|
96
|
+
local numRemovedElements = rcall("LREM", KEYS[2], -1, jobId)
|
76
97
|
|
77
98
|
if (numRemovedElements < 1) then return -3 end
|
78
99
|
|
79
100
|
-- Trim events before emiting them to avoid trimming events emitted in this script
|
80
|
-
trimEvents(KEYS[
|
101
|
+
trimEvents(KEYS[11], KEYS[4])
|
81
102
|
|
82
103
|
-- If job has a parent we need to
|
83
104
|
-- 1) remove this job id from parents dependencies
|
@@ -85,11 +106,9 @@ if rcall("EXISTS", jobIdKey) == 1 then -- // Make sure job exists
|
|
85
106
|
-- 3) push the results into parent "results" list
|
86
107
|
-- 4) if parent's dependencies is empty, then move parent to "wait/paused". Note it may be a different queue!.
|
87
108
|
-- NOTE: Priorities not supported yet for parent jobs.
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
parentId = getJobIdFromKey(ARGV[14])
|
92
|
-
parentQueueKey = getJobKeyPrefix(ARGV[14], ":" .. parentId)
|
109
|
+
if parentId == "" and parentKey ~= "" then
|
110
|
+
parentId = getJobIdFromKey(parentKey)
|
111
|
+
parentQueueKey = getJobKeyPrefix(parentKey, ":" .. parentId)
|
93
112
|
end
|
94
113
|
if parentId ~= "" and ARGV[5] == "completed" then
|
95
114
|
local parentKey = parentQueueKey .. ":" .. parentId
|
@@ -102,17 +121,14 @@ if rcall("EXISTS", jobIdKey) == 1 then -- // Make sure job exists
|
|
102
121
|
end
|
103
122
|
|
104
123
|
-- Remove job?
|
105
|
-
local keepJobs = cmsgpack.unpack(ARGV[6])
|
106
|
-
local maxCount = keepJobs['count']
|
107
|
-
local maxAge = keepJobs['age']
|
108
124
|
if maxCount ~= 0 then
|
109
|
-
local targetSet = KEYS[
|
125
|
+
local targetSet = KEYS[9]
|
110
126
|
-- Add to complete/failed set
|
111
127
|
rcall("ZADD", targetSet, timestamp, jobId)
|
112
128
|
rcall("HMSET", jobIdKey, ARGV[3], ARGV[4], "finishedOn", timestamp) -- "returnvalue" / "failedReason" and "finishedOn"
|
113
129
|
|
114
130
|
-- Remove old jobs?
|
115
|
-
local prefix = ARGV[
|
131
|
+
local prefix = ARGV[8]
|
116
132
|
|
117
133
|
if maxAge ~= nil then
|
118
134
|
local start = timestamp - maxAge * 1000
|
@@ -135,40 +151,28 @@ if rcall("EXISTS", jobIdKey) == 1 then -- // Make sure job exists
|
|
135
151
|
rcall("DEL", jobIdKey, jobIdKey .. ':logs', jobIdKey .. ':processed')
|
136
152
|
end
|
137
153
|
|
138
|
-
rcall("XADD", KEYS[
|
154
|
+
rcall("XADD", KEYS[4], "*", "event", ARGV[5], "jobId", jobId, ARGV[3],
|
139
155
|
ARGV[4])
|
140
156
|
|
141
157
|
if ARGV[5] == "failed" then
|
142
|
-
if tonumber(
|
143
|
-
rcall("XADD", KEYS[
|
144
|
-
jobId, "attemptsMade",
|
158
|
+
if tonumber(attemptsMade) >= tonumber(attempts) then
|
159
|
+
rcall("XADD", KEYS[4], "*", "event", "retries-exhausted", "jobId",
|
160
|
+
jobId, "attemptsMade", attemptsMade)
|
145
161
|
end
|
146
162
|
end
|
147
163
|
|
148
164
|
-- Collect metrics
|
149
|
-
if
|
150
|
-
collectMetrics(KEYS[
|
165
|
+
if maxMetricsSize ~= "" then
|
166
|
+
collectMetrics(KEYS[12], KEYS[12]..':data', maxMetricsSize, timestamp)
|
151
167
|
end
|
152
168
|
|
153
169
|
-- Try to get next job to avoid an extra roundtrip if the queue is not closing,
|
154
170
|
-- and not rate limited.
|
155
|
-
if (ARGV[
|
171
|
+
if (ARGV[7] == "1") then
|
156
172
|
-- move from wait to active
|
157
|
-
local jobId = rcall("RPOPLPUSH", KEYS[
|
173
|
+
local jobId = rcall("RPOPLPUSH", KEYS[1], KEYS[2])
|
158
174
|
if jobId then
|
159
|
-
|
160
|
-
local lockKey = jobKey .. ':lock'
|
161
|
-
|
162
|
-
-- get a lock
|
163
|
-
if ARGV[10] ~= "0" then
|
164
|
-
rcall("SET", lockKey, ARGV[10], "PX", ARGV[11])
|
165
|
-
end
|
166
|
-
|
167
|
-
moveJobFromWaitToActive(KEYS[5], KEYS[6], jobKey, jobId, timestamp)
|
168
|
-
|
169
|
-
return {rcall("HGETALL", jobKey), jobId} -- get job data
|
170
|
-
else
|
171
|
-
rcall("XADD", KEYS[6], "*", "event", "drained");
|
175
|
+
return moveJobFromWaitToActive(KEYS, ARGV[8], jobId, timestamp, opts)
|
172
176
|
end
|
173
177
|
end
|
174
178
|
|