groupmq-plus 1.1.0 → 1.1.2
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/index.cjs +218 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +62 -9
- package/dist/index.d.ts +62 -9
- package/dist/index.js +218 -10
- package/dist/index.js.map +1 -1
- package/dist/lua/change-delay.lua +20 -13
- package/dist/lua/clean-status.lua +13 -4
- package/dist/lua/complete-and-reserve-next-with-metadata.lua +8 -0
- package/dist/lua/complete-with-metadata.lua +8 -0
- package/dist/lua/enqueue-flow.lua +4 -0
- package/dist/lua/record-job-result.lua +10 -0
- package/dist/lua/remove.lua +13 -3
- package/dist/lua/reserve.lua +2 -2
- package/package.json +1 -1
|
@@ -4,6 +4,11 @@ local jobId = ARGV[1]
|
|
|
4
4
|
local newDelayUntil = tonumber(ARGV[2])
|
|
5
5
|
local now = tonumber(ARGV[3])
|
|
6
6
|
|
|
7
|
+
-- Validate required parameters
|
|
8
|
+
if not newDelayUntil or not now then
|
|
9
|
+
return 0
|
|
10
|
+
end
|
|
11
|
+
|
|
7
12
|
local jobKey = ns .. ":job:" .. jobId
|
|
8
13
|
local delayedKey = ns .. ":delayed"
|
|
9
14
|
local readyKey = ns .. ":ready"
|
|
@@ -21,6 +26,12 @@ end
|
|
|
21
26
|
|
|
22
27
|
local gZ = ns .. ":g:" .. groupId
|
|
23
28
|
|
|
29
|
+
-- Check if job is still in group (not deleted)
|
|
30
|
+
local jobInGroup = redis.call("ZSCORE", gZ, jobId)
|
|
31
|
+
if not jobInGroup then
|
|
32
|
+
return 0
|
|
33
|
+
end
|
|
34
|
+
|
|
24
35
|
-- Update job's delayUntil field
|
|
25
36
|
redis.call("HSET", jobKey, "delayUntil", tostring(newDelayUntil))
|
|
26
37
|
|
|
@@ -30,13 +41,9 @@ local inDelayed = redis.call("ZSCORE", delayedKey, jobId)
|
|
|
30
41
|
if newDelayUntil > 0 and newDelayUntil > now then
|
|
31
42
|
-- Job should be delayed
|
|
32
43
|
redis.call("HSET", jobKey, "status", "delayed")
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
else
|
|
37
|
-
-- Move to delayed
|
|
38
|
-
redis.call("ZADD", delayedKey, newDelayUntil, jobId)
|
|
39
|
-
-- If this is the head job, remove group from ready
|
|
44
|
+
redis.call("ZADD", delayedKey, newDelayUntil, jobId)
|
|
45
|
+
-- If this is the head job and wasn't already delayed, remove group from ready
|
|
46
|
+
if not inDelayed then
|
|
40
47
|
local head = redis.call("ZRANGE", gZ, 0, 0)
|
|
41
48
|
if head and #head > 0 and head[1] == jobId then
|
|
42
49
|
redis.call("ZREM", readyKey, groupId)
|
|
@@ -48,12 +55,12 @@ else
|
|
|
48
55
|
if inDelayed then
|
|
49
56
|
-- Remove from delayed
|
|
50
57
|
redis.call("ZREM", delayedKey, jobId)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
58
|
+
end
|
|
59
|
+
-- If this is the head job, ensure group is in ready
|
|
60
|
+
local head = redis.call("ZRANGE", gZ, 0, 0, "WITHSCORES")
|
|
61
|
+
if head and #head >= 2 and head[1] == jobId then
|
|
62
|
+
local headScore = tonumber(head[2])
|
|
63
|
+
redis.call("ZADD", readyKey, headScore, groupId)
|
|
57
64
|
end
|
|
58
65
|
end
|
|
59
66
|
|
|
@@ -30,6 +30,7 @@ for i = 1, #ids do
|
|
|
30
30
|
-- Remove from group and update ready queue for ALL statuses
|
|
31
31
|
-- This prevents poisoned groups when completed/failed jobs are cleaned
|
|
32
32
|
local groupId = redis.call('HGET', jobKey, 'groupId')
|
|
33
|
+
local parentId = redis.call('HGET', jobKey, 'parentId')
|
|
33
34
|
if groupId then
|
|
34
35
|
local gZ = ns .. ':g:' .. groupId
|
|
35
36
|
local readyKey = ns .. ':ready'
|
|
@@ -51,10 +52,18 @@ for i = 1, #ids do
|
|
|
51
52
|
end
|
|
52
53
|
end
|
|
53
54
|
|
|
54
|
-
-- Delete job hash, idempotence key and
|
|
55
|
-
redis.call('DEL',
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
-- Delete job hash, idempotence key, flow results and children tracking (variadic DEL optimization)
|
|
56
|
+
redis.call('DEL',
|
|
57
|
+
jobKey,
|
|
58
|
+
ns .. ':unique:' .. id,
|
|
59
|
+
ns .. ':flow:results:' .. id,
|
|
60
|
+
ns .. ':flow:children:' .. id
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
-- 2. If this job is a child, remove it from parent's children set
|
|
64
|
+
if parentId then
|
|
65
|
+
redis.call('SREM', ns .. ':flow:children:' .. parentId, id)
|
|
66
|
+
end
|
|
58
67
|
|
|
59
68
|
removed = removed + 1
|
|
60
69
|
end
|
|
@@ -145,6 +145,14 @@ elseif status == "failed" then
|
|
|
145
145
|
end
|
|
146
146
|
end
|
|
147
147
|
|
|
148
|
+
-- Publish completion/failure event for waiters
|
|
149
|
+
local eventPayload = cjson.encode({
|
|
150
|
+
id = completedJobId,
|
|
151
|
+
status = status,
|
|
152
|
+
result = resultOrError
|
|
153
|
+
})
|
|
154
|
+
redis.call("PUBLISH", ns .. ":events", eventPayload)
|
|
155
|
+
|
|
148
156
|
-- Part 3: Handle group active list and get next job (BullMQ-style)
|
|
149
157
|
local groupActiveKey = ns .. ":g:" .. gid .. ":active"
|
|
150
158
|
local activeJobId = redis.call("LINDEX", groupActiveKey, 0)
|
|
@@ -186,5 +186,13 @@ elseif status == "failed" then
|
|
|
186
186
|
end
|
|
187
187
|
end
|
|
188
188
|
|
|
189
|
+
-- Publish completion/failure event for waiters
|
|
190
|
+
local eventPayload = cjson.encode({
|
|
191
|
+
id = jobId,
|
|
192
|
+
status = status,
|
|
193
|
+
result = resultOrError
|
|
194
|
+
})
|
|
195
|
+
redis.call("PUBLISH", ns .. ":events", eventPayload)
|
|
196
|
+
|
|
189
197
|
return 1
|
|
190
198
|
|
|
@@ -83,6 +83,10 @@ for i = 0, childrenCount - 1 do
|
|
|
83
83
|
|
|
84
84
|
redis.call("SET", ns .. ":unique:" .. childId, childId)
|
|
85
85
|
|
|
86
|
+
-- Record child relationship for introspection
|
|
87
|
+
-- Key: {ns}:flow:children:{parentId}
|
|
88
|
+
redis.call("SADD", ns .. ":flow:children:" .. parentId, childId)
|
|
89
|
+
|
|
86
90
|
-- Handle delay or immediate waiting
|
|
87
91
|
if childDelay > 0 then
|
|
88
92
|
local delayUntil = now + childDelay
|
|
@@ -139,5 +139,15 @@ elseif status == "failed" then
|
|
|
139
139
|
end
|
|
140
140
|
end
|
|
141
141
|
|
|
142
|
+
-- Publish completion/failure event for waiters
|
|
143
|
+
if status == "completed" or status == "failed" then
|
|
144
|
+
local eventPayload = cjson.encode({
|
|
145
|
+
id = jobId,
|
|
146
|
+
status = status,
|
|
147
|
+
result = resultOrError
|
|
148
|
+
})
|
|
149
|
+
redis.call("PUBLISH", ns .. ":events", eventPayload)
|
|
150
|
+
end
|
|
151
|
+
|
|
142
152
|
return 1
|
|
143
153
|
|
package/dist/lua/remove.lua
CHANGED
|
@@ -13,6 +13,7 @@ if redis.call("EXISTS", jobKey) == 0 then
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
local groupId = redis.call("HGET", jobKey, "groupId")
|
|
16
|
+
local parentId = redis.call("HGET", jobKey, "parentId")
|
|
16
17
|
|
|
17
18
|
-- Remove from delayed and processing structures
|
|
18
19
|
redis.call("ZREM", delayedKey, jobId)
|
|
@@ -46,9 +47,18 @@ if groupId then
|
|
|
46
47
|
end
|
|
47
48
|
end
|
|
48
49
|
|
|
49
|
-
-- Finally, delete the job hash and
|
|
50
|
-
redis.call("DEL",
|
|
51
|
-
|
|
50
|
+
-- Finally, delete the job hash, flow results and children tracking (variadic DEL optimization)
|
|
51
|
+
redis.call("DEL",
|
|
52
|
+
jobKey,
|
|
53
|
+
ns .. ":flow:results:" .. jobId,
|
|
54
|
+
ns .. ":flow:children:" .. jobId
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
-- Clean up flow relationships
|
|
58
|
+
-- If this job is a child, remove it from parent's children set
|
|
59
|
+
if parentId then
|
|
60
|
+
redis.call("SREM", ns .. ":flow:children:" .. parentId, jobId)
|
|
61
|
+
end
|
|
52
62
|
|
|
53
63
|
return 1
|
|
54
64
|
|
package/dist/lua/reserve.lua
CHANGED
|
@@ -87,8 +87,8 @@ for i = 1, #groups, 2 do
|
|
|
87
87
|
-- Group has capacity, try to get head job
|
|
88
88
|
local head = redis.call("ZRANGE", gZ, 0, 0, "WITHSCORES")
|
|
89
89
|
if head and #head >= 2 then
|
|
90
|
-
local
|
|
91
|
-
local headJobKey = ns .. ":job:" ..
|
|
90
|
+
local candidateJobId = head[1]
|
|
91
|
+
local headJobKey = ns .. ":job:" .. candidateJobId
|
|
92
92
|
|
|
93
93
|
-- Skip if head job is delayed (will be promoted later)
|
|
94
94
|
local jobStatus = redis.call("HGET", headJobKey, "status")
|