glide-mq 0.4.1 → 0.4.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.
@@ -1 +1 @@
1
- {"version":3,"file":"flow-producer.d.ts","sourceRoot":"","sources":["../src/flow-producer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,OAAO,EAAU,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAM5B,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,GAAG,CAAC;IACT,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;CACtB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,IAAI,CAAsB;IAClC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,OAAO,CAAS;gBAEZ,IAAI,EAAE,mBAAmB;IAIrC,gBAAgB;YACF,SAAS;IAYvB;;;;OAIG;IACG,GAAG,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAe1C;;OAEG;IACG,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IASnD;;;;OAIG;YACW,gBAAgB;IAyI9B;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ7B"}
1
+ {"version":3,"file":"flow-producer.d.ts","sourceRoot":"","sources":["../src/flow-producer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,OAAO,EAAU,MAAM,SAAS,CAAC;AACpE,OAAO,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC;AAM5B,MAAM,WAAW,OAAO;IACtB,GAAG,EAAE,GAAG,CAAC;IACT,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;CACtB;AAED,qBAAa,YAAY;IACvB,OAAO,CAAC,IAAI,CAAsB;IAClC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,OAAO,CAAS;gBAEZ,IAAI,EAAE,mBAAmB;IAIrC,gBAAgB;YACF,SAAS;IAYvB;;;;OAIG;IACG,GAAG,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAe1C;;OAEG;IACG,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IASnD;;;;OAIG;YACW,gBAAgB;IA0I9B;;;OAGG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAQ7B"}
@@ -94,7 +94,7 @@ class FlowProducer {
94
94
  const { addJob } = await Promise.resolve().then(() => __importStar(require('./functions/index')));
95
95
  const timestamp = Date.now();
96
96
  const opts = flow.opts ?? {};
97
- const jobId = await addJob(client, parentKeys, flow.name, JSON.stringify(flow.data), JSON.stringify(opts), timestamp, opts.delay ?? 0, opts.priority ?? 0, '', opts.attempts ?? 0);
97
+ const jobId = await addJob(client, parentKeys, flow.name, JSON.stringify(flow.data), JSON.stringify(opts), timestamp, opts.delay ?? 0, opts.priority ?? 0, '', opts.attempts ?? 0, opts.ordering?.key ?? '');
98
98
  const job = new job_1.Job(client, parentKeys, String(jobId), flow.name, flow.data, opts);
99
99
  job.timestamp = timestamp;
100
100
  return { job };
@@ -1 +1 @@
1
- {"version":3,"file":"flow-producer.js","sourceRoot":"","sources":["../src/flow-producer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"flow-producer.js","sourceRoot":"","sources":["../src/flow-producer.ts"],"names":[],"mappings":"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"}
@@ -1,20 +1,20 @@
1
1
  import type { Client } from '../types';
2
2
  import type { GlideReturnType } from '@glidemq/speedkey';
3
3
  export declare const LIBRARY_NAME = "glidemq";
4
- export declare const LIBRARY_VERSION = "9";
4
+ export declare const LIBRARY_VERSION = "15";
5
5
  export declare const CONSUMER_GROUP = "workers";
6
- export declare const LIBRARY_SOURCE = "#!lua name=glidemq\n\nlocal PRIORITY_SHIFT = 4398046511104\n\nlocal function emitEvent(eventsKey, eventType, jobId, extraFields)\n local fields = {'event', eventType, 'jobId', tostring(jobId)}\n if extraFields then\n for i = 1, #extraFields, 2 do\n fields[#fields + 1] = extraFields[i]\n fields[#fields + 1] = extraFields[i + 1]\n end\n end\n redis.call('XADD', eventsKey, 'MAXLEN', '~', '1000', '*', unpack(fields))\nend\n\nredis.register_function('glidemq_version', function(keys, args)\n return '9'\nend)\n\nredis.register_function('glidemq_addJob', function(keys, args)\n local idKey = keys[1]\n local streamKey = keys[2]\n local scheduledKey = keys[3]\n local eventsKey = keys[4]\n local jobName = args[1]\n local jobData = args[2]\n local jobOpts = args[3]\n local timestamp = tonumber(args[4])\n local delay = tonumber(args[5]) or 0\n local priority = tonumber(args[6]) or 0\n local parentId = args[7] or ''\n local maxAttempts = tonumber(args[8]) or 0\n local jobId = redis.call('INCR', idKey)\n local jobIdStr = tostring(jobId)\n local prefix = string.sub(idKey, 1, #idKey - 2)\n local jobKey = prefix .. 'job:' .. jobIdStr\n local hashFields = {\n 'id', jobIdStr,\n 'name', jobName,\n 'data', jobData,\n 'opts', jobOpts,\n 'timestamp', tostring(timestamp),\n 'attemptsMade', '0',\n 'delay', tostring(delay),\n 'priority', tostring(priority),\n 'maxAttempts', tostring(maxAttempts)\n }\n if parentId ~= '' then\n hashFields[#hashFields + 1] = 'parentId'\n hashFields[#hashFields + 1] = parentId\n end\n if delay > 0 or priority > 0 then\n hashFields[#hashFields + 1] = 'state'\n hashFields[#hashFields + 1] = delay > 0 and 'delayed' or 'prioritized'\n else\n hashFields[#hashFields + 1] = 'state'\n hashFields[#hashFields + 1] = 'waiting'\n end\n redis.call('HSET', jobKey, unpack(hashFields))\n if delay > 0 then\n local score = priority * PRIORITY_SHIFT + (timestamp + delay)\n redis.call('ZADD', scheduledKey, score, jobIdStr)\n elseif priority > 0 then\n local score = priority * PRIORITY_SHIFT\n redis.call('ZADD', scheduledKey, score, jobIdStr)\n else\n redis.call('XADD', streamKey, '*', 'jobId', jobIdStr)\n end\n emitEvent(eventsKey, 'added', jobIdStr, {'name', jobName})\n return jobIdStr\nend)\n\nredis.register_function('glidemq_promote', function(keys, args)\n local scheduledKey = keys[1]\n local streamKey = keys[2]\n local eventsKey = keys[3]\n local now = tonumber(args[1])\n local count = 0\n local cursorMin = 0\n while true do\n local nextEntry = redis.call('ZRANGEBYSCORE', scheduledKey, tostring(cursorMin), '+inf', 'WITHSCORES', 'LIMIT', 0, 1)\n if not nextEntry or #nextEntry == 0 then\n break\n end\n local firstScore = tonumber(nextEntry[2]) or 0\n local priority = math.floor(firstScore / PRIORITY_SHIFT)\n local minScore = priority * PRIORITY_SHIFT\n local maxDueScore = minScore + now\n local members = redis.call('ZRANGEBYSCORE', scheduledKey, tostring(minScore), tostring(maxDueScore))\n for i = 1, #members do\n local jobId = members[i]\n redis.call('XADD', streamKey, '*', 'jobId', jobId)\n redis.call('ZREM', scheduledKey, jobId)\n local prefix = string.sub(scheduledKey, 1, #scheduledKey - 9)\n local jobKey = prefix .. 'job:' .. jobId\n redis.call('HSET', jobKey, 'state', 'waiting')\n emitEvent(eventsKey, 'promoted', jobId, nil)\n count = count + 1\n end\n cursorMin = (priority + 1) * PRIORITY_SHIFT\n end\n return count\nend)\n\nredis.register_function('glidemq_complete', function(keys, args)\n local streamKey = keys[1]\n local completedKey = keys[2]\n local eventsKey = keys[3]\n local jobKey = keys[4]\n local jobId = args[1]\n local entryId = args[2]\n local returnvalue = args[3]\n local timestamp = tonumber(args[4])\n local group = args[5]\n local removeMode = args[6] or '0'\n local removeCount = tonumber(args[7]) or 0\n local removeAge = tonumber(args[8]) or 0\n local depsMember = args[9] or ''\n local parentId = args[10] or ''\n redis.call('XACK', streamKey, group, entryId)\n redis.call('XDEL', streamKey, entryId)\n redis.call('ZADD', completedKey, timestamp, jobId)\n redis.call('HSET', jobKey,\n 'state', 'completed',\n 'returnvalue', returnvalue,\n 'finishedOn', tostring(timestamp)\n )\n emitEvent(eventsKey, 'completed', jobId, {'returnvalue', returnvalue})\n local prefix = string.sub(jobKey, 1, #jobKey - #('job:' .. jobId))\n if removeMode == 'true' then\n redis.call('ZREM', completedKey, jobId)\n redis.call('DEL', jobKey)\n elseif removeMode == 'count' and removeCount > 0 then\n local total = redis.call('ZCARD', completedKey)\n if total > removeCount then\n local excess = redis.call('ZRANGE', completedKey, 0, total - removeCount - 1)\n for i = 1, #excess do\n local oldId = excess[i]\n redis.call('DEL', prefix .. 'job:' .. oldId)\n redis.call('ZREM', completedKey, oldId)\n end\n end\n elseif removeMode == 'age_count' then\n if removeAge > 0 then\n local cutoff = timestamp - (removeAge * 1000)\n local old = redis.call('ZRANGEBYSCORE', completedKey, '0', tostring(cutoff))\n for i = 1, #old do\n local oldId = old[i]\n redis.call('DEL', prefix .. 'job:' .. oldId)\n redis.call('ZREM', completedKey, oldId)\n end\n end\n if removeCount > 0 then\n local total = redis.call('ZCARD', completedKey)\n if total > removeCount then\n local excess = redis.call('ZRANGE', completedKey, 0, total - removeCount - 1)\n for i = 1, #excess do\n local oldId = excess[i]\n redis.call('DEL', prefix .. 'job:' .. oldId)\n redis.call('ZREM', completedKey, oldId)\n end\n end\n end\n end\n if depsMember ~= '' and parentId ~= '' and #keys >= 8 then\n local parentDepsKey = keys[5]\n local parentJobKey = keys[6]\n local parentStreamKey = keys[7]\n local parentEventsKey = keys[8]\n local doneCount = redis.call('HINCRBY', parentJobKey, 'depsCompleted', 1)\n local totalDeps = redis.call('SCARD', parentDepsKey)\n local remaining = totalDeps - doneCount\n if remaining <= 0 then\n redis.call('HSET', parentJobKey, 'state', 'waiting')\n redis.call('XADD', parentStreamKey, '*', 'jobId', parentId)\n emitEvent(parentEventsKey, 'active', parentId, nil)\n end\n end\n return 1\nend)\n\nredis.register_function('glidemq_completeAndFetchNext', function(keys, args)\n local streamKey = keys[1]\n local completedKey = keys[2]\n local eventsKey = keys[3]\n local jobKey = keys[4]\n local jobId = args[1]\n local entryId = args[2]\n local returnvalue = args[3]\n local timestamp = tonumber(args[4])\n local group = args[5]\n local consumer = args[6]\n local removeMode = args[7] or '0'\n local removeCount = tonumber(args[8]) or 0\n local removeAge = tonumber(args[9]) or 0\n local depsMember = args[10] or ''\n local parentId = args[11] or ''\n\n -- Phase 1: Complete current job (same as glidemq_complete)\n redis.call('XACK', streamKey, group, entryId)\n redis.call('XDEL', streamKey, entryId)\n redis.call('ZADD', completedKey, timestamp, jobId)\n redis.call('HSET', jobKey,\n 'state', 'completed',\n 'returnvalue', returnvalue,\n 'finishedOn', tostring(timestamp)\n )\n emitEvent(eventsKey, 'completed', jobId, {'returnvalue', returnvalue})\n local prefix = string.sub(jobKey, 1, #jobKey - #('job:' .. jobId))\n\n -- Retention cleanup\n if removeMode == 'true' then\n redis.call('ZREM', completedKey, jobId)\n redis.call('DEL', jobKey)\n elseif removeMode == 'count' and removeCount > 0 then\n local total = redis.call('ZCARD', completedKey)\n if total > removeCount then\n local excess = redis.call('ZRANGE', completedKey, 0, total - removeCount - 1)\n for i = 1, #excess do\n redis.call('DEL', prefix .. 'job:' .. excess[i])\n redis.call('ZREM', completedKey, excess[i])\n end\n end\n end\n\n -- Parent deps\n if depsMember ~= '' and parentId ~= '' and #keys >= 8 then\n local parentDepsKey = keys[5]\n local parentJobKey = keys[6]\n local parentStreamKey = keys[7]\n local parentEventsKey = keys[8]\n local doneCount = redis.call('HINCRBY', parentJobKey, 'depsCompleted', 1)\n local totalDeps = redis.call('SCARD', parentDepsKey)\n if totalDeps - doneCount <= 0 then\n redis.call('HSET', parentJobKey, 'state', 'waiting')\n redis.call('XADD', parentStreamKey, '*', 'jobId', parentId)\n emitEvent(parentEventsKey, 'active', parentId, nil)\n end\n end\n\n -- Phase 2: Fetch next job (non-blocking XREADGROUP)\n local nextEntries = redis.call('XREADGROUP', 'GROUP', group, consumer, 'COUNT', 1, 'STREAMS', streamKey, '>')\n if not nextEntries or #nextEntries == 0 then\n return cjson.encode({completed = jobId, next = false})\n end\n local streamData = nextEntries[1]\n local entries = streamData[2]\n if not entries or #entries == 0 then\n return cjson.encode({completed = jobId, next = false})\n end\n local nextEntry = entries[1]\n local nextEntryId = nextEntry[1]\n local nextFields = nextEntry[2]\n local nextJobId = nil\n for i = 1, #nextFields, 2 do\n if nextFields[i] == 'jobId' then\n nextJobId = nextFields[i + 1]\n break\n end\n end\n if not nextJobId then\n return cjson.encode({completed = jobId, next = false})\n end\n\n -- Phase 3: Activate next job (same as moveToActive)\n local nextJobKey = prefix .. 'job:' .. nextJobId\n local nextExists = redis.call('EXISTS', nextJobKey)\n if nextExists == 0 then\n return cjson.encode({completed = jobId, next = false, nextEntryId = nextEntryId})\n end\n local revoked = redis.call('HGET', nextJobKey, 'revoked')\n if revoked == '1' then\n return cjson.encode({completed = jobId, next = 'REVOKED', nextJobId = nextJobId, nextEntryId = nextEntryId})\n end\n redis.call('HSET', nextJobKey, 'state', 'active', 'processedOn', tostring(timestamp), 'lastActive', tostring(timestamp))\n local nextHash = redis.call('HGETALL', nextJobKey)\n return cjson.encode({completed = jobId, next = nextHash, nextJobId = nextJobId, nextEntryId = nextEntryId})\nend)\n\nredis.register_function('glidemq_fail', function(keys, args)\n local streamKey = keys[1]\n local failedKey = keys[2]\n local scheduledKey = keys[3]\n local eventsKey = keys[4]\n local jobKey = keys[5]\n local jobId = args[1]\n local entryId = args[2]\n local failedReason = args[3]\n local timestamp = tonumber(args[4])\n local maxAttempts = tonumber(args[5]) or 0\n local backoffDelay = tonumber(args[6]) or 0\n local group = args[7]\n local removeMode = args[8] or '0'\n local removeCount = tonumber(args[9]) or 0\n local removeAge = tonumber(args[10]) or 0\n redis.call('XACK', streamKey, group, entryId)\n redis.call('XDEL', streamKey, entryId)\n local attemptsMade = redis.call('HINCRBY', jobKey, 'attemptsMade', 1)\n if maxAttempts > 0 and attemptsMade < maxAttempts then\n local retryAt = timestamp + backoffDelay\n local priority = tonumber(redis.call('HGET', jobKey, 'priority')) or 0\n local score = priority * PRIORITY_SHIFT + retryAt\n redis.call('ZADD', scheduledKey, score, jobId)\n redis.call('HSET', jobKey,\n 'state', 'delayed',\n 'failedReason', failedReason,\n 'processedOn', tostring(timestamp)\n )\n emitEvent(eventsKey, 'retrying', jobId, {\n 'failedReason', failedReason,\n 'attemptsMade', tostring(attemptsMade),\n 'delay', tostring(backoffDelay)\n })\n return 'retrying'\n else\n redis.call('ZADD', failedKey, timestamp, jobId)\n redis.call('HSET', jobKey,\n 'state', 'failed',\n 'failedReason', failedReason,\n 'finishedOn', tostring(timestamp),\n 'processedOn', tostring(timestamp)\n )\n emitEvent(eventsKey, 'failed', jobId, {'failedReason', failedReason})\n local prefix = string.sub(jobKey, 1, #jobKey - #('job:' .. jobId))\n if removeMode == 'true' then\n redis.call('ZREM', failedKey, jobId)\n redis.call('DEL', jobKey)\n elseif removeMode == 'count' and removeCount > 0 then\n local total = redis.call('ZCARD', failedKey)\n if total > removeCount then\n local excess = redis.call('ZRANGE', failedKey, 0, total - removeCount - 1)\n for i = 1, #excess do\n local oldId = excess[i]\n redis.call('DEL', prefix .. 'job:' .. oldId)\n redis.call('ZREM', failedKey, oldId)\n end\n end\n elseif removeMode == 'age_count' then\n if removeAge > 0 then\n local cutoff = timestamp - (removeAge * 1000)\n local old = redis.call('ZRANGEBYSCORE', failedKey, '0', tostring(cutoff))\n for i = 1, #old do\n local oldId = old[i]\n redis.call('DEL', prefix .. 'job:' .. oldId)\n redis.call('ZREM', failedKey, oldId)\n end\n end\n if removeCount > 0 then\n local total = redis.call('ZCARD', failedKey)\n if total > removeCount then\n local excess = redis.call('ZRANGE', failedKey, 0, total - removeCount - 1)\n for i = 1, #excess do\n local oldId = excess[i]\n redis.call('DEL', prefix .. 'job:' .. oldId)\n redis.call('ZREM', failedKey, oldId)\n end\n end\n end\n end\n return 'failed'\n end\nend)\n\nredis.register_function('glidemq_reclaimStalled', function(keys, args)\n local streamKey = keys[1]\n local eventsKey = keys[2]\n local group = args[1]\n local consumer = args[2]\n local minIdleMs = tonumber(args[3])\n local maxStalledCount = tonumber(args[4]) or 1\n local timestamp = tonumber(args[5])\n local failedKey = args[6]\n local result = redis.call('XAUTOCLAIM', streamKey, group, consumer, minIdleMs, '0-0')\n local entries = result[2]\n if not entries or #entries == 0 then\n return 0\n end\n local prefix = string.sub(streamKey, 1, #streamKey - 6)\n local count = 0\n for i = 1, #entries do\n local entry = entries[i]\n local entryId = entry[1]\n local fields = entry[2]\n local jobId = nil\n if type(fields) == 'table' then\n for j = 1, #fields, 2 do\n if fields[j] == 'jobId' then\n jobId = fields[j + 1]\n break\n end\n end\n end\n if jobId then\n local jobKey = prefix .. 'job:' .. jobId\n local lastActive = tonumber(redis.call('HGET', jobKey, 'lastActive'))\n if lastActive and (timestamp - lastActive) < minIdleMs then\n count = count + 1\n else\n local stalledCount = redis.call('HINCRBY', jobKey, 'stalledCount', 1)\n if stalledCount > maxStalledCount then\n redis.call('XACK', streamKey, group, entryId)\n redis.call('XDEL', streamKey, entryId)\n redis.call('ZADD', failedKey, timestamp, jobId)\n redis.call('HSET', jobKey,\n 'state', 'failed',\n 'failedReason', 'job stalled more than maxStalledCount',\n 'finishedOn', tostring(timestamp)\n )\n emitEvent(eventsKey, 'failed', jobId, {\n 'failedReason', 'job stalled more than maxStalledCount'\n })\n else\n redis.call('HSET', jobKey, 'state', 'active')\n emitEvent(eventsKey, 'stalled', jobId, nil)\n end\n count = count + 1\n end\n end\n end\n return count\nend)\n\nredis.register_function('glidemq_pause', function(keys, args)\n local metaKey = keys[1]\n local eventsKey = keys[2]\n redis.call('HSET', metaKey, 'paused', '1')\n emitEvent(eventsKey, 'paused', '0', nil)\n return 1\nend)\n\nredis.register_function('glidemq_resume', function(keys, args)\n local metaKey = keys[1]\n local eventsKey = keys[2]\n redis.call('HSET', metaKey, 'paused', '0')\n emitEvent(eventsKey, 'resumed', '0', nil)\n return 1\nend)\n\nredis.register_function('glidemq_dedup', function(keys, args)\n local dedupKey = keys[1]\n local idKey = keys[2]\n local streamKey = keys[3]\n local scheduledKey = keys[4]\n local eventsKey = keys[5]\n local dedupId = args[1]\n local ttlMs = tonumber(args[2]) or 0\n local mode = args[3]\n local jobName = args[4]\n local jobData = args[5]\n local jobOpts = args[6]\n local timestamp = tonumber(args[7])\n local delay = tonumber(args[8]) or 0\n local priority = tonumber(args[9]) or 0\n local parentId = args[10] or ''\n local maxAttempts = tonumber(args[11]) or 0\n local prefix = string.sub(idKey, 1, #idKey - 2)\n local existing = redis.call('HGET', dedupKey, dedupId)\n if mode == 'simple' then\n if existing then\n local sep = string.find(existing, ':')\n if sep then\n local existingJobId = string.sub(existing, 1, sep - 1)\n local jobKey = prefix .. 'job:' .. existingJobId\n local state = redis.call('HGET', jobKey, 'state')\n if state and state ~= 'completed' and state ~= 'failed' then\n return 'skipped'\n end\n end\n end\n elseif mode == 'throttle' then\n if existing and ttlMs > 0 then\n local sep = string.find(existing, ':')\n if sep then\n local storedTs = tonumber(string.sub(existing, sep + 1))\n if storedTs and (timestamp - storedTs) < ttlMs then\n return 'skipped'\n end\n end\n end\n elseif mode == 'debounce' then\n if existing then\n local sep = string.find(existing, ':')\n if sep then\n local existingJobId = string.sub(existing, 1, sep - 1)\n local jobKey = prefix .. 'job:' .. existingJobId\n local state = redis.call('HGET', jobKey, 'state')\n if state == 'delayed' or state == 'prioritized' then\n redis.call('ZREM', scheduledKey, existingJobId)\n redis.call('DEL', jobKey)\n emitEvent(eventsKey, 'removed', existingJobId, nil)\n elseif state and state ~= 'completed' and state ~= 'failed' then\n return 'skipped'\n end\n end\n end\n end\n local jobId = redis.call('INCR', idKey)\n local jobIdStr = tostring(jobId)\n local jobKey = prefix .. 'job:' .. jobIdStr\n local hashFields = {\n 'id', jobIdStr,\n 'name', jobName,\n 'data', jobData,\n 'opts', jobOpts,\n 'timestamp', tostring(timestamp),\n 'attemptsMade', '0',\n 'delay', tostring(delay),\n 'priority', tostring(priority),\n 'maxAttempts', tostring(maxAttempts)\n }\n if parentId ~= '' then\n hashFields[#hashFields + 1] = 'parentId'\n hashFields[#hashFields + 1] = parentId\n end\n if delay > 0 or priority > 0 then\n hashFields[#hashFields + 1] = 'state'\n hashFields[#hashFields + 1] = delay > 0 and 'delayed' or 'prioritized'\n else\n hashFields[#hashFields + 1] = 'state'\n hashFields[#hashFields + 1] = 'waiting'\n end\n redis.call('HSET', jobKey, unpack(hashFields))\n if delay > 0 then\n local score = priority * PRIORITY_SHIFT + (timestamp + delay)\n redis.call('ZADD', scheduledKey, score, jobIdStr)\n elseif priority > 0 then\n local score = priority * PRIORITY_SHIFT\n redis.call('ZADD', scheduledKey, score, jobIdStr)\n else\n redis.call('XADD', streamKey, '*', 'jobId', jobIdStr)\n end\n redis.call('HSET', dedupKey, dedupId, jobIdStr .. ':' .. tostring(timestamp))\n emitEvent(eventsKey, 'added', jobIdStr, {'name', jobName})\n return jobIdStr\nend)\n\nredis.register_function('glidemq_rateLimit', function(keys, args)\n local rateKey = keys[1]\n local metaKey = keys[2]\n local maxPerWindow = tonumber(args[1])\n local windowDuration = tonumber(args[2])\n local now = tonumber(args[3])\n local windowStart = tonumber(redis.call('HGET', rateKey, 'windowStart')) or 0\n local count = tonumber(redis.call('HGET', rateKey, 'count')) or 0\n if now - windowStart >= windowDuration then\n redis.call('HSET', rateKey, 'windowStart', tostring(now), 'count', '1')\n return 0\n end\n if count >= maxPerWindow then\n local delayMs = windowDuration - (now - windowStart)\n return delayMs\n end\n redis.call('HSET', rateKey, 'count', tostring(count + 1))\n return 0\nend)\n\nredis.register_function('glidemq_checkConcurrency', function(keys, args)\n local metaKey = keys[1]\n local streamKey = keys[2]\n local group = args[1]\n local gc = tonumber(redis.call('HGET', metaKey, 'globalConcurrency')) or 0\n if gc <= 0 then\n return -1\n end\n local pending = redis.call('XPENDING', streamKey, group)\n local pendingCount = tonumber(pending[1]) or 0\n local remaining = gc - pendingCount\n if remaining <= 0 then\n return 0\n end\n return remaining\nend)\n\nredis.register_function('glidemq_moveToActive', function(keys, args)\n local jobKey = keys[1]\n local timestamp = args[1]\n local exists = redis.call('EXISTS', jobKey)\n if exists == 0 then\n return ''\n end\n local revoked = redis.call('HGET', jobKey, 'revoked')\n if revoked == '1' then\n return 'REVOKED'\n end\n redis.call('HSET', jobKey, 'state', 'active', 'processedOn', timestamp, 'lastActive', timestamp)\n local fields = redis.call('HGETALL', jobKey)\n return cjson.encode(fields)\nend)\n\nredis.register_function('glidemq_addFlow', function(keys, args)\n local parentIdKey = keys[1]\n local parentStreamKey = keys[2]\n local parentScheduledKey = keys[3]\n local parentEventsKey = keys[4]\n local parentName = args[1]\n local parentData = args[2]\n local parentOpts = args[3]\n local timestamp = tonumber(args[4])\n local parentDelay = tonumber(args[5]) or 0\n local parentPriority = tonumber(args[6]) or 0\n local parentMaxAttempts = tonumber(args[7]) or 0\n local numChildren = tonumber(args[8])\n local parentJobId = redis.call('INCR', parentIdKey)\n local parentJobIdStr = tostring(parentJobId)\n local parentPrefix = string.sub(parentIdKey, 1, #parentIdKey - 2)\n local parentJobKey = parentPrefix .. 'job:' .. parentJobIdStr\n local depsKey = parentPrefix .. 'deps:' .. parentJobIdStr\n local parentHash = {\n 'id', parentJobIdStr,\n 'name', parentName,\n 'data', parentData,\n 'opts', parentOpts,\n 'timestamp', tostring(timestamp),\n 'attemptsMade', '0',\n 'delay', tostring(parentDelay),\n 'priority', tostring(parentPriority),\n 'maxAttempts', tostring(parentMaxAttempts),\n 'state', 'waiting-children'\n }\n redis.call('HSET', parentJobKey, unpack(parentHash))\n local childIds = {}\n local childArgOffset = 8\n local childKeyOffset = 4\n for i = 1, numChildren do\n local base = childArgOffset + (i - 1) * 8\n local childName = args[base + 1]\n local childData = args[base + 2]\n local childOpts = args[base + 3]\n local childDelay = tonumber(args[base + 4]) or 0\n local childPriority = tonumber(args[base + 5]) or 0\n local childMaxAttempts = tonumber(args[base + 6]) or 0\n local childQueuePrefix = args[base + 7]\n local childParentQueue = args[base + 8]\n local ckBase = childKeyOffset + (i - 1) * 4\n local childIdKey = keys[ckBase + 1]\n local childStreamKey = keys[ckBase + 2]\n local childScheduledKey = keys[ckBase + 3]\n local childEventsKey = keys[ckBase + 4]\n local childJobId = redis.call('INCR', childIdKey)\n local childJobIdStr = tostring(childJobId)\n local childPrefix = string.sub(childIdKey, 1, #childIdKey - 2)\n local childJobKey = childPrefix .. 'job:' .. childJobIdStr\n local childHash = {\n 'id', childJobIdStr,\n 'name', childName,\n 'data', childData,\n 'opts', childOpts,\n 'timestamp', tostring(timestamp),\n 'attemptsMade', '0',\n 'delay', tostring(childDelay),\n 'priority', tostring(childPriority),\n 'maxAttempts', tostring(childMaxAttempts),\n 'parentId', parentJobIdStr,\n 'parentQueue', childParentQueue\n }\n if childDelay > 0 or childPriority > 0 then\n childHash[#childHash + 1] = 'state'\n childHash[#childHash + 1] = childDelay > 0 and 'delayed' or 'prioritized'\n else\n childHash[#childHash + 1] = 'state'\n childHash[#childHash + 1] = 'waiting'\n end\n redis.call('HSET', childJobKey, unpack(childHash))\n local depsMember = childQueuePrefix .. ':' .. childJobIdStr\n redis.call('SADD', depsKey, depsMember)\n if childDelay > 0 then\n local score = childPriority * PRIORITY_SHIFT + (timestamp + childDelay)\n redis.call('ZADD', childScheduledKey, score, childJobIdStr)\n elseif childPriority > 0 then\n local score = childPriority * PRIORITY_SHIFT\n redis.call('ZADD', childScheduledKey, score, childJobIdStr)\n else\n redis.call('XADD', childStreamKey, '*', 'jobId', childJobIdStr)\n end\n emitEvent(childEventsKey, 'added', childJobIdStr, {'name', childName})\n childIds[#childIds + 1] = childJobIdStr\n end\n local extraDepsOffset = childArgOffset + numChildren * 8\n local numExtraDeps = tonumber(args[extraDepsOffset + 1]) or 0\n for i = 1, numExtraDeps do\n local extraMember = args[extraDepsOffset + 1 + i]\n redis.call('SADD', depsKey, extraMember)\n end\n emitEvent(parentEventsKey, 'added', parentJobIdStr, {'name', parentName})\n local result = {parentJobIdStr}\n for i = 1, #childIds do\n result[#result + 1] = childIds[i]\n end\n return cjson.encode(result)\nend)\n\nredis.register_function('glidemq_completeChild', function(keys, args)\n local depsKey = keys[1]\n local parentJobKey = keys[2]\n local parentStreamKey = keys[3]\n local parentEventsKey = keys[4]\n local depsMember = args[1]\n local parentId = args[2]\n local doneCount = redis.call('HINCRBY', parentJobKey, 'depsCompleted', 1)\n local totalDeps = redis.call('SCARD', depsKey)\n local remaining = totalDeps - doneCount\n if remaining <= 0 then\n redis.call('HSET', parentJobKey, 'state', 'waiting')\n redis.call('XADD', parentStreamKey, '*', 'jobId', parentId)\n emitEvent(parentEventsKey, 'active', parentId, nil)\n end\n return remaining\nend)\n\nredis.register_function('glidemq_removeJob', function(keys, args)\n local jobKey = keys[1]\n local streamKey = keys[2]\n local scheduledKey = keys[3]\n local completedKey = keys[4]\n local failedKey = keys[5]\n local eventsKey = keys[6]\n local logKey = keys[7]\n local jobId = args[1]\n local exists = redis.call('EXISTS', jobKey)\n if exists == 0 then\n return 0\n end\n redis.call('ZREM', scheduledKey, jobId)\n redis.call('ZREM', completedKey, jobId)\n redis.call('ZREM', failedKey, jobId)\n redis.call('DEL', jobKey)\n redis.call('DEL', logKey)\n emitEvent(eventsKey, 'removed', jobId, nil)\n return 1\nend)\n\nredis.register_function('glidemq_revoke', function(keys, args)\n local jobKey = keys[1]\n local streamKey = keys[2]\n local scheduledKey = keys[3]\n local failedKey = keys[4]\n local eventsKey = keys[5]\n local jobId = args[1]\n local timestamp = tonumber(args[2])\n local group = args[3]\n local exists = redis.call('EXISTS', jobKey)\n if exists == 0 then\n return 'not_found'\n end\n redis.call('HSET', jobKey, 'revoked', '1')\n local state = redis.call('HGET', jobKey, 'state')\n if state == 'waiting' or state == 'delayed' or state == 'prioritized' then\n redis.call('ZREM', scheduledKey, jobId)\n local entries = redis.call('XRANGE', streamKey, '-', '+')\n for i = 1, #entries do\n local entryId = entries[i][1]\n local fields = entries[i][2]\n for j = 1, #fields, 2 do\n if fields[j] == 'jobId' and fields[j+1] == jobId then\n redis.call('XACK', streamKey, group, entryId)\n redis.call('XDEL', streamKey, entryId)\n break\n end\n end\n end\n redis.call('ZADD', failedKey, timestamp, jobId)\n redis.call('HSET', jobKey,\n 'state', 'failed',\n 'failedReason', 'revoked',\n 'finishedOn', tostring(timestamp)\n )\n emitEvent(eventsKey, 'revoked', jobId, nil)\n return 'revoked'\n end\n emitEvent(eventsKey, 'revoked', jobId, nil)\n return 'flagged'\nend)\n\nredis.register_function('glidemq_searchByName', function(keys, args)\n local stateKey = keys[1]\n local stateType = args[1]\n local nameFilter = args[2]\n local limit = tonumber(args[3]) or 100\n local prefix = args[4]\n local matched = {}\n if stateType == 'zset' then\n local members = redis.call('ZRANGE', stateKey, 0, -1)\n for i = 1, #members do\n if #matched >= limit then break end\n local jobId = members[i]\n local jobKey = prefix .. 'job:' .. jobId\n local name = redis.call('HGET', jobKey, 'name')\n if name == nameFilter then\n matched[#matched + 1] = jobId\n end\n end\n elseif stateType == 'stream' then\n local entries = redis.call('XRANGE', stateKey, '-', '+')\n for i = 1, #entries do\n if #matched >= limit then break end\n local fields = entries[i][2]\n local jobId = nil\n for j = 1, #fields, 2 do\n if fields[j] == 'jobId' then\n jobId = fields[j + 1]\n break\n end\n end\n if jobId then\n local jobKey = prefix .. 'job:' .. jobId\n local name = redis.call('HGET', jobKey, 'name')\n if name == nameFilter then\n matched[#matched + 1] = jobId\n end\n end\n end\n end\n return matched\nend)\n";
6
+ export declare const LIBRARY_SOURCE = "#!lua name=glidemq\n\nlocal PRIORITY_SHIFT = 4398046511104\n\nlocal function emitEvent(eventsKey, eventType, jobId, extraFields)\n local fields = {'event', eventType, 'jobId', tostring(jobId)}\n if extraFields then\n for i = 1, #extraFields, 2 do\n fields[#fields + 1] = extraFields[i]\n fields[#fields + 1] = extraFields[i + 1]\n end\n end\n redis.call('XADD', eventsKey, 'MAXLEN', '~', '1000', '*', unpack(fields))\nend\n\nlocal function markOrderingDone(jobKey, jobId)\n local orderingKey = redis.call('HGET', jobKey, 'orderingKey')\n if not orderingKey or orderingKey == '' then\n return\n end\n local orderingSeq = tonumber(redis.call('HGET', jobKey, 'orderingSeq')) or 0\n if orderingSeq <= 0 then\n return\n end\n\n local prefix = string.sub(jobKey, 1, #jobKey - #('job:' .. jobId))\n local metaKey = prefix .. 'meta'\n local doneField = 'orderdone:' .. orderingKey\n local pendingKey = prefix .. 'orderdone:pending:' .. orderingKey\n\n local lastDone = tonumber(redis.call('HGET', metaKey, doneField)) or 0\n if orderingSeq <= lastDone then\n redis.call('HDEL', pendingKey, tostring(orderingSeq))\n return\n end\n\n redis.call('HSET', pendingKey, tostring(orderingSeq), '1')\n local advanced = lastDone\n while true do\n local nextSeq = advanced + 1\n if redis.call('HEXISTS', pendingKey, tostring(nextSeq)) == 0 then\n break\n end\n redis.call('HDEL', pendingKey, tostring(nextSeq))\n advanced = nextSeq\n end\n if advanced > lastDone then\n redis.call('HSET', metaKey, doneField, tostring(advanced))\n end\nend\n\nlocal function extractOrderingKeyFromOpts(optsJson)\n if not optsJson or optsJson == '' then\n return ''\n end\n local ok, decoded = pcall(cjson.decode, optsJson)\n if not ok or type(decoded) ~= 'table' then\n return ''\n end\n local ordering = decoded['ordering']\n if type(ordering) ~= 'table' then\n return ''\n end\n local key = ordering['key']\n if key == nil then\n return ''\n end\n return tostring(key)\nend\n\nredis.register_function('glidemq_version', function(keys, args)\n return '15'\nend)\n\nredis.register_function('glidemq_addJob', function(keys, args)\n local idKey = keys[1]\n local streamKey = keys[2]\n local scheduledKey = keys[3]\n local eventsKey = keys[4]\n local jobName = args[1]\n local jobData = args[2]\n local jobOpts = args[3]\n local timestamp = tonumber(args[4])\n local delay = tonumber(args[5]) or 0\n local priority = tonumber(args[6]) or 0\n local parentId = args[7] or ''\n local maxAttempts = tonumber(args[8]) or 0\n local orderingKey = args[9] or ''\n local jobId = redis.call('INCR', idKey)\n local jobIdStr = tostring(jobId)\n local prefix = string.sub(idKey, 1, #idKey - 2)\n local jobKey = prefix .. 'job:' .. jobIdStr\n local orderingSeq = 0\n if orderingKey ~= '' then\n local orderingMetaKey = prefix .. 'ordering'\n orderingSeq = redis.call('HINCRBY', orderingMetaKey, orderingKey, 1)\n end\n local hashFields = {\n 'id', jobIdStr,\n 'name', jobName,\n 'data', jobData,\n 'opts', jobOpts,\n 'timestamp', tostring(timestamp),\n 'attemptsMade', '0',\n 'delay', tostring(delay),\n 'priority', tostring(priority),\n 'maxAttempts', tostring(maxAttempts)\n }\n if orderingKey ~= '' then\n hashFields[#hashFields + 1] = 'orderingKey'\n hashFields[#hashFields + 1] = orderingKey\n hashFields[#hashFields + 1] = 'orderingSeq'\n hashFields[#hashFields + 1] = tostring(orderingSeq)\n end\n if parentId ~= '' then\n hashFields[#hashFields + 1] = 'parentId'\n hashFields[#hashFields + 1] = parentId\n end\n if delay > 0 or priority > 0 then\n hashFields[#hashFields + 1] = 'state'\n hashFields[#hashFields + 1] = delay > 0 and 'delayed' or 'prioritized'\n else\n hashFields[#hashFields + 1] = 'state'\n hashFields[#hashFields + 1] = 'waiting'\n end\n redis.call('HSET', jobKey, unpack(hashFields))\n if delay > 0 then\n local score = priority * PRIORITY_SHIFT + (timestamp + delay)\n redis.call('ZADD', scheduledKey, score, jobIdStr)\n elseif priority > 0 then\n local score = priority * PRIORITY_SHIFT\n redis.call('ZADD', scheduledKey, score, jobIdStr)\n else\n redis.call('XADD', streamKey, '*', 'jobId', jobIdStr)\n end\n emitEvent(eventsKey, 'added', jobIdStr, {'name', jobName})\n return jobIdStr\nend)\n\nredis.register_function('glidemq_promote', function(keys, args)\n local scheduledKey = keys[1]\n local streamKey = keys[2]\n local eventsKey = keys[3]\n local now = tonumber(args[1])\n local MAX_PROMOTIONS = 1000\n local count = 0\n local cursorMin = 0\n while count < MAX_PROMOTIONS do\n local nextEntry = redis.call('ZRANGEBYSCORE', scheduledKey, string.format('%.0f', cursorMin), '+inf', 'WITHSCORES', 'LIMIT', 0, 1)\n if not nextEntry or #nextEntry == 0 then\n break\n end\n local firstScore = tonumber(nextEntry[2]) or 0\n local priority = math.floor(firstScore / PRIORITY_SHIFT)\n local minScore = priority * PRIORITY_SHIFT\n local maxDueScore = minScore + now\n local remaining = MAX_PROMOTIONS - count\n local members = redis.call(\n 'ZRANGEBYSCORE',\n scheduledKey,\n string.format('%.0f', minScore),\n string.format('%.0f', maxDueScore),\n 'LIMIT',\n 0,\n remaining\n )\n for i = 1, #members do\n local jobId = members[i]\n redis.call('XADD', streamKey, '*', 'jobId', jobId)\n redis.call('ZREM', scheduledKey, jobId)\n local prefix = string.sub(scheduledKey, 1, #scheduledKey - 9)\n local jobKey = prefix .. 'job:' .. jobId\n redis.call('HSET', jobKey, 'state', 'waiting')\n emitEvent(eventsKey, 'promoted', jobId, nil)\n count = count + 1\n end\n cursorMin = (priority + 1) * PRIORITY_SHIFT\n end\n return count\nend)\n\nredis.register_function('glidemq_complete', function(keys, args)\n local streamKey = keys[1]\n local completedKey = keys[2]\n local eventsKey = keys[3]\n local jobKey = keys[4]\n local jobId = args[1]\n local entryId = args[2]\n local returnvalue = args[3]\n local timestamp = tonumber(args[4])\n local group = args[5]\n local removeMode = args[6] or '0'\n local removeCount = tonumber(args[7]) or 0\n local removeAge = tonumber(args[8]) or 0\n local depsMember = args[9] or ''\n local parentId = args[10] or ''\n redis.call('XACK', streamKey, group, entryId)\n redis.call('XDEL', streamKey, entryId)\n redis.call('ZADD', completedKey, timestamp, jobId)\n redis.call('HSET', jobKey,\n 'state', 'completed',\n 'returnvalue', returnvalue,\n 'finishedOn', tostring(timestamp)\n )\n markOrderingDone(jobKey, jobId)\n emitEvent(eventsKey, 'completed', jobId, {'returnvalue', returnvalue})\n local prefix = string.sub(jobKey, 1, #jobKey - #('job:' .. jobId))\n if removeMode == 'true' then\n redis.call('ZREM', completedKey, jobId)\n redis.call('DEL', jobKey)\n elseif removeMode == 'count' and removeCount > 0 then\n local total = redis.call('ZCARD', completedKey)\n if total > removeCount then\n local excess = redis.call('ZRANGE', completedKey, 0, total - removeCount - 1)\n for i = 1, #excess do\n local oldId = excess[i]\n redis.call('DEL', prefix .. 'job:' .. oldId)\n redis.call('ZREM', completedKey, oldId)\n end\n end\n elseif removeMode == 'age_count' then\n if removeAge > 0 then\n local cutoff = timestamp - (removeAge * 1000)\n local old = redis.call('ZRANGEBYSCORE', completedKey, '0', tostring(cutoff))\n for i = 1, #old do\n local oldId = old[i]\n redis.call('DEL', prefix .. 'job:' .. oldId)\n redis.call('ZREM', completedKey, oldId)\n end\n end\n if removeCount > 0 then\n local total = redis.call('ZCARD', completedKey)\n if total > removeCount then\n local excess = redis.call('ZRANGE', completedKey, 0, total - removeCount - 1)\n for i = 1, #excess do\n local oldId = excess[i]\n redis.call('DEL', prefix .. 'job:' .. oldId)\n redis.call('ZREM', completedKey, oldId)\n end\n end\n end\n end\n if depsMember ~= '' and parentId ~= '' and #keys >= 8 then\n local parentDepsKey = keys[5]\n local parentJobKey = keys[6]\n local parentStreamKey = keys[7]\n local parentEventsKey = keys[8]\n local doneCount = redis.call('HINCRBY', parentJobKey, 'depsCompleted', 1)\n local totalDeps = redis.call('SCARD', parentDepsKey)\n local remaining = totalDeps - doneCount\n if remaining <= 0 then\n redis.call('HSET', parentJobKey, 'state', 'waiting')\n redis.call('XADD', parentStreamKey, '*', 'jobId', parentId)\n emitEvent(parentEventsKey, 'active', parentId, nil)\n end\n end\n return 1\nend)\n\nredis.register_function('glidemq_completeAndFetchNext', function(keys, args)\n local streamKey = keys[1]\n local completedKey = keys[2]\n local eventsKey = keys[3]\n local jobKey = keys[4]\n local jobId = args[1]\n local entryId = args[2]\n local returnvalue = args[3]\n local timestamp = tonumber(args[4])\n local group = args[5]\n local consumer = args[6]\n local removeMode = args[7] or '0'\n local removeCount = tonumber(args[8]) or 0\n local removeAge = tonumber(args[9]) or 0\n local depsMember = args[10] or ''\n local parentId = args[11] or ''\n\n -- Phase 1: Complete current job (same as glidemq_complete)\n redis.call('XACK', streamKey, group, entryId)\n redis.call('XDEL', streamKey, entryId)\n redis.call('ZADD', completedKey, timestamp, jobId)\n redis.call('HSET', jobKey,\n 'state', 'completed',\n 'returnvalue', returnvalue,\n 'finishedOn', tostring(timestamp)\n )\n markOrderingDone(jobKey, jobId)\n emitEvent(eventsKey, 'completed', jobId, {'returnvalue', returnvalue})\n local prefix = string.sub(jobKey, 1, #jobKey - #('job:' .. jobId))\n\n -- Retention cleanup\n if removeMode == 'true' then\n redis.call('ZREM', completedKey, jobId)\n redis.call('DEL', jobKey)\n elseif removeMode == 'count' and removeCount > 0 then\n local total = redis.call('ZCARD', completedKey)\n if total > removeCount then\n local excess = redis.call('ZRANGE', completedKey, 0, total - removeCount - 1)\n for i = 1, #excess do\n redis.call('DEL', prefix .. 'job:' .. excess[i])\n redis.call('ZREM', completedKey, excess[i])\n end\n end\n end\n\n -- Parent deps\n if depsMember ~= '' and parentId ~= '' and #keys >= 8 then\n local parentDepsKey = keys[5]\n local parentJobKey = keys[6]\n local parentStreamKey = keys[7]\n local parentEventsKey = keys[8]\n local doneCount = redis.call('HINCRBY', parentJobKey, 'depsCompleted', 1)\n local totalDeps = redis.call('SCARD', parentDepsKey)\n if totalDeps - doneCount <= 0 then\n redis.call('HSET', parentJobKey, 'state', 'waiting')\n redis.call('XADD', parentStreamKey, '*', 'jobId', parentId)\n emitEvent(parentEventsKey, 'active', parentId, nil)\n end\n end\n\n -- Phase 2: Fetch next job (non-blocking XREADGROUP)\n local nextEntries = redis.call('XREADGROUP', 'GROUP', group, consumer, 'COUNT', 1, 'STREAMS', streamKey, '>')\n if not nextEntries or #nextEntries == 0 then\n return cjson.encode({completed = jobId, next = false})\n end\n local streamData = nextEntries[1]\n local entries = streamData[2]\n if not entries or #entries == 0 then\n return cjson.encode({completed = jobId, next = false})\n end\n local nextEntry = entries[1]\n local nextEntryId = nextEntry[1]\n local nextFields = nextEntry[2]\n local nextJobId = nil\n for i = 1, #nextFields, 2 do\n if nextFields[i] == 'jobId' then\n nextJobId = nextFields[i + 1]\n break\n end\n end\n if not nextJobId then\n return cjson.encode({completed = jobId, next = false})\n end\n\n -- Phase 3: Activate next job (same as moveToActive)\n local nextJobKey = prefix .. 'job:' .. nextJobId\n local nextExists = redis.call('EXISTS', nextJobKey)\n if nextExists == 0 then\n return cjson.encode({completed = jobId, next = false, nextEntryId = nextEntryId})\n end\n local revoked = redis.call('HGET', nextJobKey, 'revoked')\n if revoked == '1' then\n return cjson.encode({completed = jobId, next = 'REVOKED', nextJobId = nextJobId, nextEntryId = nextEntryId})\n end\n redis.call('HSET', nextJobKey, 'state', 'active', 'processedOn', tostring(timestamp), 'lastActive', tostring(timestamp))\n local nextHash = redis.call('HGETALL', nextJobKey)\n return cjson.encode({completed = jobId, next = nextHash, nextJobId = nextJobId, nextEntryId = nextEntryId})\nend)\n\nredis.register_function('glidemq_fail', function(keys, args)\n local streamKey = keys[1]\n local failedKey = keys[2]\n local scheduledKey = keys[3]\n local eventsKey = keys[4]\n local jobKey = keys[5]\n local jobId = args[1]\n local entryId = args[2]\n local failedReason = args[3]\n local timestamp = tonumber(args[4])\n local maxAttempts = tonumber(args[5]) or 0\n local backoffDelay = tonumber(args[6]) or 0\n local group = args[7]\n local removeMode = args[8] or '0'\n local removeCount = tonumber(args[9]) or 0\n local removeAge = tonumber(args[10]) or 0\n redis.call('XACK', streamKey, group, entryId)\n redis.call('XDEL', streamKey, entryId)\n local attemptsMade = redis.call('HINCRBY', jobKey, 'attemptsMade', 1)\n if maxAttempts > 0 and attemptsMade < maxAttempts then\n local retryAt = timestamp + backoffDelay\n local priority = tonumber(redis.call('HGET', jobKey, 'priority')) or 0\n local score = priority * PRIORITY_SHIFT + retryAt\n redis.call('ZADD', scheduledKey, score, jobId)\n redis.call('HSET', jobKey,\n 'state', 'delayed',\n 'failedReason', failedReason,\n 'processedOn', tostring(timestamp)\n )\n emitEvent(eventsKey, 'retrying', jobId, {\n 'failedReason', failedReason,\n 'attemptsMade', tostring(attemptsMade),\n 'delay', tostring(backoffDelay)\n })\n return 'retrying'\n else\n redis.call('ZADD', failedKey, timestamp, jobId)\n redis.call('HSET', jobKey,\n 'state', 'failed',\n 'failedReason', failedReason,\n 'finishedOn', tostring(timestamp),\n 'processedOn', tostring(timestamp)\n )\n markOrderingDone(jobKey, jobId)\n emitEvent(eventsKey, 'failed', jobId, {'failedReason', failedReason})\n local prefix = string.sub(jobKey, 1, #jobKey - #('job:' .. jobId))\n if removeMode == 'true' then\n redis.call('ZREM', failedKey, jobId)\n redis.call('DEL', jobKey)\n elseif removeMode == 'count' and removeCount > 0 then\n local total = redis.call('ZCARD', failedKey)\n if total > removeCount then\n local excess = redis.call('ZRANGE', failedKey, 0, total - removeCount - 1)\n for i = 1, #excess do\n local oldId = excess[i]\n redis.call('DEL', prefix .. 'job:' .. oldId)\n redis.call('ZREM', failedKey, oldId)\n end\n end\n elseif removeMode == 'age_count' then\n if removeAge > 0 then\n local cutoff = timestamp - (removeAge * 1000)\n local old = redis.call('ZRANGEBYSCORE', failedKey, '0', tostring(cutoff))\n for i = 1, #old do\n local oldId = old[i]\n redis.call('DEL', prefix .. 'job:' .. oldId)\n redis.call('ZREM', failedKey, oldId)\n end\n end\n if removeCount > 0 then\n local total = redis.call('ZCARD', failedKey)\n if total > removeCount then\n local excess = redis.call('ZRANGE', failedKey, 0, total - removeCount - 1)\n for i = 1, #excess do\n local oldId = excess[i]\n redis.call('DEL', prefix .. 'job:' .. oldId)\n redis.call('ZREM', failedKey, oldId)\n end\n end\n end\n end\n return 'failed'\n end\nend)\n\nredis.register_function('glidemq_reclaimStalled', function(keys, args)\n local streamKey = keys[1]\n local eventsKey = keys[2]\n local group = args[1]\n local consumer = args[2]\n local minIdleMs = tonumber(args[3])\n local maxStalledCount = tonumber(args[4]) or 1\n local timestamp = tonumber(args[5])\n local failedKey = args[6]\n local result = redis.call('XAUTOCLAIM', streamKey, group, consumer, minIdleMs, '0-0')\n local entries = result[2]\n if not entries or #entries == 0 then\n return 0\n end\n local prefix = string.sub(streamKey, 1, #streamKey - 6)\n local count = 0\n for i = 1, #entries do\n local entry = entries[i]\n local entryId = entry[1]\n local fields = entry[2]\n local jobId = nil\n if type(fields) == 'table' then\n for j = 1, #fields, 2 do\n if fields[j] == 'jobId' then\n jobId = fields[j + 1]\n break\n end\n end\n end\n if jobId then\n local jobKey = prefix .. 'job:' .. jobId\n local lastActive = tonumber(redis.call('HGET', jobKey, 'lastActive'))\n if lastActive and (timestamp - lastActive) < minIdleMs then\n count = count + 1\n else\n local stalledCount = redis.call('HINCRBY', jobKey, 'stalledCount', 1)\n if stalledCount > maxStalledCount then\n redis.call('XACK', streamKey, group, entryId)\n redis.call('XDEL', streamKey, entryId)\n redis.call('ZADD', failedKey, timestamp, jobId)\n redis.call('HSET', jobKey,\n 'state', 'failed',\n 'failedReason', 'job stalled more than maxStalledCount',\n 'finishedOn', tostring(timestamp)\n )\n markOrderingDone(jobKey, jobId)\n emitEvent(eventsKey, 'failed', jobId, {\n 'failedReason', 'job stalled more than maxStalledCount'\n })\n else\n redis.call('HSET', jobKey, 'state', 'active')\n emitEvent(eventsKey, 'stalled', jobId, nil)\n end\n count = count + 1\n end\n end\n end\n return count\nend)\n\nredis.register_function('glidemq_pause', function(keys, args)\n local metaKey = keys[1]\n local eventsKey = keys[2]\n redis.call('HSET', metaKey, 'paused', '1')\n emitEvent(eventsKey, 'paused', '0', nil)\n return 1\nend)\n\nredis.register_function('glidemq_resume', function(keys, args)\n local metaKey = keys[1]\n local eventsKey = keys[2]\n redis.call('HSET', metaKey, 'paused', '0')\n emitEvent(eventsKey, 'resumed', '0', nil)\n return 1\nend)\n\nredis.register_function('glidemq_dedup', function(keys, args)\n local dedupKey = keys[1]\n local idKey = keys[2]\n local streamKey = keys[3]\n local scheduledKey = keys[4]\n local eventsKey = keys[5]\n local dedupId = args[1]\n local ttlMs = tonumber(args[2]) or 0\n local mode = args[3]\n local jobName = args[4]\n local jobData = args[5]\n local jobOpts = args[6]\n local timestamp = tonumber(args[7])\n local delay = tonumber(args[8]) or 0\n local priority = tonumber(args[9]) or 0\n local parentId = args[10] or ''\n local maxAttempts = tonumber(args[11]) or 0\n local orderingKey = args[12] or ''\n local prefix = string.sub(idKey, 1, #idKey - 2)\n local existing = redis.call('HGET', dedupKey, dedupId)\n if mode == 'simple' then\n if existing then\n local sep = string.find(existing, ':')\n if sep then\n local existingJobId = string.sub(existing, 1, sep - 1)\n local jobKey = prefix .. 'job:' .. existingJobId\n local state = redis.call('HGET', jobKey, 'state')\n if state and state ~= 'completed' and state ~= 'failed' then\n return 'skipped'\n end\n end\n end\n elseif mode == 'throttle' then\n if existing and ttlMs > 0 then\n local sep = string.find(existing, ':')\n if sep then\n local storedTs = tonumber(string.sub(existing, sep + 1))\n if storedTs and (timestamp - storedTs) < ttlMs then\n return 'skipped'\n end\n end\n end\n elseif mode == 'debounce' then\n if existing then\n local sep = string.find(existing, ':')\n if sep then\n local existingJobId = string.sub(existing, 1, sep - 1)\n local jobKey = prefix .. 'job:' .. existingJobId\n local state = redis.call('HGET', jobKey, 'state')\n if state == 'delayed' or state == 'prioritized' then\n redis.call('ZREM', scheduledKey, existingJobId)\n markOrderingDone(jobKey, existingJobId)\n redis.call('DEL', jobKey)\n emitEvent(eventsKey, 'removed', existingJobId, nil)\n elseif state and state ~= 'completed' and state ~= 'failed' then\n return 'skipped'\n end\n end\n end\n end\n local jobId = redis.call('INCR', idKey)\n local jobIdStr = tostring(jobId)\n local jobKey = prefix .. 'job:' .. jobIdStr\n local orderingSeq = 0\n if orderingKey ~= '' then\n local orderingMetaKey = prefix .. 'ordering'\n orderingSeq = redis.call('HINCRBY', orderingMetaKey, orderingKey, 1)\n end\n local hashFields = {\n 'id', jobIdStr,\n 'name', jobName,\n 'data', jobData,\n 'opts', jobOpts,\n 'timestamp', tostring(timestamp),\n 'attemptsMade', '0',\n 'delay', tostring(delay),\n 'priority', tostring(priority),\n 'maxAttempts', tostring(maxAttempts)\n }\n if orderingKey ~= '' then\n hashFields[#hashFields + 1] = 'orderingKey'\n hashFields[#hashFields + 1] = orderingKey\n hashFields[#hashFields + 1] = 'orderingSeq'\n hashFields[#hashFields + 1] = tostring(orderingSeq)\n end\n if parentId ~= '' then\n hashFields[#hashFields + 1] = 'parentId'\n hashFields[#hashFields + 1] = parentId\n end\n if delay > 0 or priority > 0 then\n hashFields[#hashFields + 1] = 'state'\n hashFields[#hashFields + 1] = delay > 0 and 'delayed' or 'prioritized'\n else\n hashFields[#hashFields + 1] = 'state'\n hashFields[#hashFields + 1] = 'waiting'\n end\n redis.call('HSET', jobKey, unpack(hashFields))\n if delay > 0 then\n local score = priority * PRIORITY_SHIFT + (timestamp + delay)\n redis.call('ZADD', scheduledKey, score, jobIdStr)\n elseif priority > 0 then\n local score = priority * PRIORITY_SHIFT\n redis.call('ZADD', scheduledKey, score, jobIdStr)\n else\n redis.call('XADD', streamKey, '*', 'jobId', jobIdStr)\n end\n redis.call('HSET', dedupKey, dedupId, jobIdStr .. ':' .. tostring(timestamp))\n emitEvent(eventsKey, 'added', jobIdStr, {'name', jobName})\n return jobIdStr\nend)\n\nredis.register_function('glidemq_rateLimit', function(keys, args)\n local rateKey = keys[1]\n local metaKey = keys[2]\n local maxPerWindow = tonumber(args[1])\n local windowDuration = tonumber(args[2])\n local now = tonumber(args[3])\n local windowStart = tonumber(redis.call('HGET', rateKey, 'windowStart')) or 0\n local count = tonumber(redis.call('HGET', rateKey, 'count')) or 0\n if now - windowStart >= windowDuration then\n redis.call('HSET', rateKey, 'windowStart', tostring(now), 'count', '1')\n return 0\n end\n if count >= maxPerWindow then\n local delayMs = windowDuration - (now - windowStart)\n return delayMs\n end\n redis.call('HSET', rateKey, 'count', tostring(count + 1))\n return 0\nend)\n\nredis.register_function('glidemq_checkConcurrency', function(keys, args)\n local metaKey = keys[1]\n local streamKey = keys[2]\n local group = args[1]\n local gc = tonumber(redis.call('HGET', metaKey, 'globalConcurrency')) or 0\n if gc <= 0 then\n return -1\n end\n local pending = redis.call('XPENDING', streamKey, group)\n local pendingCount = tonumber(pending[1]) or 0\n local remaining = gc - pendingCount\n if remaining <= 0 then\n return 0\n end\n return remaining\nend)\n\nredis.register_function('glidemq_moveToActive', function(keys, args)\n local jobKey = keys[1]\n local timestamp = args[1]\n local exists = redis.call('EXISTS', jobKey)\n if exists == 0 then\n return ''\n end\n local revoked = redis.call('HGET', jobKey, 'revoked')\n if revoked == '1' then\n return 'REVOKED'\n end\n redis.call('HSET', jobKey, 'state', 'active', 'processedOn', timestamp, 'lastActive', timestamp)\n local fields = redis.call('HGETALL', jobKey)\n return cjson.encode(fields)\nend)\n\nredis.register_function('glidemq_deferActive', function(keys, args)\n local streamKey = keys[1]\n local jobKey = keys[2]\n local jobId = args[1]\n local entryId = args[2]\n local group = args[3]\n local exists = redis.call('EXISTS', jobKey)\n redis.call('XACK', streamKey, group, entryId)\n redis.call('XDEL', streamKey, entryId)\n if exists == 0 then\n return 0\n end\n redis.call('XADD', streamKey, '*', 'jobId', jobId)\n redis.call('HSET', jobKey, 'state', 'waiting')\n return 1\nend)\n\nredis.register_function('glidemq_addFlow', function(keys, args)\n local parentIdKey = keys[1]\n local parentStreamKey = keys[2]\n local parentScheduledKey = keys[3]\n local parentEventsKey = keys[4]\n local parentName = args[1]\n local parentData = args[2]\n local parentOpts = args[3]\n local timestamp = tonumber(args[4])\n local parentDelay = tonumber(args[5]) or 0\n local parentPriority = tonumber(args[6]) or 0\n local parentMaxAttempts = tonumber(args[7]) or 0\n local numChildren = tonumber(args[8])\n local parentJobId = redis.call('INCR', parentIdKey)\n local parentJobIdStr = tostring(parentJobId)\n local parentPrefix = string.sub(parentIdKey, 1, #parentIdKey - 2)\n local parentJobKey = parentPrefix .. 'job:' .. parentJobIdStr\n local depsKey = parentPrefix .. 'deps:' .. parentJobIdStr\n local parentOrderingKey = extractOrderingKeyFromOpts(parentOpts)\n local parentOrderingSeq = 0\n if parentOrderingKey ~= '' then\n local parentOrderingMetaKey = parentPrefix .. 'ordering'\n parentOrderingSeq = redis.call('HINCRBY', parentOrderingMetaKey, parentOrderingKey, 1)\n end\n local parentHash = {\n 'id', parentJobIdStr,\n 'name', parentName,\n 'data', parentData,\n 'opts', parentOpts,\n 'timestamp', tostring(timestamp),\n 'attemptsMade', '0',\n 'delay', tostring(parentDelay),\n 'priority', tostring(parentPriority),\n 'maxAttempts', tostring(parentMaxAttempts),\n 'state', 'waiting-children'\n }\n if parentOrderingKey ~= '' then\n parentHash[#parentHash + 1] = 'orderingKey'\n parentHash[#parentHash + 1] = parentOrderingKey\n parentHash[#parentHash + 1] = 'orderingSeq'\n parentHash[#parentHash + 1] = tostring(parentOrderingSeq)\n end\n redis.call('HSET', parentJobKey, unpack(parentHash))\n local childIds = {}\n local childArgOffset = 8\n local childKeyOffset = 4\n for i = 1, numChildren do\n local base = childArgOffset + (i - 1) * 8\n local childName = args[base + 1]\n local childData = args[base + 2]\n local childOpts = args[base + 3]\n local childDelay = tonumber(args[base + 4]) or 0\n local childPriority = tonumber(args[base + 5]) or 0\n local childMaxAttempts = tonumber(args[base + 6]) or 0\n local childQueuePrefix = args[base + 7]\n local childParentQueue = args[base + 8]\n local ckBase = childKeyOffset + (i - 1) * 4\n local childIdKey = keys[ckBase + 1]\n local childStreamKey = keys[ckBase + 2]\n local childScheduledKey = keys[ckBase + 3]\n local childEventsKey = keys[ckBase + 4]\n local childJobId = redis.call('INCR', childIdKey)\n local childJobIdStr = tostring(childJobId)\n local childPrefix = string.sub(childIdKey, 1, #childIdKey - 2)\n local childJobKey = childPrefix .. 'job:' .. childJobIdStr\n local childOrderingKey = extractOrderingKeyFromOpts(childOpts)\n local childOrderingSeq = 0\n if childOrderingKey ~= '' then\n local childOrderingMetaKey = childPrefix .. 'ordering'\n childOrderingSeq = redis.call('HINCRBY', childOrderingMetaKey, childOrderingKey, 1)\n end\n local childHash = {\n 'id', childJobIdStr,\n 'name', childName,\n 'data', childData,\n 'opts', childOpts,\n 'timestamp', tostring(timestamp),\n 'attemptsMade', '0',\n 'delay', tostring(childDelay),\n 'priority', tostring(childPriority),\n 'maxAttempts', tostring(childMaxAttempts),\n 'parentId', parentJobIdStr,\n 'parentQueue', childParentQueue\n }\n if childOrderingKey ~= '' then\n childHash[#childHash + 1] = 'orderingKey'\n childHash[#childHash + 1] = childOrderingKey\n childHash[#childHash + 1] = 'orderingSeq'\n childHash[#childHash + 1] = tostring(childOrderingSeq)\n end\n if childDelay > 0 or childPriority > 0 then\n childHash[#childHash + 1] = 'state'\n childHash[#childHash + 1] = childDelay > 0 and 'delayed' or 'prioritized'\n else\n childHash[#childHash + 1] = 'state'\n childHash[#childHash + 1] = 'waiting'\n end\n redis.call('HSET', childJobKey, unpack(childHash))\n local depsMember = childQueuePrefix .. ':' .. childJobIdStr\n redis.call('SADD', depsKey, depsMember)\n if childDelay > 0 then\n local score = childPriority * PRIORITY_SHIFT + (timestamp + childDelay)\n redis.call('ZADD', childScheduledKey, score, childJobIdStr)\n elseif childPriority > 0 then\n local score = childPriority * PRIORITY_SHIFT\n redis.call('ZADD', childScheduledKey, score, childJobIdStr)\n else\n redis.call('XADD', childStreamKey, '*', 'jobId', childJobIdStr)\n end\n emitEvent(childEventsKey, 'added', childJobIdStr, {'name', childName})\n childIds[#childIds + 1] = childJobIdStr\n end\n local extraDepsOffset = childArgOffset + numChildren * 8\n local numExtraDeps = tonumber(args[extraDepsOffset + 1]) or 0\n for i = 1, numExtraDeps do\n local extraMember = args[extraDepsOffset + 1 + i]\n redis.call('SADD', depsKey, extraMember)\n end\n emitEvent(parentEventsKey, 'added', parentJobIdStr, {'name', parentName})\n local result = {parentJobIdStr}\n for i = 1, #childIds do\n result[#result + 1] = childIds[i]\n end\n return cjson.encode(result)\nend)\n\nredis.register_function('glidemq_completeChild', function(keys, args)\n local depsKey = keys[1]\n local parentJobKey = keys[2]\n local parentStreamKey = keys[3]\n local parentEventsKey = keys[4]\n local depsMember = args[1]\n local parentId = args[2]\n local doneCount = redis.call('HINCRBY', parentJobKey, 'depsCompleted', 1)\n local totalDeps = redis.call('SCARD', depsKey)\n local remaining = totalDeps - doneCount\n if remaining <= 0 then\n redis.call('HSET', parentJobKey, 'state', 'waiting')\n redis.call('XADD', parentStreamKey, '*', 'jobId', parentId)\n emitEvent(parentEventsKey, 'active', parentId, nil)\n end\n return remaining\nend)\n\nredis.register_function('glidemq_removeJob', function(keys, args)\n local jobKey = keys[1]\n local streamKey = keys[2]\n local scheduledKey = keys[3]\n local completedKey = keys[4]\n local failedKey = keys[5]\n local eventsKey = keys[6]\n local logKey = keys[7]\n local jobId = args[1]\n local exists = redis.call('EXISTS', jobKey)\n if exists == 0 then\n return 0\n end\n redis.call('ZREM', scheduledKey, jobId)\n redis.call('ZREM', completedKey, jobId)\n redis.call('ZREM', failedKey, jobId)\n markOrderingDone(jobKey, jobId)\n redis.call('DEL', jobKey)\n redis.call('DEL', logKey)\n emitEvent(eventsKey, 'removed', jobId, nil)\n return 1\nend)\n\nredis.register_function('glidemq_revoke', function(keys, args)\n local jobKey = keys[1]\n local streamKey = keys[2]\n local scheduledKey = keys[3]\n local failedKey = keys[4]\n local eventsKey = keys[5]\n local jobId = args[1]\n local timestamp = tonumber(args[2])\n local group = args[3]\n local exists = redis.call('EXISTS', jobKey)\n if exists == 0 then\n return 'not_found'\n end\n redis.call('HSET', jobKey, 'revoked', '1')\n local state = redis.call('HGET', jobKey, 'state')\n if state == 'waiting' or state == 'delayed' or state == 'prioritized' then\n redis.call('ZREM', scheduledKey, jobId)\n local entries = redis.call('XRANGE', streamKey, '-', '+')\n for i = 1, #entries do\n local entryId = entries[i][1]\n local fields = entries[i][2]\n for j = 1, #fields, 2 do\n if fields[j] == 'jobId' and fields[j+1] == jobId then\n redis.call('XACK', streamKey, group, entryId)\n redis.call('XDEL', streamKey, entryId)\n break\n end\n end\n end\n redis.call('ZADD', failedKey, timestamp, jobId)\n redis.call('HSET', jobKey,\n 'state', 'failed',\n 'failedReason', 'revoked',\n 'finishedOn', tostring(timestamp)\n )\n markOrderingDone(jobKey, jobId)\n emitEvent(eventsKey, 'revoked', jobId, nil)\n return 'revoked'\n end\n emitEvent(eventsKey, 'revoked', jobId, nil)\n return 'flagged'\nend)\n\nredis.register_function('glidemq_searchByName', function(keys, args)\n local stateKey = keys[1]\n local stateType = args[1]\n local nameFilter = args[2]\n local limit = tonumber(args[3]) or 100\n local prefix = args[4]\n local matched = {}\n if stateType == 'zset' then\n local members = redis.call('ZRANGE', stateKey, 0, -1)\n for i = 1, #members do\n if #matched >= limit then break end\n local jobId = members[i]\n local jobKey = prefix .. 'job:' .. jobId\n local name = redis.call('HGET', jobKey, 'name')\n if name == nameFilter then\n matched[#matched + 1] = jobId\n end\n end\n elseif stateType == 'stream' then\n local entries = redis.call('XRANGE', stateKey, '-', '+')\n for i = 1, #entries do\n if #matched >= limit then break end\n local fields = entries[i][2]\n local jobId = nil\n for j = 1, #fields, 2 do\n if fields[j] == 'jobId' then\n jobId = fields[j + 1]\n break\n end\n end\n if jobId then\n local jobKey = prefix .. 'job:' .. jobId\n local name = redis.call('HGET', jobKey, 'name')\n if name == nameFilter then\n matched[#matched + 1] = jobId\n end\n end\n end\n end\n return matched\nend)\n";
7
7
  export type QueueKeys = ReturnType<typeof import('../utils').buildKeys>;
8
8
  /**
9
9
  * Add a job to the queue atomically.
10
10
  * Returns the new job ID (string).
11
11
  */
12
- export declare function addJob(client: Client, k: QueueKeys, jobName: string, data: string, opts: string, timestamp: number, delay: number, priority: number, parentId: string, maxAttempts: number): Promise<string>;
12
+ export declare function addJob(client: Client, k: QueueKeys, jobName: string, data: string, opts: string, timestamp: number, delay: number, priority: number, parentId: string, maxAttempts: number, orderingKey?: string): Promise<string>;
13
13
  /**
14
14
  * Add a job with deduplication. Checks the dedup hash and either skips or adds the job.
15
15
  * Returns "skipped" if deduplicated, otherwise the new job ID (string).
16
16
  */
17
- export declare function dedup(client: Client, k: QueueKeys, dedupId: string, ttlMs: number, mode: string, jobName: string, data: string, opts: string, timestamp: number, delay: number, priority: number, parentId: string, maxAttempts: number): Promise<string>;
17
+ export declare function dedup(client: Client, k: QueueKeys, dedupId: string, ttlMs: number, mode: string, jobName: string, data: string, opts: string, timestamp: number, delay: number, priority: number, parentId: string, maxAttempts: number, orderingKey?: string): Promise<string>;
18
18
  /**
19
19
  * Promote delayed/prioritized jobs whose score <= now from scheduled ZSet to stream.
20
20
  * Returns the number of jobs promoted.
@@ -101,6 +101,12 @@ export declare function checkConcurrency(client: Client, k: QueueKeys, group?: s
101
101
  * Replaces: HGETALL + revoked check + HSET lastActive (3 round trips -> 1)
102
102
  */
103
103
  export declare function moveToActive(client: Client, k: QueueKeys, jobId: string, timestamp: number): Promise<Record<string, string> | 'REVOKED' | null>;
104
+ /**
105
+ * Defers an active job back to waiting by acknowledging + deleting the current
106
+ * stream entry and re-enqueuing the same jobId to the stream tail.
107
+ * If the job hash no longer exists, it only removes the stream entry.
108
+ */
109
+ export declare function deferActive(client: Client, k: QueueKeys, jobId: string, entryId: string, group?: string): Promise<void>;
104
110
  /**
105
111
  * Remove a job from all data structures (hash, stream, scheduled, completed, failed).
106
112
  * Returns 1 if removed, 0 if not found.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/functions/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/functions/index.ts"],"names":[],"mappings":"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"}
@@ -13,13 +13,14 @@ exports.resume = resume;
13
13
  exports.rateLimit = rateLimit;
14
14
  exports.checkConcurrency = checkConcurrency;
15
15
  exports.moveToActive = moveToActive;
16
+ exports.deferActive = deferActive;
16
17
  exports.removeJob = removeJob;
17
18
  exports.revokeJob = revokeJob;
18
19
  exports.searchByName = searchByName;
19
20
  exports.addFlow = addFlow;
20
21
  exports.completeChild = completeChild;
21
22
  exports.LIBRARY_NAME = 'glidemq';
22
- exports.LIBRARY_VERSION = '9';
23
+ exports.LIBRARY_VERSION = '15';
23
24
  // Consumer group name used by workers
24
25
  exports.CONSUMER_GROUP = 'workers';
25
26
  // Embedded Lua library source (from glidemq.lua)
@@ -39,6 +40,61 @@ local function emitEvent(eventsKey, eventType, jobId, extraFields)
39
40
  redis.call('XADD', eventsKey, 'MAXLEN', '~', '1000', '*', unpack(fields))
40
41
  end
41
42
 
43
+ local function markOrderingDone(jobKey, jobId)
44
+ local orderingKey = redis.call('HGET', jobKey, 'orderingKey')
45
+ if not orderingKey or orderingKey == '' then
46
+ return
47
+ end
48
+ local orderingSeq = tonumber(redis.call('HGET', jobKey, 'orderingSeq')) or 0
49
+ if orderingSeq <= 0 then
50
+ return
51
+ end
52
+
53
+ local prefix = string.sub(jobKey, 1, #jobKey - #('job:' .. jobId))
54
+ local metaKey = prefix .. 'meta'
55
+ local doneField = 'orderdone:' .. orderingKey
56
+ local pendingKey = prefix .. 'orderdone:pending:' .. orderingKey
57
+
58
+ local lastDone = tonumber(redis.call('HGET', metaKey, doneField)) or 0
59
+ if orderingSeq <= lastDone then
60
+ redis.call('HDEL', pendingKey, tostring(orderingSeq))
61
+ return
62
+ end
63
+
64
+ redis.call('HSET', pendingKey, tostring(orderingSeq), '1')
65
+ local advanced = lastDone
66
+ while true do
67
+ local nextSeq = advanced + 1
68
+ if redis.call('HEXISTS', pendingKey, tostring(nextSeq)) == 0 then
69
+ break
70
+ end
71
+ redis.call('HDEL', pendingKey, tostring(nextSeq))
72
+ advanced = nextSeq
73
+ end
74
+ if advanced > lastDone then
75
+ redis.call('HSET', metaKey, doneField, tostring(advanced))
76
+ end
77
+ end
78
+
79
+ local function extractOrderingKeyFromOpts(optsJson)
80
+ if not optsJson or optsJson == '' then
81
+ return ''
82
+ end
83
+ local ok, decoded = pcall(cjson.decode, optsJson)
84
+ if not ok or type(decoded) ~= 'table' then
85
+ return ''
86
+ end
87
+ local ordering = decoded['ordering']
88
+ if type(ordering) ~= 'table' then
89
+ return ''
90
+ end
91
+ local key = ordering['key']
92
+ if key == nil then
93
+ return ''
94
+ end
95
+ return tostring(key)
96
+ end
97
+
42
98
  redis.register_function('glidemq_version', function(keys, args)
43
99
  return '${exports.LIBRARY_VERSION}'
44
100
  end)
@@ -56,10 +112,16 @@ redis.register_function('glidemq_addJob', function(keys, args)
56
112
  local priority = tonumber(args[6]) or 0
57
113
  local parentId = args[7] or ''
58
114
  local maxAttempts = tonumber(args[8]) or 0
115
+ local orderingKey = args[9] or ''
59
116
  local jobId = redis.call('INCR', idKey)
60
117
  local jobIdStr = tostring(jobId)
61
118
  local prefix = string.sub(idKey, 1, #idKey - 2)
62
119
  local jobKey = prefix .. 'job:' .. jobIdStr
120
+ local orderingSeq = 0
121
+ if orderingKey ~= '' then
122
+ local orderingMetaKey = prefix .. 'ordering'
123
+ orderingSeq = redis.call('HINCRBY', orderingMetaKey, orderingKey, 1)
124
+ end
63
125
  local hashFields = {
64
126
  'id', jobIdStr,
65
127
  'name', jobName,
@@ -71,6 +133,12 @@ redis.register_function('glidemq_addJob', function(keys, args)
71
133
  'priority', tostring(priority),
72
134
  'maxAttempts', tostring(maxAttempts)
73
135
  }
136
+ if orderingKey ~= '' then
137
+ hashFields[#hashFields + 1] = 'orderingKey'
138
+ hashFields[#hashFields + 1] = orderingKey
139
+ hashFields[#hashFields + 1] = 'orderingSeq'
140
+ hashFields[#hashFields + 1] = tostring(orderingSeq)
141
+ end
74
142
  if parentId ~= '' then
75
143
  hashFields[#hashFields + 1] = 'parentId'
76
144
  hashFields[#hashFields + 1] = parentId
@@ -101,10 +169,11 @@ redis.register_function('glidemq_promote', function(keys, args)
101
169
  local streamKey = keys[2]
102
170
  local eventsKey = keys[3]
103
171
  local now = tonumber(args[1])
172
+ local MAX_PROMOTIONS = 1000
104
173
  local count = 0
105
174
  local cursorMin = 0
106
- while true do
107
- local nextEntry = redis.call('ZRANGEBYSCORE', scheduledKey, tostring(cursorMin), '+inf', 'WITHSCORES', 'LIMIT', 0, 1)
175
+ while count < MAX_PROMOTIONS do
176
+ local nextEntry = redis.call('ZRANGEBYSCORE', scheduledKey, string.format('%.0f', cursorMin), '+inf', 'WITHSCORES', 'LIMIT', 0, 1)
108
177
  if not nextEntry or #nextEntry == 0 then
109
178
  break
110
179
  end
@@ -112,7 +181,16 @@ redis.register_function('glidemq_promote', function(keys, args)
112
181
  local priority = math.floor(firstScore / PRIORITY_SHIFT)
113
182
  local minScore = priority * PRIORITY_SHIFT
114
183
  local maxDueScore = minScore + now
115
- local members = redis.call('ZRANGEBYSCORE', scheduledKey, tostring(minScore), tostring(maxDueScore))
184
+ local remaining = MAX_PROMOTIONS - count
185
+ local members = redis.call(
186
+ 'ZRANGEBYSCORE',
187
+ scheduledKey,
188
+ string.format('%.0f', minScore),
189
+ string.format('%.0f', maxDueScore),
190
+ 'LIMIT',
191
+ 0,
192
+ remaining
193
+ )
116
194
  for i = 1, #members do
117
195
  local jobId = members[i]
118
196
  redis.call('XADD', streamKey, '*', 'jobId', jobId)
@@ -151,6 +229,7 @@ redis.register_function('glidemq_complete', function(keys, args)
151
229
  'returnvalue', returnvalue,
152
230
  'finishedOn', tostring(timestamp)
153
231
  )
232
+ markOrderingDone(jobKey, jobId)
154
233
  emitEvent(eventsKey, 'completed', jobId, {'returnvalue', returnvalue})
155
234
  local prefix = string.sub(jobKey, 1, #jobKey - #('job:' .. jobId))
156
235
  if removeMode == 'true' then
@@ -231,6 +310,7 @@ redis.register_function('glidemq_completeAndFetchNext', function(keys, args)
231
310
  'returnvalue', returnvalue,
232
311
  'finishedOn', tostring(timestamp)
233
312
  )
313
+ markOrderingDone(jobKey, jobId)
234
314
  emitEvent(eventsKey, 'completed', jobId, {'returnvalue', returnvalue})
235
315
  local prefix = string.sub(jobKey, 1, #jobKey - #('job:' .. jobId))
236
316
 
@@ -346,6 +426,7 @@ redis.register_function('glidemq_fail', function(keys, args)
346
426
  'finishedOn', tostring(timestamp),
347
427
  'processedOn', tostring(timestamp)
348
428
  )
429
+ markOrderingDone(jobKey, jobId)
349
430
  emitEvent(eventsKey, 'failed', jobId, {'failedReason', failedReason})
350
431
  local prefix = string.sub(jobKey, 1, #jobKey - #('job:' .. jobId))
351
432
  if removeMode == 'true' then
@@ -432,6 +513,7 @@ redis.register_function('glidemq_reclaimStalled', function(keys, args)
432
513
  'failedReason', 'job stalled more than maxStalledCount',
433
514
  'finishedOn', tostring(timestamp)
434
515
  )
516
+ markOrderingDone(jobKey, jobId)
435
517
  emitEvent(eventsKey, 'failed', jobId, {
436
518
  'failedReason', 'job stalled more than maxStalledCount'
437
519
  })
@@ -479,6 +561,7 @@ redis.register_function('glidemq_dedup', function(keys, args)
479
561
  local priority = tonumber(args[9]) or 0
480
562
  local parentId = args[10] or ''
481
563
  local maxAttempts = tonumber(args[11]) or 0
564
+ local orderingKey = args[12] or ''
482
565
  local prefix = string.sub(idKey, 1, #idKey - 2)
483
566
  local existing = redis.call('HGET', dedupKey, dedupId)
484
567
  if mode == 'simple' then
@@ -512,6 +595,7 @@ redis.register_function('glidemq_dedup', function(keys, args)
512
595
  local state = redis.call('HGET', jobKey, 'state')
513
596
  if state == 'delayed' or state == 'prioritized' then
514
597
  redis.call('ZREM', scheduledKey, existingJobId)
598
+ markOrderingDone(jobKey, existingJobId)
515
599
  redis.call('DEL', jobKey)
516
600
  emitEvent(eventsKey, 'removed', existingJobId, nil)
517
601
  elseif state and state ~= 'completed' and state ~= 'failed' then
@@ -523,6 +607,11 @@ redis.register_function('glidemq_dedup', function(keys, args)
523
607
  local jobId = redis.call('INCR', idKey)
524
608
  local jobIdStr = tostring(jobId)
525
609
  local jobKey = prefix .. 'job:' .. jobIdStr
610
+ local orderingSeq = 0
611
+ if orderingKey ~= '' then
612
+ local orderingMetaKey = prefix .. 'ordering'
613
+ orderingSeq = redis.call('HINCRBY', orderingMetaKey, orderingKey, 1)
614
+ end
526
615
  local hashFields = {
527
616
  'id', jobIdStr,
528
617
  'name', jobName,
@@ -534,6 +623,12 @@ redis.register_function('glidemq_dedup', function(keys, args)
534
623
  'priority', tostring(priority),
535
624
  'maxAttempts', tostring(maxAttempts)
536
625
  }
626
+ if orderingKey ~= '' then
627
+ hashFields[#hashFields + 1] = 'orderingKey'
628
+ hashFields[#hashFields + 1] = orderingKey
629
+ hashFields[#hashFields + 1] = 'orderingSeq'
630
+ hashFields[#hashFields + 1] = tostring(orderingSeq)
631
+ end
537
632
  if parentId ~= '' then
538
633
  hashFields[#hashFields + 1] = 'parentId'
539
634
  hashFields[#hashFields + 1] = parentId
@@ -613,6 +708,23 @@ redis.register_function('glidemq_moveToActive', function(keys, args)
613
708
  return cjson.encode(fields)
614
709
  end)
615
710
 
711
+ redis.register_function('glidemq_deferActive', function(keys, args)
712
+ local streamKey = keys[1]
713
+ local jobKey = keys[2]
714
+ local jobId = args[1]
715
+ local entryId = args[2]
716
+ local group = args[3]
717
+ local exists = redis.call('EXISTS', jobKey)
718
+ redis.call('XACK', streamKey, group, entryId)
719
+ redis.call('XDEL', streamKey, entryId)
720
+ if exists == 0 then
721
+ return 0
722
+ end
723
+ redis.call('XADD', streamKey, '*', 'jobId', jobId)
724
+ redis.call('HSET', jobKey, 'state', 'waiting')
725
+ return 1
726
+ end)
727
+
616
728
  redis.register_function('glidemq_addFlow', function(keys, args)
617
729
  local parentIdKey = keys[1]
618
730
  local parentStreamKey = keys[2]
@@ -631,6 +743,12 @@ redis.register_function('glidemq_addFlow', function(keys, args)
631
743
  local parentPrefix = string.sub(parentIdKey, 1, #parentIdKey - 2)
632
744
  local parentJobKey = parentPrefix .. 'job:' .. parentJobIdStr
633
745
  local depsKey = parentPrefix .. 'deps:' .. parentJobIdStr
746
+ local parentOrderingKey = extractOrderingKeyFromOpts(parentOpts)
747
+ local parentOrderingSeq = 0
748
+ if parentOrderingKey ~= '' then
749
+ local parentOrderingMetaKey = parentPrefix .. 'ordering'
750
+ parentOrderingSeq = redis.call('HINCRBY', parentOrderingMetaKey, parentOrderingKey, 1)
751
+ end
634
752
  local parentHash = {
635
753
  'id', parentJobIdStr,
636
754
  'name', parentName,
@@ -643,6 +761,12 @@ redis.register_function('glidemq_addFlow', function(keys, args)
643
761
  'maxAttempts', tostring(parentMaxAttempts),
644
762
  'state', 'waiting-children'
645
763
  }
764
+ if parentOrderingKey ~= '' then
765
+ parentHash[#parentHash + 1] = 'orderingKey'
766
+ parentHash[#parentHash + 1] = parentOrderingKey
767
+ parentHash[#parentHash + 1] = 'orderingSeq'
768
+ parentHash[#parentHash + 1] = tostring(parentOrderingSeq)
769
+ end
646
770
  redis.call('HSET', parentJobKey, unpack(parentHash))
647
771
  local childIds = {}
648
772
  local childArgOffset = 8
@@ -666,6 +790,12 @@ redis.register_function('glidemq_addFlow', function(keys, args)
666
790
  local childJobIdStr = tostring(childJobId)
667
791
  local childPrefix = string.sub(childIdKey, 1, #childIdKey - 2)
668
792
  local childJobKey = childPrefix .. 'job:' .. childJobIdStr
793
+ local childOrderingKey = extractOrderingKeyFromOpts(childOpts)
794
+ local childOrderingSeq = 0
795
+ if childOrderingKey ~= '' then
796
+ local childOrderingMetaKey = childPrefix .. 'ordering'
797
+ childOrderingSeq = redis.call('HINCRBY', childOrderingMetaKey, childOrderingKey, 1)
798
+ end
669
799
  local childHash = {
670
800
  'id', childJobIdStr,
671
801
  'name', childName,
@@ -679,6 +809,12 @@ redis.register_function('glidemq_addFlow', function(keys, args)
679
809
  'parentId', parentJobIdStr,
680
810
  'parentQueue', childParentQueue
681
811
  }
812
+ if childOrderingKey ~= '' then
813
+ childHash[#childHash + 1] = 'orderingKey'
814
+ childHash[#childHash + 1] = childOrderingKey
815
+ childHash[#childHash + 1] = 'orderingSeq'
816
+ childHash[#childHash + 1] = tostring(childOrderingSeq)
817
+ end
682
818
  if childDelay > 0 or childPriority > 0 then
683
819
  childHash[#childHash + 1] = 'state'
684
820
  childHash[#childHash + 1] = childDelay > 0 and 'delayed' or 'prioritized'
@@ -749,6 +885,7 @@ redis.register_function('glidemq_removeJob', function(keys, args)
749
885
  redis.call('ZREM', scheduledKey, jobId)
750
886
  redis.call('ZREM', completedKey, jobId)
751
887
  redis.call('ZREM', failedKey, jobId)
888
+ markOrderingDone(jobKey, jobId)
752
889
  redis.call('DEL', jobKey)
753
890
  redis.call('DEL', logKey)
754
891
  emitEvent(eventsKey, 'removed', jobId, nil)
@@ -790,6 +927,7 @@ redis.register_function('glidemq_revoke', function(keys, args)
790
927
  'failedReason', 'revoked',
791
928
  'finishedOn', tostring(timestamp)
792
929
  )
930
+ markOrderingDone(jobKey, jobId)
793
931
  emitEvent(eventsKey, 'revoked', jobId, nil)
794
932
  return 'revoked'
795
933
  end
@@ -844,7 +982,7 @@ end)
844
982
  * Add a job to the queue atomically.
845
983
  * Returns the new job ID (string).
846
984
  */
847
- async function addJob(client, k, jobName, data, opts, timestamp, delay, priority, parentId, maxAttempts) {
985
+ async function addJob(client, k, jobName, data, opts, timestamp, delay, priority, parentId, maxAttempts, orderingKey = '') {
848
986
  const result = await client.fcall('glidemq_addJob', [k.id, k.stream, k.scheduled, k.events], [
849
987
  jobName,
850
988
  data,
@@ -854,6 +992,7 @@ async function addJob(client, k, jobName, data, opts, timestamp, delay, priority
854
992
  priority.toString(),
855
993
  parentId,
856
994
  maxAttempts.toString(),
995
+ orderingKey,
857
996
  ]);
858
997
  return result;
859
998
  }
@@ -861,7 +1000,7 @@ async function addJob(client, k, jobName, data, opts, timestamp, delay, priority
861
1000
  * Add a job with deduplication. Checks the dedup hash and either skips or adds the job.
862
1001
  * Returns "skipped" if deduplicated, otherwise the new job ID (string).
863
1002
  */
864
- async function dedup(client, k, dedupId, ttlMs, mode, jobName, data, opts, timestamp, delay, priority, parentId, maxAttempts) {
1003
+ async function dedup(client, k, dedupId, ttlMs, mode, jobName, data, opts, timestamp, delay, priority, parentId, maxAttempts, orderingKey = '') {
865
1004
  const result = await client.fcall('glidemq_dedup', [k.dedup, k.id, k.stream, k.scheduled, k.events], [
866
1005
  dedupId,
867
1006
  ttlMs.toString(),
@@ -874,6 +1013,7 @@ async function dedup(client, k, dedupId, ttlMs, mode, jobName, data, opts, times
874
1013
  priority.toString(),
875
1014
  parentId,
876
1015
  maxAttempts.toString(),
1016
+ orderingKey,
877
1017
  ]);
878
1018
  return result;
879
1019
  }
@@ -1055,6 +1195,14 @@ async function moveToActive(client, k, jobId, timestamp) {
1055
1195
  }
1056
1196
  return hash;
1057
1197
  }
1198
+ /**
1199
+ * Defers an active job back to waiting by acknowledging + deleting the current
1200
+ * stream entry and re-enqueuing the same jobId to the stream tail.
1201
+ * If the job hash no longer exists, it only removes the stream entry.
1202
+ */
1203
+ async function deferActive(client, k, jobId, entryId, group = exports.CONSUMER_GROUP) {
1204
+ await client.fcall('glidemq_deferActive', [k.stream, k.job(jobId)], [jobId, entryId, group]);
1205
+ }
1058
1206
  /**
1059
1207
  * Remove a job from all data structures (hash, stream, scheduled, completed, failed).
1060
1208
  * Returns 1 if removed, 0 if not found.