152 lines
4.4 KiB
Lua
152 lines
4.4 KiB
Lua
--[[
|
|
Get jobs (id + data) for the provided states.
|
|
|
|
Job ids and their hashes are read in the same script so that ids whose hash
|
|
disappears after the id is read (but before the job is loaded) do not appear
|
|
in the result set. Ids without a job hash (for example the deprecated wait
|
|
list marker entry stored in the list) are skipped. For bounded ranges the
|
|
script iterates forward using the range offset as a cursor to backfill
|
|
skipped ids, preserving the requested page size when possible.
|
|
|
|
Input:
|
|
KEYS[1] 'prefix'
|
|
|
|
ARGV[1] start
|
|
ARGV[2] end
|
|
ARGV[3] asc ('1' | '0')
|
|
ARGV[4] max iterations (backfill bound)
|
|
ARGV[5...] types
|
|
|
|
Output:
|
|
results grouped per requested type; each entry is a
|
|
{jobId, {field, value, ...}} tuple
|
|
]]
|
|
local rcall = redis.call
|
|
local prefix = KEYS[1]
|
|
local rangeStart = tonumber(ARGV[1])
|
|
local rangeEnd = tonumber(ARGV[2])
|
|
local asc = ARGV[3] == "1"
|
|
local max_iterations = tonumber(ARGV[4])
|
|
local results = {}
|
|
|
|
local function isListType(stateType)
|
|
return stateType == "wait" or stateType == "paused" or stateType == "active"
|
|
end
|
|
|
|
-- Fetch a slice of ids for the given state respecting the requested order.
|
|
local function fetchIds(stateKey, stateType, sliceStart, sliceEnd, listLength)
|
|
if isListType(stateType) then
|
|
if asc then
|
|
local modifiedRangeStart
|
|
local modifiedRangeEnd
|
|
if sliceStart == -1 then
|
|
modifiedRangeStart = 0
|
|
else
|
|
modifiedRangeStart = -(sliceStart + 1)
|
|
end
|
|
|
|
if sliceEnd == -1 then
|
|
modifiedRangeEnd = 0
|
|
else
|
|
modifiedRangeEnd = -(sliceEnd + 1)
|
|
end
|
|
|
|
-- Ascending list slices use negative indexes. When the whole window is
|
|
-- beyond the list length Redis clamps both indexes to 0 and LRANGE would
|
|
-- return the head element, so guard against out-of-range slices with LLEN.
|
|
if listLength ~= nil and sliceStart >= 0 and sliceEnd >= 0 and sliceStart >= listLength then
|
|
return {}
|
|
end
|
|
|
|
local ids = rcall("LRANGE", stateKey, modifiedRangeEnd, modifiedRangeStart)
|
|
local reversed = {}
|
|
for i = #ids, 1, -1 do
|
|
reversed[#reversed + 1] = ids[i]
|
|
end
|
|
return reversed
|
|
else
|
|
return rcall("LRANGE", stateKey, sliceStart, sliceEnd)
|
|
end
|
|
else
|
|
if asc then
|
|
return rcall("ZRANGE", stateKey, sliceStart, sliceEnd)
|
|
else
|
|
return rcall("ZREVRANGE", stateKey, sliceStart, sliceEnd)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Fetch the job hash for an id and append it when present.
|
|
local function appendJob(entries, jobId)
|
|
local jobData = rcall("HGETALL", prefix .. jobId)
|
|
if #jobData > 0 then
|
|
entries[#entries + 1] = {jobId, jobData}
|
|
end
|
|
end
|
|
|
|
local function cleanupDeprecatedMarker(stateKey, stateType)
|
|
if stateType == "wait" or stateType == "paused" then
|
|
local marker = rcall("LINDEX", stateKey, -1)
|
|
if marker and string.sub(marker, 1, 2) == "0:" then
|
|
local count = rcall("LLEN", stateKey)
|
|
if count > 1 then
|
|
rcall("RPOP", stateKey)
|
|
return count - 1
|
|
end
|
|
return 0
|
|
end
|
|
end
|
|
end
|
|
|
|
local function collectJobs(stateKey, stateType)
|
|
local entries = {}
|
|
local listLength
|
|
local cleanedListLength = cleanupDeprecatedMarker(stateKey, stateType)
|
|
|
|
if asc and isListType(stateType) and rangeStart >= 0 and rangeEnd >= 0 then
|
|
if cleanedListLength ~= nil then
|
|
listLength = cleanedListLength
|
|
else
|
|
listLength = rcall("LLEN", stateKey)
|
|
end
|
|
end
|
|
|
|
-- Unbounded or negative ranges: fetch the exact window and skip missing ids.
|
|
if rangeStart < 0 or rangeEnd < 0 then
|
|
local ids = fetchIds(stateKey, stateType, rangeStart, rangeEnd, listLength)
|
|
for i = 1, #ids do
|
|
appendJob(entries, ids[i])
|
|
end
|
|
return entries
|
|
end
|
|
|
|
-- Bounded range: iterate forward to backfill skipped ids.
|
|
local needed = rangeEnd - rangeStart + 1
|
|
local cursor = rangeStart
|
|
local iterations = 0
|
|
while #entries < needed and iterations < max_iterations do
|
|
local ids = fetchIds(stateKey, stateType, cursor, cursor + needed - 1, listLength)
|
|
if #ids == 0 then
|
|
break
|
|
end
|
|
for i = 1, #ids do
|
|
if #entries >= needed then
|
|
break
|
|
end
|
|
appendJob(entries, ids[i])
|
|
end
|
|
cursor = cursor + #ids
|
|
iterations = iterations + 1
|
|
end
|
|
|
|
return entries
|
|
end
|
|
|
|
for i = 5, #ARGV do
|
|
local stateType = ARGV[i]
|
|
local stateKey = prefix .. stateType
|
|
results[#results + 1] = collectJobs(stateKey, stateType)
|
|
end
|
|
|
|
return results
|