bullmq 5.15.0 → 5.16.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.
Files changed (99) hide show
  1. package/dist/cjs/classes/job-scheduler.js +140 -0
  2. package/dist/cjs/classes/job-scheduler.js.map +1 -0
  3. package/dist/cjs/classes/job.js +1 -1
  4. package/dist/cjs/classes/job.js.map +1 -1
  5. package/dist/cjs/classes/queue.js +64 -0
  6. package/dist/cjs/classes/queue.js.map +1 -1
  7. package/dist/cjs/classes/repeat.js +9 -10
  8. package/dist/cjs/classes/repeat.js.map +1 -1
  9. package/dist/cjs/classes/scripts.js +45 -39
  10. package/dist/cjs/classes/scripts.js.map +1 -1
  11. package/dist/cjs/classes/worker.js +23 -4
  12. package/dist/cjs/classes/worker.js.map +1 -1
  13. package/dist/cjs/commands/addJobScheduler-2.lua +75 -0
  14. package/dist/cjs/commands/{cleanJobsInSet-2.lua → cleanJobsInSet-3.lua} +6 -5
  15. package/dist/cjs/commands/drain-5.lua +41 -0
  16. package/dist/cjs/commands/includes/cleanSet.lua +50 -29
  17. package/dist/cjs/commands/includes/removeZSetJobs.lua +13 -1
  18. package/dist/cjs/commands/moveStalledJobsToWait-9.lua +2 -4
  19. package/dist/cjs/commands/removeJob-2.lua +17 -9
  20. package/dist/cjs/commands/removeJobScheduler-3.lua +43 -0
  21. package/dist/cjs/enums/error-code.js +1 -0
  22. package/dist/cjs/enums/error-code.js.map +1 -1
  23. package/dist/cjs/scripts/addJobScheduler-2.js +223 -0
  24. package/dist/cjs/scripts/addJobScheduler-2.js.map +1 -0
  25. package/dist/cjs/scripts/{cleanJobsInSet-2.js → cleanJobsInSet-3.js} +57 -35
  26. package/dist/cjs/scripts/{cleanJobsInSet-2.js.map → cleanJobsInSet-3.js.map} +1 -1
  27. package/dist/cjs/scripts/{drain-4.js → drain-5.js} +28 -7
  28. package/dist/cjs/scripts/drain-5.js.map +1 -0
  29. package/dist/cjs/scripts/index.js +4 -2
  30. package/dist/cjs/scripts/index.js.map +1 -1
  31. package/dist/cjs/scripts/moveStalledJobsToWait-9.js +2 -3
  32. package/dist/cjs/scripts/moveStalledJobsToWait-9.js.map +1 -1
  33. package/dist/cjs/scripts/obliterate-2.js +11 -1
  34. package/dist/cjs/scripts/obliterate-2.js.map +1 -1
  35. package/dist/cjs/scripts/removeJob-2.js +16 -9
  36. package/dist/cjs/scripts/removeJob-2.js.map +1 -1
  37. package/dist/cjs/scripts/removeJobScheduler-3.js +49 -0
  38. package/dist/cjs/scripts/removeJobScheduler-3.js.map +1 -0
  39. package/dist/cjs/tsconfig-cjs.tsbuildinfo +1 -1
  40. package/dist/cjs/utils.js +25 -1
  41. package/dist/cjs/utils.js.map +1 -1
  42. package/dist/esm/classes/job-scheduler.d.ts +30 -0
  43. package/dist/esm/classes/job-scheduler.js +135 -0
  44. package/dist/esm/classes/job-scheduler.js.map +1 -0
  45. package/dist/esm/classes/job.d.ts +1 -1
  46. package/dist/esm/classes/job.js +2 -2
  47. package/dist/esm/classes/job.js.map +1 -1
  48. package/dist/esm/classes/queue.d.ts +47 -0
  49. package/dist/esm/classes/queue.js +64 -0
  50. package/dist/esm/classes/queue.js.map +1 -1
  51. package/dist/esm/classes/repeat.js +9 -10
  52. package/dist/esm/classes/repeat.js.map +1 -1
  53. package/dist/esm/classes/scripts.d.ts +3 -7
  54. package/dist/esm/classes/scripts.js +46 -40
  55. package/dist/esm/classes/scripts.js.map +1 -1
  56. package/dist/esm/classes/worker.d.ts +3 -0
  57. package/dist/esm/classes/worker.js +23 -4
  58. package/dist/esm/classes/worker.js.map +1 -1
  59. package/dist/esm/commands/addJobScheduler-2.lua +75 -0
  60. package/dist/esm/commands/{cleanJobsInSet-2.lua → cleanJobsInSet-3.lua} +6 -5
  61. package/dist/esm/commands/drain-5.lua +41 -0
  62. package/dist/esm/commands/includes/cleanSet.lua +50 -29
  63. package/dist/esm/commands/includes/removeZSetJobs.lua +13 -1
  64. package/dist/esm/commands/moveStalledJobsToWait-9.lua +2 -4
  65. package/dist/esm/commands/removeJob-2.lua +17 -9
  66. package/dist/esm/commands/removeJobScheduler-3.lua +43 -0
  67. package/dist/esm/enums/error-code.d.ts +2 -1
  68. package/dist/esm/enums/error-code.js +1 -0
  69. package/dist/esm/enums/error-code.js.map +1 -1
  70. package/dist/esm/scripts/addJobScheduler-2.d.ts +5 -0
  71. package/dist/esm/scripts/addJobScheduler-2.js +220 -0
  72. package/dist/esm/scripts/addJobScheduler-2.js.map +1 -0
  73. package/dist/esm/scripts/{cleanJobsInSet-2.js → cleanJobsInSet-3.js} +57 -35
  74. package/dist/esm/scripts/{cleanJobsInSet-2.js.map → cleanJobsInSet-3.js.map} +1 -1
  75. package/dist/esm/scripts/{drain-4.js → drain-5.js} +28 -7
  76. package/dist/esm/scripts/drain-5.js.map +1 -0
  77. package/dist/esm/scripts/index.d.ts +4 -2
  78. package/dist/esm/scripts/index.js +4 -2
  79. package/dist/esm/scripts/index.js.map +1 -1
  80. package/dist/esm/scripts/moveStalledJobsToWait-9.js +2 -3
  81. package/dist/esm/scripts/moveStalledJobsToWait-9.js.map +1 -1
  82. package/dist/esm/scripts/obliterate-2.js +11 -1
  83. package/dist/esm/scripts/obliterate-2.js.map +1 -1
  84. package/dist/esm/scripts/removeJob-2.js +16 -9
  85. package/dist/esm/scripts/removeJob-2.js.map +1 -1
  86. package/dist/esm/scripts/removeJobScheduler-3.d.ts +5 -0
  87. package/dist/esm/scripts/removeJobScheduler-3.js +46 -0
  88. package/dist/esm/scripts/removeJobScheduler-3.js.map +1 -0
  89. package/dist/esm/tsconfig.tsbuildinfo +1 -1
  90. package/dist/esm/utils.d.ts +7 -0
  91. package/dist/esm/utils.js +23 -0
  92. package/dist/esm/utils.js.map +1 -1
  93. package/package.json +1 -1
  94. package/dist/cjs/commands/drain-4.lua +0 -26
  95. package/dist/cjs/scripts/drain-4.js.map +0 -1
  96. package/dist/esm/commands/drain-4.lua +0 -26
  97. package/dist/esm/scripts/drain-4.js.map +0 -1
  98. /package/dist/esm/scripts/{cleanJobsInSet-2.d.ts → cleanJobsInSet-3.d.ts} +0 -0
  99. /package/dist/esm/scripts/{drain-4.d.ts → drain-5.d.ts} +0 -0
@@ -0,0 +1,41 @@
1
+ --[[
2
+ Drains the queue, removes all jobs that are waiting
3
+ or delayed, but not active, completed or failed
4
+
5
+ Input:
6
+ KEYS[1] 'wait',
7
+ KEYS[2] 'paused'
8
+ KEYS[3] 'delayed'
9
+ KEYS[4] 'prioritized'
10
+ KEYS[5] 'jobschedulers' (repeat)
11
+
12
+ ARGV[1] queue key prefix
13
+ ]]
14
+ local rcall = redis.call
15
+ local queueBaseKey = ARGV[1]
16
+
17
+ --- @include "includes/removeListJobs"
18
+ --- @include "includes/removeZSetJobs"
19
+
20
+ removeListJobs(KEYS[1], true, queueBaseKey, 0) -- wait
21
+ removeListJobs(KEYS[2], true, queueBaseKey, 0) -- paused
22
+
23
+ if KEYS[3] ~= "" then
24
+
25
+ -- We must not remove delayed jobs if they are associated to a job scheduler.
26
+ local scheduledJobs = {}
27
+ local jobSchedulers = rcall("ZRANGE", KEYS[5], 0, -1, "WITHSCORES")
28
+
29
+ -- For every job scheduler, get the current delayed job id.
30
+ for i = 1, #jobSchedulers, 2 do
31
+ local jobSchedulerId = jobSchedulers[i]
32
+ local jobSchedulerMillis = jobSchedulers[i + 1]
33
+
34
+ local delayedJobId = "repeat:" .. jobSchedulerId .. ":" .. jobSchedulerMillis
35
+ scheduledJobs[delayedJobId] = true
36
+ end
37
+
38
+ removeZSetJobs(KEYS[3], true, queueBaseKey, 0, scheduledJobs) -- delayed
39
+ end
40
+
41
+ removeZSetJobs(KEYS[4], true, queueBaseKey, 0) -- prioritized
@@ -1,45 +1,66 @@
1
1
  --[[
2
2
  Function to clean job set.
3
3
  Returns jobIds and deleted count number.
4
- ]]
4
+ ]]
5
5
 
6
6
  -- Includes
7
7
  --- @include "batches"
8
8
  --- @include "getJobsInZset"
9
9
  --- @include "getTimestamp"
10
10
  --- @include "removeJob"
11
-
12
- local function cleanSet(setKey, jobKeyPrefix, rangeEnd, timestamp, limit, attributes, isFinished)
13
- local jobs = getJobsInZset(setKey, rangeEnd, limit)
14
- local deleted = {}
15
- local deletedCount = 0
16
- local jobTS
17
- for i, job in ipairs(jobs) do
18
- if limit > 0 and deletedCount >= limit then
19
- break
11
+ local function isJobSchedulerJob(jobId, jobSchedulersKey)
12
+ if jobSchedulersKey then
13
+ local jobSchedulerId = jobId:match("repeat:(.*):%d+")
14
+ if jobSchedulerId then
15
+ return rcall("ZSCORE", jobSchedulersKey, jobSchedulerId)
16
+ end
20
17
  end
18
+ return false
19
+ end
20
+
21
+ local function cleanSet(
22
+ setKey,
23
+ jobKeyPrefix,
24
+ rangeEnd,
25
+ timestamp,
26
+ limit,
27
+ attributes,
28
+ isFinished,
29
+ jobSchedulersKey)
30
+ local jobs = getJobsInZset(setKey, rangeEnd, limit)
31
+ local deleted = {}
32
+ local deletedCount = 0
33
+ local jobTS
34
+ for i, job in ipairs(jobs) do
35
+ if limit > 0 and deletedCount >= limit then
36
+ break
37
+ end
21
38
 
22
- local jobKey = jobKeyPrefix .. job
23
- if isFinished then
24
- removeJob(job, true, jobKeyPrefix, true --[[remove debounce key]])
25
- deletedCount = deletedCount + 1
26
- table.insert(deleted, job)
27
- else
28
- -- * finishedOn says when the job was completed, but it isn't set unless the job has actually completed
29
- jobTS = getTimestamp(jobKey, attributes)
30
- if (not jobTS or jobTS <= timestamp) then
31
- removeJob(job, true, jobKeyPrefix, true --[[remove debounce key]])
32
- deletedCount = deletedCount + 1
33
- table.insert(deleted, job)
34
- end
39
+ -- Extract a Job Scheduler Id from jobId ("repeat:job-scheduler-id:millis")
40
+ -- and check if it is in the scheduled jobs
41
+ if not isJobSchedulerJob(job, jobSchedulersKey) then
42
+ local jobKey = jobKeyPrefix .. job
43
+ if isFinished then
44
+ removeJob(job, true, jobKeyPrefix, true --[[remove debounce key]] )
45
+ deletedCount = deletedCount + 1
46
+ table.insert(deleted, job)
47
+ else
48
+ -- * finishedOn says when the job was completed, but it isn't set unless the job has actually completed
49
+ jobTS = getTimestamp(jobKey, attributes)
50
+ if (not jobTS or jobTS <= timestamp) then
51
+ removeJob(job, true, jobKeyPrefix, true --[[remove debounce key]] )
52
+ deletedCount = deletedCount + 1
53
+ table.insert(deleted, job)
54
+ end
55
+ end
56
+ end
35
57
  end
36
- end
37
58
 
38
- if(#deleted > 0) then
39
- for from, to in batches(#deleted, 7000) do
40
- rcall("ZREM", setKey, unpack(deleted, from, to))
59
+ if (#deleted > 0) then
60
+ for from, to in batches(#deleted, 7000) do
61
+ rcall("ZREM", setKey, unpack(deleted, from, to))
62
+ end
41
63
  end
42
- end
43
64
 
44
- return {deleted, deletedCount}
65
+ return {deleted, deletedCount}
45
66
  end
@@ -3,8 +3,20 @@
3
3
  --- @include "getZSetItems"
4
4
  --- @include "removeJobs"
5
5
 
6
- local function removeZSetJobs(keyName, hard, baseKey, max)
6
+ local function removeZSetJobs(keyName, hard, baseKey, max, jobsToIgnore)
7
7
  local jobs = getZSetItems(keyName, max)
8
+
9
+ -- filter out jobs to ignore
10
+ if jobsToIgnore then
11
+ local filteredJobs = {}
12
+ for i = 1, #jobs do
13
+ if not jobsToIgnore[jobs[i]] then
14
+ table.insert(filteredJobs, jobs[i])
15
+ end
16
+ end
17
+ jobs = filteredJobs
18
+ end
19
+
8
20
  local count = removeJobs(jobs, hard, baseKey, max)
9
21
  if(#jobs > 0) then
10
22
  for from, to in batches(#jobs, 7000) do
@@ -44,7 +44,7 @@ local metaKey = KEYS[6]
44
44
  local pausedKey = KEYS[7]
45
45
  local markerKey = KEYS[8]
46
46
  local eventStreamKey = KEYS[9]
47
- local maxStalledJobCount = ARGV[1]
47
+ local maxStalledJobCount = tonumber(ARGV[1])
48
48
  local queueKeyPrefix = ARGV[2]
49
49
  local timestamp = ARGV[3]
50
50
  local maxCheckTime = ARGV[4]
@@ -63,8 +63,6 @@ local failed = {}
63
63
  if (#stalling > 0) then
64
64
  rcall('DEL', stalledKey)
65
65
 
66
- local MAX_STALLED_JOB_COUNT = tonumber(maxStalledJobCount)
67
-
68
66
  -- Remove from active list
69
67
  for i, jobId in ipairs(stalling) do
70
68
  -- Markers in waitlist DEPRECATED in v5: Remove in v6.
@@ -83,7 +81,7 @@ if (#stalling > 0) then
83
81
  -- If this job has been stalled too many times, such as if it crashes the worker, then fail it.
84
82
  local stalledCount =
85
83
  rcall("HINCRBY", jobKey, "stalledCounter", 1)
86
- if (stalledCount > MAX_STALLED_JOB_COUNT) then
84
+ if (stalledCount > maxStalledJobCount) then
87
85
  local jobAttributes = rcall("HMGET", jobKey, "opts", "parent", "deid")
88
86
  local rawOpts = jobAttributes[1]
89
87
  local rawParentData = jobAttributes[2]
@@ -24,7 +24,7 @@ local rcall = redis.call
24
24
  --- @include "includes/removeJobKeys"
25
25
  --- @include "includes/removeParentDependencyKey"
26
26
 
27
- local function removeJob( prefix, jobId, parentKey, removeChildren)
27
+ local function removeJob(prefix, jobId, parentKey, removeChildren)
28
28
  local jobKey = prefix .. jobId;
29
29
 
30
30
  removeParentDependencyKey(jobKey, false, parentKey, nil)
@@ -33,14 +33,14 @@ local function removeJob( prefix, jobId, parentKey, removeChildren)
33
33
  -- Check if this job has children
34
34
  -- If so, we are going to try to remove the children recursively in deep first way because
35
35
  -- if some job is locked we must exit with and error.
36
- --local countProcessed = rcall("HLEN", jobKey .. ":processed")
36
+ -- local countProcessed = rcall("HLEN", jobKey .. ":processed")
37
37
  local processed = rcall("HGETALL", jobKey .. ":processed")
38
38
 
39
39
  if (#processed > 0) then
40
40
  for i = 1, #processed, 2 do
41
41
  local childJobId = getJobIdFromKey(processed[i])
42
42
  local childJobPrefix = getJobKeyPrefix(processed[i], childJobId)
43
- removeJob( childJobPrefix, childJobId, jobKey, removeChildren )
43
+ removeJob(childJobPrefix, childJobId, jobKey, removeChildren)
44
44
  end
45
45
  end
46
46
 
@@ -50,7 +50,7 @@ local function removeJob( prefix, jobId, parentKey, removeChildren)
50
50
  -- We need to get the jobId for this job.
51
51
  local childJobId = getJobIdFromKey(childJobKey)
52
52
  local childJobPrefix = getJobKeyPrefix(childJobKey, childJobId)
53
- removeJob( childJobPrefix, childJobId, jobKey, removeChildren )
53
+ removeJob(childJobPrefix, childJobId, jobKey, removeChildren)
54
54
  end
55
55
  end
56
56
 
@@ -60,7 +60,7 @@ local function removeJob( prefix, jobId, parentKey, removeChildren)
60
60
  for i = 1, #failed, 2 do
61
61
  local childJobId = getJobIdFromKey(failed[i])
62
62
  local childJobPrefix = getJobKeyPrefix(failed[i], childJobId)
63
- removeJob( childJobPrefix, childJobId, jobKey, removeChildren )
63
+ removeJob(childJobPrefix, childJobId, jobKey, removeChildren)
64
64
  end
65
65
  end
66
66
  end
@@ -70,15 +70,23 @@ local function removeJob( prefix, jobId, parentKey, removeChildren)
70
70
  removeDebounceKey(prefix, jobKey)
71
71
  if removeJobKeys(jobKey) > 0 then
72
72
  local maxEvents = getOrSetMaxEvents(KEYS[2])
73
- rcall("XADD", prefix .. "events", "MAXLEN", "~", maxEvents, "*", "event", "removed",
74
- "jobId", jobId, "prev", prev)
73
+ rcall("XADD", prefix .. "events", "MAXLEN", "~", maxEvents, "*", "event", "removed", "jobId", jobId, "prev",
74
+ prev)
75
75
  end
76
76
  end
77
77
 
78
78
  local prefix = KEYS[1]
79
+ local jobId = ARGV[1]
80
+ local shouldRemoveChildren = ARGV[2]
81
+ local jobKey = prefix .. jobId
79
82
 
80
- if not isLocked(prefix, ARGV[1], ARGV[2]) then
81
- removeJob(prefix, ARGV[1], nil, ARGV[2])
83
+ -- Check if the job belongs to a job scheduler and it is in delayed state.
84
+ if rcall("ZSCORE", prefix .. "delayed", jobId) and rcall("HGET", jobKey, "rjk") then
85
+ return -8 -- Return error code as the job is part of a job scheduler and is in delayed state.
86
+ end
87
+
88
+ if not isLocked(prefix, jobId, shouldRemoveChildren) then
89
+ removeJob(prefix, jobId, nil, shouldRemoveChildren)
82
90
  return 1
83
91
  end
84
92
  return 0
@@ -0,0 +1,43 @@
1
+
2
+ --[[
3
+ Removes a repeatable job
4
+ Input:
5
+ KEYS[1] job schedulers key
6
+ KEYS[2] delayed jobs key
7
+ KEYS[3] events key
8
+
9
+ ARGV[1] job scheduler id
10
+ ARGV[2] prefix key
11
+
12
+ Output:
13
+ 0 - OK
14
+ 1 - Missing repeat job
15
+
16
+ Events:
17
+ 'removed'
18
+ ]]
19
+ local rcall = redis.call
20
+
21
+ -- Includes
22
+ --- @include "includes/removeJobKeys"
23
+
24
+ local jobSchedulerId = ARGV[1]
25
+ local prefix = ARGV[2]
26
+
27
+ local millis = rcall("ZSCORE", KEYS[1], jobSchedulerId)
28
+
29
+ if millis then
30
+ -- Delete next programmed job.
31
+ local delayedJobId = "repeat:" .. jobSchedulerId .. ":" .. millis
32
+ if(rcall("ZREM", KEYS[2], delayedJobId) == 1) then
33
+ removeJobKeys(prefix .. delayedJobId)
34
+ rcall("XADD", KEYS[3], "*", "event", "removed", "jobId", delayedJobId, "prev", "delayed")
35
+ end
36
+ end
37
+
38
+ if(rcall("ZREM", KEYS[1], jobSchedulerId) == 1) then
39
+ rcall("DEL", KEYS[1] .. ":" .. jobSchedulerId)
40
+ return 0
41
+ end
42
+
43
+ return 1
@@ -10,5 +10,6 @@ var ErrorCode;
10
10
  ErrorCode[ErrorCode["ParentJobNotExist"] = -5] = "ParentJobNotExist";
11
11
  ErrorCode[ErrorCode["JobLockMismatch"] = -6] = "JobLockMismatch";
12
12
  ErrorCode[ErrorCode["ParentJobCannotBeReplaced"] = -7] = "ParentJobCannotBeReplaced";
13
+ ErrorCode[ErrorCode["JobBelongsToJobScheduler"] = -8] = "JobBelongsToJobScheduler";
13
14
  })(ErrorCode = exports.ErrorCode || (exports.ErrorCode = {}));
14
15
  //# sourceMappingURL=error-code.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"error-code.js","sourceRoot":"","sources":["../../../src/enums/error-code.ts"],"names":[],"mappings":";;;AAAA,IAAY,SAQX;AARD,WAAY,SAAS;IACnB,wDAAgB,CAAA;IAChB,gEAAoB,CAAA;IACpB,4DAAkB,CAAA;IAClB,8EAA2B,CAAA;IAC3B,oEAAsB,CAAA;IACtB,gEAAoB,CAAA;IACpB,oFAA8B,CAAA;AAChC,CAAC,EARW,SAAS,GAAT,iBAAS,KAAT,iBAAS,QAQpB"}
1
+ {"version":3,"file":"error-code.js","sourceRoot":"","sources":["../../../src/enums/error-code.ts"],"names":[],"mappings":";;;AAAA,IAAY,SASX;AATD,WAAY,SAAS;IACnB,wDAAgB,CAAA;IAChB,gEAAoB,CAAA;IACpB,4DAAkB,CAAA;IAClB,8EAA2B,CAAA;IAC3B,oEAAsB,CAAA;IACtB,gEAAoB,CAAA;IACpB,oFAA8B,CAAA;IAC9B,kFAA6B,CAAA;AAC/B,CAAC,EATW,SAAS,GAAT,iBAAS,KAAT,iBAAS,QASpB"}
@@ -0,0 +1,223 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addJobScheduler = void 0;
4
+ const content = `--[[
5
+ Adds a job scheduler, i.e. a job factory that creates jobs based on a given schedule (repeat options).
6
+ Input:
7
+ KEYS[1] 'repeat' key
8
+ KEYS[2] 'delayed' key
9
+ ARGV[1] next milliseconds
10
+ ARGV[2] msgpacked options
11
+ [1] name
12
+ [2] tz?
13
+ [3] patten?
14
+ [4] endDate?
15
+ [5] every?
16
+ ARGV[3] jobs scheduler id
17
+ ARGV[4] prefix key
18
+ Output:
19
+ repeatableKey - OK
20
+ ]]
21
+ local rcall = redis.call
22
+ local repeatKey = KEYS[1]
23
+ local delayedKey = KEYS[2]
24
+ local nextMillis = ARGV[1]
25
+ local jobSchedulerId = ARGV[3]
26
+ local prefixKey = ARGV[4]
27
+ -- Includes
28
+ --[[
29
+ Function to remove job.
30
+ ]]
31
+ -- Includes
32
+ --[[
33
+ Function to remove debounce key.
34
+ ]]
35
+ local function removeDebounceKey(prefixKey, jobKey)
36
+ local debounceId = rcall("HGET", jobKey, "deid")
37
+ if debounceId then
38
+ local debounceKey = prefixKey .. "de:" .. debounceId
39
+ rcall("DEL", debounceKey)
40
+ end
41
+ end
42
+ --[[
43
+ Function to remove job keys.
44
+ ]]
45
+ local function removeJobKeys(jobKey)
46
+ return rcall("DEL", jobKey, jobKey .. ':logs',
47
+ jobKey .. ':dependencies', jobKey .. ':processed', jobKey .. ':failed')
48
+ end
49
+ --[[
50
+ Check if this job has a parent. If so we will just remove it from
51
+ the parent child list, but if it is the last child we should move the parent to "wait/paused"
52
+ which requires code from "moveToFinished"
53
+ ]]
54
+ -- Includes
55
+ --[[
56
+ Function to add job in target list and add marker if needed.
57
+ ]]
58
+ -- Includes
59
+ --[[
60
+ Add marker if needed when a job is available.
61
+ ]]
62
+ local function addBaseMarkerIfNeeded(markerKey, isPausedOrMaxed)
63
+ if not isPausedOrMaxed then
64
+ rcall("ZADD", markerKey, 0, "0")
65
+ end
66
+ end
67
+ local function addJobInTargetList(targetKey, markerKey, pushCmd, isPausedOrMaxed, jobId)
68
+ rcall(pushCmd, targetKey, jobId)
69
+ addBaseMarkerIfNeeded(markerKey, isPausedOrMaxed)
70
+ end
71
+ --[[
72
+ Functions to destructure job key.
73
+ Just a bit of warning, these functions may be a bit slow and affect performance significantly.
74
+ ]]
75
+ local getJobIdFromKey = function (jobKey)
76
+ return string.match(jobKey, ".*:(.*)")
77
+ end
78
+ local getJobKeyPrefix = function (jobKey, jobId)
79
+ return string.sub(jobKey, 0, #jobKey - #jobId)
80
+ end
81
+ --[[
82
+ Function to check for the meta.paused key to decide if we are paused or not
83
+ (since an empty list and !EXISTS are not really the same).
84
+ ]]
85
+ local function getTargetQueueList(queueMetaKey, activeKey, waitKey, pausedKey)
86
+ local queueAttributes = rcall("HMGET", queueMetaKey, "paused", "concurrency")
87
+ if queueAttributes[1] then
88
+ return pausedKey, true
89
+ else
90
+ if queueAttributes[2] then
91
+ local activeCount = rcall("LLEN", activeKey)
92
+ if activeCount >= tonumber(queueAttributes[2]) then
93
+ return waitKey, true
94
+ else
95
+ return waitKey, false
96
+ end
97
+ end
98
+ end
99
+ return waitKey, false
100
+ end
101
+ local function moveParentToWait(parentPrefix, parentId, emitEvent)
102
+ local parentTarget, isPausedOrMaxed = getTargetQueueList(parentPrefix .. "meta", parentPrefix .. "active",
103
+ parentPrefix .. "wait", parentPrefix .. "paused")
104
+ addJobInTargetList(parentTarget, parentPrefix .. "marker", "RPUSH", isPausedOrMaxed, parentId)
105
+ if emitEvent then
106
+ local parentEventStream = parentPrefix .. "events"
107
+ rcall("XADD", parentEventStream, "*", "event", "waiting", "jobId", parentId, "prev", "waiting-children")
108
+ end
109
+ end
110
+ local function removeParentDependencyKey(jobKey, hard, parentKey, baseKey, debounceId)
111
+ if parentKey then
112
+ local parentDependenciesKey = parentKey .. ":dependencies"
113
+ local result = rcall("SREM", parentDependenciesKey, jobKey)
114
+ if result > 0 then
115
+ local pendingDependencies = rcall("SCARD", parentDependenciesKey)
116
+ if pendingDependencies == 0 then
117
+ local parentId = getJobIdFromKey(parentKey)
118
+ local parentPrefix = getJobKeyPrefix(parentKey, parentId)
119
+ local numRemovedElements = rcall("ZREM", parentPrefix .. "waiting-children", parentId)
120
+ if numRemovedElements == 1 then
121
+ if hard then -- remove parent in same queue
122
+ if parentPrefix == baseKey then
123
+ removeParentDependencyKey(parentKey, hard, nil, baseKey, nil)
124
+ removeJobKeys(parentKey)
125
+ if debounceId then
126
+ rcall("DEL", parentPrefix .. "de:" .. debounceId)
127
+ end
128
+ else
129
+ moveParentToWait(parentPrefix, parentId)
130
+ end
131
+ else
132
+ moveParentToWait(parentPrefix, parentId, true)
133
+ end
134
+ end
135
+ end
136
+ return true
137
+ end
138
+ else
139
+ local parentAttributes = rcall("HMGET", jobKey, "parentKey", "deid")
140
+ local missedParentKey = parentAttributes[1]
141
+ if( (type(missedParentKey) == "string") and missedParentKey ~= ""
142
+ and (rcall("EXISTS", missedParentKey) == 1)) then
143
+ local parentDependenciesKey = missedParentKey .. ":dependencies"
144
+ local result = rcall("SREM", parentDependenciesKey, jobKey)
145
+ if result > 0 then
146
+ local pendingDependencies = rcall("SCARD", parentDependenciesKey)
147
+ if pendingDependencies == 0 then
148
+ local parentId = getJobIdFromKey(missedParentKey)
149
+ local parentPrefix = getJobKeyPrefix(missedParentKey, parentId)
150
+ local numRemovedElements = rcall("ZREM", parentPrefix .. "waiting-children", parentId)
151
+ if numRemovedElements == 1 then
152
+ if hard then
153
+ if parentPrefix == baseKey then
154
+ removeParentDependencyKey(missedParentKey, hard, nil, baseKey, nil)
155
+ removeJobKeys(missedParentKey)
156
+ if parentAttributes[2] then
157
+ rcall("DEL", parentPrefix .. "de:" .. parentAttributes[2])
158
+ end
159
+ else
160
+ moveParentToWait(parentPrefix, parentId)
161
+ end
162
+ else
163
+ moveParentToWait(parentPrefix, parentId, true)
164
+ end
165
+ end
166
+ end
167
+ return true
168
+ end
169
+ end
170
+ end
171
+ return false
172
+ end
173
+ local function removeJob(jobId, hard, baseKey, shouldRemoveDebounceKey)
174
+ local jobKey = baseKey .. jobId
175
+ removeParentDependencyKey(jobKey, hard, nil, baseKey)
176
+ if shouldRemoveDebounceKey then
177
+ removeDebounceKey(baseKey, jobKey)
178
+ end
179
+ removeJobKeys(jobKey)
180
+ end
181
+ local function storeRepeatableJob(repeatKey, nextMillis, rawOpts)
182
+ rcall("ZADD", repeatKey, nextMillis, jobSchedulerId)
183
+ local opts = cmsgpack.unpack(rawOpts)
184
+ local optionalValues = {}
185
+ if opts['tz'] then
186
+ table.insert(optionalValues, "tz")
187
+ table.insert(optionalValues, opts['tz'])
188
+ end
189
+ if opts['pattern'] then
190
+ table.insert(optionalValues, "pattern")
191
+ table.insert(optionalValues, opts['pattern'])
192
+ end
193
+ if opts['endDate'] then
194
+ table.insert(optionalValues, "endDate")
195
+ table.insert(optionalValues, opts['endDate'])
196
+ end
197
+ if opts['every'] then
198
+ table.insert(optionalValues, "every")
199
+ table.insert(optionalValues, opts['every'])
200
+ end
201
+ rcall("HMSET", repeatKey .. ":" .. jobSchedulerId, "name", opts['name'],
202
+ unpack(optionalValues))
203
+ end
204
+ -- If we are overriding a repeatable job we must delete the delayed job for
205
+ -- the next iteration.
206
+ local prevMillis = rcall("ZSCORE", repeatKey, jobSchedulerId)
207
+ if prevMillis ~= false then
208
+ local delayedJobId = "repeat:" .. jobSchedulerId .. ":" .. prevMillis
209
+ local nextDelayedJobId = repeatKey .. ":" .. jobSchedulerId .. ":" .. nextMillis
210
+ if rcall("ZSCORE", delayedKey, delayedJobId) ~= false
211
+ and rcall("EXISTS", nextDelayedJobId) ~= 1 then
212
+ removeJob(delayedJobId, true, prefixKey, true --[[remove debounce key]])
213
+ rcall("ZREM", delayedKey, delayedJobId)
214
+ end
215
+ end
216
+ return storeRepeatableJob(repeatKey, nextMillis, ARGV[2])
217
+ `;
218
+ exports.addJobScheduler = {
219
+ name: 'addJobScheduler',
220
+ content,
221
+ keys: 2,
222
+ };
223
+ //# sourceMappingURL=addJobScheduler-2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"addJobScheduler-2.js","sourceRoot":"","sources":["../../../src/scripts/addJobScheduler-2.ts"],"names":[],"mappings":";;;AAAA,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqNf,CAAC;AACW,QAAA,eAAe,GAAG;IAC7B,IAAI,EAAE,iBAAiB;IACvB,OAAO;IACP,IAAI,EAAE,CAAC;CACR,CAAC"}
@@ -6,6 +6,7 @@ const content = `--[[
6
6
  Input:
7
7
  KEYS[1] set key,
8
8
  KEYS[2] events stream key
9
+ KEYS[3] job schedulers key
9
10
  ARGV[1] jobKey prefix
10
11
  ARGV[2] timestamp
11
12
  ARGV[3] limit the number of jobs to be removed. 0 is unlimited
@@ -237,7 +238,7 @@ end
237
238
  --[[
238
239
  Function to clean job set.
239
240
  Returns jobIds and deleted count number.
240
- ]]
241
+ ]]
241
242
  -- Includes
242
243
  --[[
243
244
  Function to loop in batches.
@@ -268,54 +269,75 @@ local function getJobsInZset(zsetKey, rangeEnd, limit)
268
269
  return rcall("ZRANGEBYSCORE", zsetKey, 0, rangeEnd)
269
270
  end
270
271
  end
271
- local function cleanSet(setKey, jobKeyPrefix, rangeEnd, timestamp, limit, attributes, isFinished)
272
- local jobs = getJobsInZset(setKey, rangeEnd, limit)
273
- local deleted = {}
274
- local deletedCount = 0
275
- local jobTS
276
- for i, job in ipairs(jobs) do
277
- if limit > 0 and deletedCount >= limit then
278
- break
272
+ local function isJobSchedulerJob(jobId, jobSchedulersKey)
273
+ if jobSchedulersKey then
274
+ local jobSchedulerId = jobId:match("repeat:(.*):%d+")
275
+ if jobSchedulerId then
276
+ return rcall("ZSCORE", jobSchedulersKey, jobSchedulerId)
277
+ end
279
278
  end
280
- local jobKey = jobKeyPrefix .. job
281
- if isFinished then
282
- removeJob(job, true, jobKeyPrefix, true --[[remove debounce key]])
283
- deletedCount = deletedCount + 1
284
- table.insert(deleted, job)
285
- else
286
- -- * finishedOn says when the job was completed, but it isn't set unless the job has actually completed
287
- jobTS = getTimestamp(jobKey, attributes)
288
- if (not jobTS or jobTS <= timestamp) then
289
- removeJob(job, true, jobKeyPrefix, true --[[remove debounce key]])
290
- deletedCount = deletedCount + 1
291
- table.insert(deleted, job)
292
- end
279
+ return false
280
+ end
281
+ local function cleanSet(
282
+ setKey,
283
+ jobKeyPrefix,
284
+ rangeEnd,
285
+ timestamp,
286
+ limit,
287
+ attributes,
288
+ isFinished,
289
+ jobSchedulersKey)
290
+ local jobs = getJobsInZset(setKey, rangeEnd, limit)
291
+ local deleted = {}
292
+ local deletedCount = 0
293
+ local jobTS
294
+ for i, job in ipairs(jobs) do
295
+ if limit > 0 and deletedCount >= limit then
296
+ break
297
+ end
298
+ -- Extract a Job Scheduler Id from jobId ("repeat:job-scheduler-id:millis")
299
+ -- and check if it is in the scheduled jobs
300
+ if not isJobSchedulerJob(job, jobSchedulersKey) then
301
+ local jobKey = jobKeyPrefix .. job
302
+ if isFinished then
303
+ removeJob(job, true, jobKeyPrefix, true --[[remove debounce key]] )
304
+ deletedCount = deletedCount + 1
305
+ table.insert(deleted, job)
306
+ else
307
+ -- * finishedOn says when the job was completed, but it isn't set unless the job has actually completed
308
+ jobTS = getTimestamp(jobKey, attributes)
309
+ if (not jobTS or jobTS <= timestamp) then
310
+ removeJob(job, true, jobKeyPrefix, true --[[remove debounce key]] )
311
+ deletedCount = deletedCount + 1
312
+ table.insert(deleted, job)
313
+ end
314
+ end
315
+ end
293
316
  end
294
- end
295
- if(#deleted > 0) then
296
- for from, to in batches(#deleted, 7000) do
297
- rcall("ZREM", setKey, unpack(deleted, from, to))
317
+ if (#deleted > 0) then
318
+ for from, to in batches(#deleted, 7000) do
319
+ rcall("ZREM", setKey, unpack(deleted, from, to))
320
+ end
298
321
  end
299
- end
300
- return {deleted, deletedCount}
322
+ return {deleted, deletedCount}
301
323
  end
302
324
  local result
303
325
  if ARGV[4] == "active" then
304
- result = cleanList(KEYS[1], ARGV[1], rangeStart, rangeEnd, ARGV[2], false)
326
+ result = cleanList(KEYS[1], ARGV[1], rangeStart, rangeEnd, ARGV[2], false --[[ hasFinished ]])
305
327
  elseif ARGV[4] == "delayed" then
306
328
  rangeEnd = "+inf"
307
329
  result = cleanSet(KEYS[1], ARGV[1], rangeEnd, ARGV[2], limit,
308
- {"processedOn", "timestamp"}, false)
330
+ {"processedOn", "timestamp"}, false --[[ hasFinished ]], KEYS[3])
309
331
  elseif ARGV[4] == "prioritized" then
310
332
  rangeEnd = "+inf"
311
333
  result = cleanSet(KEYS[1], ARGV[1], rangeEnd, ARGV[2], limit,
312
- {"timestamp"}, false)
334
+ {"timestamp"}, false --[[ hasFinished ]])
313
335
  elseif ARGV[4] == "wait" or ARGV[4] == "paused" then
314
- result = cleanList(KEYS[1], ARGV[1], rangeStart, rangeEnd, ARGV[2], true)
336
+ result = cleanList(KEYS[1], ARGV[1], rangeStart, rangeEnd, ARGV[2], true --[[ hasFinished ]])
315
337
  else
316
338
  rangeEnd = ARGV[2]
317
339
  result = cleanSet(KEYS[1], ARGV[1], rangeEnd, ARGV[2], limit,
318
- {"finishedOn"}, true)
340
+ {"finishedOn"}, true --[[ hasFinished ]])
319
341
  end
320
342
  rcall("XADD", KEYS[2], "*", "event", "cleaned", "count", result[2])
321
343
  return result[1]
@@ -323,6 +345,6 @@ return result[1]
323
345
  exports.cleanJobsInSet = {
324
346
  name: 'cleanJobsInSet',
325
347
  content,
326
- keys: 2,
348
+ keys: 3,
327
349
  };
328
- //# sourceMappingURL=cleanJobsInSet-2.js.map
350
+ //# sourceMappingURL=cleanJobsInSet-3.js.map