const content = `--[[ Adds a repeatable job Input: KEYS[1] 'repeat' key KEYS[2] 'delayed' key ARGV[1] next milliseconds ARGV[2] msgpacked options [1] name [2] tz? [3] pattern? [4] endDate? [5] every? ARGV[3] legacy custom key TODO: remove this logic in next breaking change ARGV[4] custom key ARGV[5] prefix key Output: repeatableKey - OK ]] local rcall = redis.call local repeatKey = KEYS[1] local delayedKey = KEYS[2] local nextMillis = ARGV[1] local legacyCustomKey = ARGV[3] local customKey = ARGV[4] local prefixKey = ARGV[5] -- Includes --[[ Function to remove job. ]] -- Includes --[[ Function to remove deduplication key if needed when a job is being removed. ]] local function removeDeduplicationKeyIfNeededOnRemoval(prefixKey, jobId, deduplicationId) if deduplicationId then local deduplicationKey = prefixKey .. "de:" .. deduplicationId local currentJobId = rcall('GET', deduplicationKey) if currentJobId and currentJobId == jobId then return rcall("DEL", deduplicationKey) end end end --[[ Function to remove job keys. ]] local function removeJobKeys(jobKey) return rcall("DEL", jobKey, jobKey .. ':logs', jobKey .. ':dependencies', jobKey .. ':processed', jobKey .. ':failed', jobKey .. ':unsuccessful') end --[[ Check if this job has a parent. If so we will just remove it from the parent child list, but if it is the last child we should move the parent to "wait/paused" which requires code from "moveToFinished" ]] -- Includes --[[ Function to add job in target list and add marker if needed. ]] -- Includes --[[ Add marker if needed when a job is available. ]] local function addBaseMarkerIfNeeded(markerKey, isPausedOrMaxed) if not isPausedOrMaxed then rcall("ZADD", markerKey, 0, "0") end end local function addJobInTargetList(targetKey, markerKey, pushCmd, isPausedOrMaxed, jobId) rcall(pushCmd, targetKey, jobId) addBaseMarkerIfNeeded(markerKey, isPausedOrMaxed) end --[[ Functions to destructure job key. Just a bit of warning, these functions may be a bit slow and affect performance significantly. ]] local getJobIdFromKey = function (jobKey) return string.match(jobKey, ".*:(.*)") end local getJobKeyPrefix = function (jobKey, jobId) return string.sub(jobKey, 0, #jobKey - #jobId) end --[[ Function to check for the meta.paused key to decide if we are paused or not (since an empty list and !EXISTS are not really the same). ]] local function getTargetQueueList(queueMetaKey, activeKey, waitKey, pausedKey) local queueAttributes = rcall("HMGET", queueMetaKey, "paused", "concurrency", "max", "duration") if queueAttributes[1] then return pausedKey, true, queueAttributes[3], queueAttributes[4] else if queueAttributes[2] then local activeCount = rcall("LLEN", activeKey) if activeCount >= tonumber(queueAttributes[2]) then return waitKey, true, queueAttributes[3], queueAttributes[4] else return waitKey, false, queueAttributes[3], queueAttributes[4] end end end return waitKey, false, queueAttributes[3], queueAttributes[4] end local function _moveParentToWait(parentPrefix, parentId, emitEvent) local parentTarget, isPausedOrMaxed = getTargetQueueList(parentPrefix .. "meta", parentPrefix .. "active", parentPrefix .. "wait", parentPrefix .. "paused") addJobInTargetList(parentTarget, parentPrefix .. "marker", "RPUSH", isPausedOrMaxed, parentId) if emitEvent then local parentEventStream = parentPrefix .. "events" rcall("XADD", parentEventStream, "*", "event", "waiting", "jobId", parentId, "prev", "waiting-children") end end local function removeParentDependencyKey(jobKey, hard, parentKey, baseKey, debounceId) if parentKey then local parentDependenciesKey = parentKey .. ":dependencies" local result = rcall("SREM", parentDependenciesKey, jobKey) if result > 0 then local pendingDependencies = rcall("SCARD", parentDependenciesKey) if pendingDependencies == 0 then local parentId = getJobIdFromKey(parentKey) local parentPrefix = getJobKeyPrefix(parentKey, parentId) local numRemovedElements = rcall("ZREM", parentPrefix .. "waiting-children", parentId) if numRemovedElements == 1 then if hard then -- remove parent in same queue if parentPrefix == baseKey then removeParentDependencyKey(parentKey, hard, nil, baseKey, nil) removeJobKeys(parentKey) if debounceId then rcall("DEL", parentPrefix .. "de:" .. debounceId) end else _moveParentToWait(parentPrefix, parentId) end else _moveParentToWait(parentPrefix, parentId, true) end end end return true end else local parentAttributes = rcall("HMGET", jobKey, "parentKey", "deid") local missedParentKey = parentAttributes[1] if( (type(missedParentKey) == "string") and missedParentKey ~= "" and (rcall("EXISTS", missedParentKey) == 1)) then local parentDependenciesKey = missedParentKey .. ":dependencies" local result = rcall("SREM", parentDependenciesKey, jobKey) if result > 0 then local pendingDependencies = rcall("SCARD", parentDependenciesKey) if pendingDependencies == 0 then local parentId = getJobIdFromKey(missedParentKey) local parentPrefix = getJobKeyPrefix(missedParentKey, parentId) local numRemovedElements = rcall("ZREM", parentPrefix .. "waiting-children", parentId) if numRemovedElements == 1 then if hard then if parentPrefix == baseKey then removeParentDependencyKey(missedParentKey, hard, nil, baseKey, nil) removeJobKeys(missedParentKey) if parentAttributes[2] then rcall("DEL", parentPrefix .. "de:" .. parentAttributes[2]) end else _moveParentToWait(parentPrefix, parentId) end else _moveParentToWait(parentPrefix, parentId, true) end end end return true end end end return false end local function removeJob(jobId, hard, baseKey, shouldRemoveDeduplicationKey) local jobKey = baseKey .. jobId removeParentDependencyKey(jobKey, hard, nil, baseKey) if shouldRemoveDeduplicationKey then local deduplicationId = rcall("HGET", jobKey, "deid") removeDeduplicationKeyIfNeededOnRemoval(baseKey, jobId, deduplicationId) end removeJobKeys(jobKey) end local function storeRepeatableJob(repeatKey, customKey, nextMillis, rawOpts) rcall("ZADD", repeatKey, nextMillis, customKey) local opts = cmsgpack.unpack(rawOpts) local optionalValues = {} if opts['tz'] then table.insert(optionalValues, "tz") table.insert(optionalValues, opts['tz']) end if opts['pattern'] then table.insert(optionalValues, "pattern") table.insert(optionalValues, opts['pattern']) end if opts['endDate'] then table.insert(optionalValues, "endDate") table.insert(optionalValues, opts['endDate']) end if opts['every'] then table.insert(optionalValues, "every") table.insert(optionalValues, opts['every']) end rcall("HMSET", repeatKey .. ":" .. customKey, "name", opts['name'], unpack(optionalValues)) return customKey end -- If we are overriding a repeatable job we must delete the delayed job for -- the next iteration. local prevMillis = rcall("ZSCORE", repeatKey, customKey) if prevMillis then local delayedJobId = "repeat:" .. customKey .. ":" .. prevMillis local nextDelayedJobId = repeatKey .. ":" .. customKey .. ":" .. nextMillis if rcall("ZSCORE", delayedKey, delayedJobId) and rcall("EXISTS", nextDelayedJobId) ~= 1 then removeJob(delayedJobId, true, prefixKey, true --[[remove debounce key]]) rcall("ZREM", delayedKey, delayedJobId) end end -- Keep backwards compatibility with old repeatable jobs (<= 3.0.0) if rcall("ZSCORE", repeatKey, legacyCustomKey) ~= false then return storeRepeatableJob(repeatKey, legacyCustomKey, nextMillis, ARGV[2]) end return storeRepeatableJob(repeatKey, customKey, nextMillis, ARGV[2]) `; export const addRepeatableJob = { name: 'addRepeatableJob', content, keys: 2, }; //# sourceMappingURL=addRepeatableJob-2.js.map