groupmq-plus 1.1.1 → 1.1.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.
@@ -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
- if inDelayed then
34
- -- Update existing delay
35
- redis.call("ZADD", delayedKey, newDelayUntil, jobId)
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
- -- If this is the head job, ensure group is in ready
52
- local head = redis.call("ZRANGE", gZ, 0, 0, "WITHSCORES")
53
- if head and #head >= 2 and head[1] == jobId then
54
- local headScore = tonumber(head[2])
55
- redis.call("ZADD", readyKey, headScore, groupId)
56
- end
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 flow results
55
- redis.call('DEL', jobKey)
56
- redis.call('DEL', ns .. ':unique:' .. id)
57
- redis.call('DEL', ns .. ':flow:results:' .. id)
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
@@ -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
@@ -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 flow results
50
- redis.call("DEL", jobKey)
51
- redis.call("DEL", ns .. ":flow:results:" .. jobId)
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
 
@@ -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 headJobId = head[1]
91
- local headJobKey = ns .. ":job:" .. headJobId
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")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "groupmq-plus",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "Per-group FIFO queue on Redis with visibility timeouts and retries.",
5
5
  "license": "MIT",
6
6
  "private": false,