@getstrata/core 2.2.13 → 2.2.15
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.
- package/CHANGELOG.md +41 -14
- package/dist/core/database/baseRepository.d.ts +1 -0
- package/dist/core/database/defaultConnection.d.ts +2 -1
- package/dist/core/database/relationQuery.d.ts +4 -0
- package/dist/core/http/index.d.ts +2 -2
- package/dist/core/http/route.d.ts +64 -0
- package/dist/core/queue/redisQueue.d.ts +5 -1
- package/dist/core/queue/redisStreams.d.ts +8 -2
- package/dist/core/view/index.d.ts +1 -1
- package/dist/core/view/viewRenderer.d.ts +7 -1
- package/dist/entries/http/corsMiddleware.js +10 -1
- package/dist/entries/http/route.js +45 -1
- package/dist/entries/queue/createAppQueue.js +202 -92
- package/dist/entries/queue/publicQueue.js +202 -92
- package/dist/entries/queue/queueMetrics.js +202 -92
- package/dist/entries/queue/redisQueue.js +203 -92
- package/dist/framework/public-api.d.ts +4 -4
- package/dist/index.js +430 -144
- package/package.json +1 -1
|
@@ -1324,21 +1324,6 @@ redis.call('XDEL', KEYS[1], ARGV[2])
|
|
|
1324
1324
|
if #redis.call('XPENDING', KEYS[1], ARGV[1], '-', '+', 1, ARGV[3]) == 0 then redis.call('XGROUP', 'DELCONSUMER', KEYS[1], ARGV[1], ARGV[3]) end
|
|
1325
1325
|
return 1
|
|
1326
1326
|
`;
|
|
1327
|
-
var PROMOTE_RETRIES = `
|
|
1328
|
-
local kinds = {'stream','zset'}
|
|
1329
|
-
for i=1,2 do
|
|
1330
|
-
local kind = redis.call('TYPE', KEYS[i]).ok
|
|
1331
|
-
if kind ~= 'none' and kind ~= kinds[i] then return redis.error_reply('Invalid stream retry promotion key type') end
|
|
1332
|
-
end
|
|
1333
|
-
local now = redis.call('TIME')
|
|
1334
|
-
local due = tonumber(now[1])*1000 + math.floor(tonumber(now[2])/1000)
|
|
1335
|
-
local entries = redis.call('ZRANGE', KEYS[2], '-inf', due, 'BYSCORE', 'LIMIT', 0, 100)
|
|
1336
|
-
for _, payload in ipairs(entries) do
|
|
1337
|
-
redis.call('XADD', KEYS[1], '*', 'payload', payload)
|
|
1338
|
-
redis.call('ZREM', KEYS[2], payload)
|
|
1339
|
-
end
|
|
1340
|
-
return #entries
|
|
1341
|
-
`;
|
|
1342
1327
|
async function scheduleStreamRetry(client, key, reservation, envelope, delayMs) {
|
|
1343
1328
|
if (!Number.isSafeInteger(delayMs) || delayMs < 0 || delayMs > Number.MAX_SAFE_INTEGER / 2)
|
|
1344
1329
|
throw new Error("Invalid Streams retry delay");
|
|
@@ -1360,8 +1345,93 @@ async function scheduleStreamRetry(client, key, reservation, envelope, delayMs)
|
|
|
1360
1345
|
if (Number(scheduled) !== 1)
|
|
1361
1346
|
throw new Error("Queue lease lost while scheduling retry");
|
|
1362
1347
|
}
|
|
1363
|
-
|
|
1364
|
-
|
|
1348
|
+
var POLL_STREAM_PRIORITIES = `
|
|
1349
|
+
local group = ARGV[1]
|
|
1350
|
+
local owner = ARGV[2]
|
|
1351
|
+
local visibility = ARGV[3]
|
|
1352
|
+
local function payloadOf(fields)
|
|
1353
|
+
for index = 1, #fields, 2 do
|
|
1354
|
+
if fields[index] == 'payload' and type(fields[index + 1]) == 'string' then return fields[index + 1] end
|
|
1355
|
+
end
|
|
1356
|
+
return ''
|
|
1357
|
+
end
|
|
1358
|
+
local cursors = {}
|
|
1359
|
+
for i = 1, #KEYS / 2 do cursors[i] = ARGV[3 + i] end
|
|
1360
|
+
for i = 1, #KEYS / 2 do
|
|
1361
|
+
local stream = KEYS[(i - 1) * 2 + 1]
|
|
1362
|
+
local retries = KEYS[(i - 1) * 2 + 2]
|
|
1363
|
+
local cursor = ARGV[3 + i]
|
|
1364
|
+
local streamKind = redis.call('TYPE', stream).ok
|
|
1365
|
+
local retryKind = redis.call('TYPE', retries).ok
|
|
1366
|
+
if streamKind ~= 'none' and streamKind ~= 'stream' then return redis.error_reply('Invalid stream retry promotion key type') end
|
|
1367
|
+
if retryKind ~= 'none' and retryKind ~= 'zset' then return redis.error_reply('Invalid stream retry promotion key type') end
|
|
1368
|
+
if streamKind == 'stream' then
|
|
1369
|
+
local now = redis.call('TIME')
|
|
1370
|
+
local due = tonumber(now[1]) * 1000 + math.floor(tonumber(now[2]) / 1000)
|
|
1371
|
+
local ready = redis.call('ZRANGE', retries, '-inf', due, 'BYSCORE', 'LIMIT', 0, 100)
|
|
1372
|
+
for _, payload in ipairs(ready) do
|
|
1373
|
+
redis.call('XADD', stream, '*', 'payload', payload)
|
|
1374
|
+
redis.call('ZREM', retries, payload)
|
|
1375
|
+
end
|
|
1376
|
+
local window = redis.call('XPENDING', stream, group, cursor, '+', 10)
|
|
1377
|
+
local claimed = redis.call('XAUTOCLAIM', stream, group, owner, visibility, cursor, 'COUNT', 1)
|
|
1378
|
+
for _, entry in ipairs(window) do
|
|
1379
|
+
if entry[2] ~= owner and #redis.call('XPENDING', stream, group, '-', '+', 1, entry[2]) == 0 then
|
|
1380
|
+
redis.call('XGROUP', 'DELCONSUMER', stream, group, entry[2])
|
|
1381
|
+
end
|
|
1382
|
+
end
|
|
1383
|
+
if #redis.call('XPENDING', stream, group, '-', '+', 1, owner) == 0 then
|
|
1384
|
+
redis.call('XGROUP', 'DELCONSUMER', stream, group, owner)
|
|
1385
|
+
end
|
|
1386
|
+
cursors[i] = claimed[1]
|
|
1387
|
+
if #claimed[2] > 0 then
|
|
1388
|
+
return {'1', tostring(i), claimed[2][1][1], payloadOf(claimed[2][1][2]), unpack(cursors)}
|
|
1389
|
+
end
|
|
1390
|
+
local fresh = redis.call('XREADGROUP', 'GROUP', group, owner, 'COUNT', 1, 'STREAMS', stream, '>')
|
|
1391
|
+
if not fresh and #redis.call('XPENDING', stream, group, '-', '+', 1, owner) == 0 then
|
|
1392
|
+
redis.call('XGROUP', 'DELCONSUMER', stream, group, owner)
|
|
1393
|
+
end
|
|
1394
|
+
if fresh and #fresh > 0 and #fresh[1][2] > 0 then
|
|
1395
|
+
return {'1', tostring(i), fresh[1][2][1][1], payloadOf(fresh[1][2][1][2]), unpack(cursors)}
|
|
1396
|
+
end
|
|
1397
|
+
end
|
|
1398
|
+
end
|
|
1399
|
+
return {'0', unpack(cursors)}
|
|
1400
|
+
`;
|
|
1401
|
+
async function pollStreamPriorities(client, queueKeys, owner, cursors) {
|
|
1402
|
+
const keys = [];
|
|
1403
|
+
for (const key of queueKeys)
|
|
1404
|
+
keys.push(streamQueueKey(key), streamRetryKey(key));
|
|
1405
|
+
const raw = await client.send("EVAL", [
|
|
1406
|
+
POLL_STREAM_PRIORITIES,
|
|
1407
|
+
String(keys.length),
|
|
1408
|
+
...keys,
|
|
1409
|
+
STREAM_GROUP,
|
|
1410
|
+
owner,
|
|
1411
|
+
String(readQueueVisibilityMs()),
|
|
1412
|
+
...cursors
|
|
1413
|
+
]);
|
|
1414
|
+
if (!Array.isArray(raw))
|
|
1415
|
+
throw new Error("Invalid Redis stream poll response");
|
|
1416
|
+
const flag = String(raw[0]);
|
|
1417
|
+
const cursorCount = queueKeys.length;
|
|
1418
|
+
if (flag === "0") {
|
|
1419
|
+
if (raw.length !== cursorCount + 1)
|
|
1420
|
+
throw new Error("Invalid Redis stream poll response");
|
|
1421
|
+
return { cursors: raw.slice(1).map(String), hit: null };
|
|
1422
|
+
}
|
|
1423
|
+
if (flag !== "1" || raw.length !== cursorCount + 4)
|
|
1424
|
+
throw new Error("Invalid Redis stream poll response");
|
|
1425
|
+
const index = Number(raw[1]);
|
|
1426
|
+
if (!Number.isInteger(index) || index < 1 || index > queueKeys.length)
|
|
1427
|
+
throw new Error("Invalid Redis stream poll response");
|
|
1428
|
+
return {
|
|
1429
|
+
cursors: raw.slice(4).map(String),
|
|
1430
|
+
hit: {
|
|
1431
|
+
index: index - 1,
|
|
1432
|
+
reservation: { id: String(raw[2]), owner, payload: String(raw[3]) }
|
|
1433
|
+
}
|
|
1434
|
+
};
|
|
1365
1435
|
}
|
|
1366
1436
|
async function readStreamQueueDepth(client, key) {
|
|
1367
1437
|
return Number(await client.send("EVAL", [
|
|
@@ -1410,20 +1480,6 @@ end
|
|
|
1410
1480
|
if #redis.call('XPENDING', KEYS[1], ARGV[1], '-', '+', 1, ARGV[3]) == 0 then redis.call('XGROUP', 'DELCONSUMER', KEYS[1], ARGV[1], ARGV[3]) end
|
|
1411
1481
|
return acknowledged
|
|
1412
1482
|
`;
|
|
1413
|
-
var CLAIM_STREAM = `
|
|
1414
|
-
local window = redis.call('XPENDING', KEYS[1], ARGV[1], ARGV[4], '+', 10)
|
|
1415
|
-
local claimed = redis.call('XAUTOCLAIM', KEYS[1], ARGV[1], ARGV[2], ARGV[3], ARGV[4], 'COUNT', 1)
|
|
1416
|
-
for _, entry in ipairs(window) do
|
|
1417
|
-
if entry[2] ~= ARGV[2] and #redis.call('XPENDING', KEYS[1], ARGV[1], '-', '+', 1, entry[2]) == 0 then redis.call('XGROUP', 'DELCONSUMER', KEYS[1], ARGV[1], entry[2]) end
|
|
1418
|
-
end
|
|
1419
|
-
if #redis.call('XPENDING', KEYS[1], ARGV[1], '-', '+', 1, ARGV[2]) == 0 then redis.call('XGROUP', 'DELCONSUMER', KEYS[1], ARGV[1], ARGV[2]) end
|
|
1420
|
-
return claimed
|
|
1421
|
-
`;
|
|
1422
|
-
var READ_STREAM = `
|
|
1423
|
-
local entries = redis.call('XREADGROUP', 'GROUP', ARGV[1], ARGV[2], 'COUNT', 1, 'STREAMS', KEYS[1], '>')
|
|
1424
|
-
if not entries and #redis.call('XPENDING', KEYS[1], ARGV[1], '-', '+', 1, ARGV[2]) == 0 then redis.call('XGROUP', 'DELCONSUMER', KEYS[1], ARGV[1], ARGV[2]) end
|
|
1425
|
-
return entries
|
|
1426
|
-
`;
|
|
1427
1483
|
async function ensureStreamGroup(client, key) {
|
|
1428
1484
|
await assertLegacyDrained(client, key);
|
|
1429
1485
|
try {
|
|
@@ -1433,17 +1489,6 @@ async function ensureStreamGroup(client, key) {
|
|
|
1433
1489
|
throw error;
|
|
1434
1490
|
}
|
|
1435
1491
|
}
|
|
1436
|
-
function readStreamEntry(value, owner) {
|
|
1437
|
-
if (!Array.isArray(value) || typeof value[0] !== "string" || !Array.isArray(value[1]))
|
|
1438
|
-
throw new Error("Invalid Redis stream entry response");
|
|
1439
|
-
const fields = value[1];
|
|
1440
|
-
let payload;
|
|
1441
|
-
for (let index = 0;index < fields.length; index += 2) {
|
|
1442
|
-
if (fields[index] === "payload" && typeof fields[index + 1] === "string")
|
|
1443
|
-
payload = fields[index + 1];
|
|
1444
|
-
}
|
|
1445
|
-
return { id: value[0], owner, payload: payload ?? "" };
|
|
1446
|
-
}
|
|
1447
1492
|
async function updateStreamReservation(client, key, reservation, action, controlKey) {
|
|
1448
1493
|
return Number(await client.send("EVAL", [
|
|
1449
1494
|
UPDATE_STREAM,
|
|
@@ -1460,35 +1505,6 @@ async function updateStreamReservation(client, key, reservation, action, control
|
|
|
1460
1505
|
controlKey ? "1" : "0"
|
|
1461
1506
|
])) === 1;
|
|
1462
1507
|
}
|
|
1463
|
-
async function reserveStreamJob(client, key, owner, cursor = "0-0") {
|
|
1464
|
-
const claimed = await client.send("EVAL", [
|
|
1465
|
-
CLAIM_STREAM,
|
|
1466
|
-
"1",
|
|
1467
|
-
streamQueueKey(key),
|
|
1468
|
-
STREAM_GROUP,
|
|
1469
|
-
owner,
|
|
1470
|
-
String(readQueueVisibilityMs()),
|
|
1471
|
-
cursor
|
|
1472
|
-
]);
|
|
1473
|
-
if (!Array.isArray(claimed) || typeof claimed[0] !== "string" || !Array.isArray(claimed[1]))
|
|
1474
|
-
throw new Error("Invalid Redis XAUTOCLAIM response");
|
|
1475
|
-
const nextCursor = claimed[0];
|
|
1476
|
-
if (claimed[1].length > 0)
|
|
1477
|
-
return { reservation: readStreamEntry(claimed[1][0], owner), cursor: nextCursor };
|
|
1478
|
-
const fresh = await client.send("EVAL", [
|
|
1479
|
-
READ_STREAM,
|
|
1480
|
-
"1",
|
|
1481
|
-
streamQueueKey(key),
|
|
1482
|
-
STREAM_GROUP,
|
|
1483
|
-
owner
|
|
1484
|
-
]);
|
|
1485
|
-
if (fresh === null)
|
|
1486
|
-
return { reservation: null, cursor: nextCursor };
|
|
1487
|
-
if (!Array.isArray(fresh) || !Array.isArray(fresh[0]) || !Array.isArray(fresh[0][1]))
|
|
1488
|
-
throw new Error("Invalid Redis XREADGROUP response");
|
|
1489
|
-
return { reservation: readStreamEntry(fresh[0][1][0], owner), cursor: nextCursor };
|
|
1490
|
-
}
|
|
1491
|
-
|
|
1492
1508
|
class RedisStreamsQueue {
|
|
1493
1509
|
client;
|
|
1494
1510
|
constructor(redisUrl) {
|
|
@@ -1586,12 +1602,6 @@ class RedisStreamsWorker {
|
|
|
1586
1602
|
await ensureStreamGroup(this.client, key);
|
|
1587
1603
|
this.initialized = true;
|
|
1588
1604
|
}
|
|
1589
|
-
async reserve(key) {
|
|
1590
|
-
await promoteStreamRetries(this.client, key);
|
|
1591
|
-
const result = await reserveStreamJob(this.client, key, this.owner, this.cursors.get(key));
|
|
1592
|
-
this.cursors.set(key, result.cursor);
|
|
1593
|
-
return result.reservation;
|
|
1594
|
-
}
|
|
1595
1605
|
async processNext() {
|
|
1596
1606
|
if (this.processing)
|
|
1597
1607
|
throw new Error("Queue worker already processing a job.");
|
|
@@ -1608,13 +1618,19 @@ class RedisStreamsWorker {
|
|
|
1608
1618
|
await this.initialize();
|
|
1609
1619
|
const deadline = performance.now() + this.timeoutSeconds * 1000;
|
|
1610
1620
|
do {
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1621
|
+
if (this.stopping)
|
|
1622
|
+
return false;
|
|
1623
|
+
const polled = await pollStreamPriorities(this.client, this.queueKeys, this.owner, this.queueKeys.map((key) => this.cursors.get(key) ?? "0-0"));
|
|
1624
|
+
this.queueKeys.forEach((key, index) => {
|
|
1625
|
+
const cursor = polled.cursors[index];
|
|
1626
|
+
if (cursor)
|
|
1627
|
+
this.cursors.set(key, cursor);
|
|
1628
|
+
});
|
|
1629
|
+
if (polled.hit) {
|
|
1630
|
+
const key = this.queueKeys[polled.hit.index];
|
|
1631
|
+
if (!key)
|
|
1632
|
+
throw new Error("Invalid Redis stream poll response");
|
|
1633
|
+
await this.handle(key, polled.hit.reservation);
|
|
1618
1634
|
return true;
|
|
1619
1635
|
}
|
|
1620
1636
|
if (performance.now() >= deadline || this.stopping)
|
|
@@ -1887,6 +1903,66 @@ for _, record in ipairs(redis.call('LRANGE', KEYS[2], 0, -1)) do
|
|
|
1887
1903
|
end
|
|
1888
1904
|
return reclaimed
|
|
1889
1905
|
`;
|
|
1906
|
+
var POLL_LIST_PRIORITIES = `${SCRIPT_HEADER}
|
|
1907
|
+
local owner = ARGV[1]
|
|
1908
|
+
local visibility = ARGV[2]
|
|
1909
|
+
local reservationId = ARGV[3]
|
|
1910
|
+
local reclaim = ARGV[4] == '1'
|
|
1911
|
+
local function reclaimQueue(listKey, processingKey, leaseKey)
|
|
1912
|
+
local err = checkType(listKey, 'list') or checkType(processingKey, 'list') or checkType(leaseKey, 'hash')
|
|
1913
|
+
if err then return err end
|
|
1914
|
+
local now = nowMs()
|
|
1915
|
+
for _, record in ipairs(redis.call('LRANGE', processingKey, 0, -1)) do
|
|
1916
|
+
local payload, field = record, record
|
|
1917
|
+
local modern = false
|
|
1918
|
+
local ok, reservation = pcall(cjson.decode, record)
|
|
1919
|
+
if ok and type(reservation) == 'table' and reservation.strataReservation == 1 then
|
|
1920
|
+
payload, field, modern = reservation.payload, reservation.id, true
|
|
1921
|
+
end
|
|
1922
|
+
local raw = redis.call('HGET', leaseKey, field)
|
|
1923
|
+
local expired = true
|
|
1924
|
+
if raw then
|
|
1925
|
+
if modern then
|
|
1926
|
+
local leaseOk, lease = pcall(cjson.decode, raw)
|
|
1927
|
+
expired = not leaseOk or type(lease) ~= 'table' or not tonumber(lease.expiresAt) or tonumber(lease.expiresAt) <= now
|
|
1928
|
+
else
|
|
1929
|
+
local reservedAt = tonumber(raw)
|
|
1930
|
+
expired = not reservedAt or now - reservedAt >= tonumber(visibility)
|
|
1931
|
+
end
|
|
1932
|
+
end
|
|
1933
|
+
if expired then
|
|
1934
|
+
redis.call('LPUSH', listKey, payload)
|
|
1935
|
+
redis.call('LREM', processingKey, 1, record)
|
|
1936
|
+
redis.call('HDEL', leaseKey, field)
|
|
1937
|
+
end
|
|
1938
|
+
end
|
|
1939
|
+
end
|
|
1940
|
+
local function reserveQueue(listKey, processingKey, leaseKey)
|
|
1941
|
+
local err = checkType(listKey, 'list') or checkType(processingKey, 'list') or checkType(leaseKey, 'hash')
|
|
1942
|
+
if err then return err end
|
|
1943
|
+
local payload = redis.call('LINDEX', listKey, -1)
|
|
1944
|
+
if not payload then return nil end
|
|
1945
|
+
local record = cjson.encode({strataReservation=1, id=reservationId, owner=owner, payload=payload})
|
|
1946
|
+
local lease = cjson.encode({owner=owner, expiresAt=nowMs()+tonumber(visibility)})
|
|
1947
|
+
redis.call('LPUSH', processingKey, record)
|
|
1948
|
+
redis.call('HSET', leaseKey, reservationId, lease)
|
|
1949
|
+
redis.call('RPOP', listKey)
|
|
1950
|
+
return record
|
|
1951
|
+
end
|
|
1952
|
+
local count = #KEYS / 3
|
|
1953
|
+
if reclaim then
|
|
1954
|
+
for i = 1, count do
|
|
1955
|
+
local err = reclaimQueue(KEYS[(i - 1) * 3 + 1], KEYS[(i - 1) * 3 + 2], KEYS[(i - 1) * 3 + 3])
|
|
1956
|
+
if err then return err end
|
|
1957
|
+
end
|
|
1958
|
+
end
|
|
1959
|
+
for i = 1, count do
|
|
1960
|
+
local record = reserveQueue(KEYS[(i - 1) * 3 + 1], KEYS[(i - 1) * 3 + 2], KEYS[(i - 1) * 3 + 3])
|
|
1961
|
+
if type(record) == 'string' then return {'1', tostring(i), record} end
|
|
1962
|
+
if record then return record end
|
|
1963
|
+
end
|
|
1964
|
+
return {'0'}
|
|
1965
|
+
`;
|
|
1890
1966
|
function queueInvalidKey(queueKey) {
|
|
1891
1967
|
return `${queueKey}:invalid`;
|
|
1892
1968
|
}
|
|
@@ -1921,6 +1997,41 @@ async function updateQueueReservation(client, queueKey, reservation, action) {
|
|
|
1921
1997
|
reservation.payload
|
|
1922
1998
|
])) === 1;
|
|
1923
1999
|
}
|
|
2000
|
+
async function pollListPriorities(client, queueKeys, owner, reservationId, reclaim) {
|
|
2001
|
+
const keys = [];
|
|
2002
|
+
for (const key of queueKeys) {
|
|
2003
|
+
keys.push(key, queueProcessingKey(key), queueProcessingLeaseKey(key));
|
|
2004
|
+
}
|
|
2005
|
+
const raw = await client.send("EVAL", [
|
|
2006
|
+
POLL_LIST_PRIORITIES,
|
|
2007
|
+
String(keys.length),
|
|
2008
|
+
...keys,
|
|
2009
|
+
owner,
|
|
2010
|
+
String(readQueueVisibilityMs()),
|
|
2011
|
+
reservationId,
|
|
2012
|
+
reclaim ? "1" : "0"
|
|
2013
|
+
]);
|
|
2014
|
+
if (!Array.isArray(raw))
|
|
2015
|
+
throw new Error("Invalid Redis list poll response");
|
|
2016
|
+
const flag = String(raw[0]);
|
|
2017
|
+
if (flag === "0") {
|
|
2018
|
+
if (raw.length !== 1)
|
|
2019
|
+
throw new Error("Invalid Redis list poll response");
|
|
2020
|
+
return null;
|
|
2021
|
+
}
|
|
2022
|
+
if (flag !== "1" || raw.length !== 3)
|
|
2023
|
+
throw new Error("Invalid Redis list poll response");
|
|
2024
|
+
const index = Number(raw[1]);
|
|
2025
|
+
const record = raw[2];
|
|
2026
|
+
if (!Number.isInteger(index) || index < 1 || index > queueKeys.length || typeof record !== "string") {
|
|
2027
|
+
throw new Error("Invalid Redis list poll response");
|
|
2028
|
+
}
|
|
2029
|
+
const queueKey = queueKeys[index - 1];
|
|
2030
|
+
if (!queueKey)
|
|
2031
|
+
throw new Error("Invalid Redis list poll response");
|
|
2032
|
+
const reservation = JSON.parse(record);
|
|
2033
|
+
return { queueKey, reservation: { ...reservation, record } };
|
|
2034
|
+
}
|
|
1924
2035
|
async function reclaimExpiredQueueReservations(client, queueKeys = QUEUE_KEYS) {
|
|
1925
2036
|
let reclaimed = 0;
|
|
1926
2037
|
for (const key of queueKeys) {
|
|
@@ -1981,14 +2092,14 @@ class QueueWorker {
|
|
|
1981
2092
|
}
|
|
1982
2093
|
async reserve() {
|
|
1983
2094
|
const deadline = performance.now() + this.timeoutSeconds * 1000;
|
|
2095
|
+
let reclaim = true;
|
|
1984
2096
|
do {
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
}
|
|
2097
|
+
if (this.stopping)
|
|
2098
|
+
return null;
|
|
2099
|
+
const reserved = await pollListPriorities(this.client, this.queueKeys, this.owner, crypto.randomUUID(), reclaim);
|
|
2100
|
+
reclaim = false;
|
|
2101
|
+
if (reserved)
|
|
2102
|
+
return reserved;
|
|
1992
2103
|
if (this.stopping || performance.now() >= deadline)
|
|
1993
2104
|
return null;
|
|
1994
2105
|
await Bun.sleep(Math.min(100, Math.max(1, deadline - performance.now())));
|
|
@@ -1998,7 +2109,6 @@ class QueueWorker {
|
|
|
1998
2109
|
async processNext() {
|
|
1999
2110
|
if (this.stopping)
|
|
2000
2111
|
return false;
|
|
2001
|
-
await reclaimExpiredQueueReservations(this.client, this.queueKeys);
|
|
2002
2112
|
const reserved = await this.reserve();
|
|
2003
2113
|
if (!reserved)
|
|
2004
2114
|
return false;
|