@zintrust/queue-redis 0.4.70 → 0.4.74
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/dist/BullMQRedisQueue.js +11 -10
- package/dist/RedisQueue.d.ts +10 -0
- package/dist/RedisQueue.js +92 -0
- package/dist/build-manifest.json +22 -10
- package/package.json +2 -2
package/dist/BullMQRedisQueue.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Cloudflare, createLockProvider, createRedisConnection, Env, ErrorFactory, generateUuid, getBullMQSafeQueueName, getLockProvider, Logger, queueConfig, registerLockProvider, resolveLockPrefix, ZintrustLang, } from '@zintrust/core';
|
|
1
|
+
import { Cloudflare, createLockProvider, createRedisConnection, Env, ErrorFactory, generateUuid, getBullMQSafeQueueName, getLockProvider, Logger, queueConfig, registerLockProvider, resolveDeduplicationLockKey, resolveLockPrefix, ZintrustLang, } from '@zintrust/core';
|
|
2
2
|
import { Queue } from 'bullmq';
|
|
3
3
|
import { HttpQueueDriver } from './HttpQueueDriver.js';
|
|
4
4
|
export const shouldUseHttpProxyDriver = () => {
|
|
@@ -264,8 +264,8 @@ export const BullMQRedisQueue = (() => {
|
|
|
264
264
|
const deduplicationId = String(deduplication.id).trim();
|
|
265
265
|
return deduplicationId.length > 0 ? deduplicationId : null;
|
|
266
266
|
};
|
|
267
|
-
const checkExistingLock = async (deduplicationId, provider, replace, queue, jobId) => {
|
|
268
|
-
const status = await provider.status(
|
|
267
|
+
const checkExistingLock = async (scopedDeduplicationKey, deduplicationId, provider, replace, queue, jobId) => {
|
|
268
|
+
const status = await provider.status(scopedDeduplicationKey);
|
|
269
269
|
if (status.exists && !replace) {
|
|
270
270
|
Logger.info('BullMQ: Job deduplicated', {
|
|
271
271
|
queue,
|
|
@@ -276,9 +276,9 @@ export const BullMQRedisQueue = (() => {
|
|
|
276
276
|
}
|
|
277
277
|
return false;
|
|
278
278
|
};
|
|
279
|
-
const acquireDeduplicationLock = async (deduplicationId, provider, ttl, queue, jobId) => {
|
|
279
|
+
const acquireDeduplicationLock = async (scopedDeduplicationKey, deduplicationId, provider, ttl, queue, jobId) => {
|
|
280
280
|
const lockOptions = ttl ? { ttl } : {};
|
|
281
|
-
const lock = await provider.acquire(
|
|
281
|
+
const lock = await provider.acquire(scopedDeduplicationKey, lockOptions);
|
|
282
282
|
if (!lock.acquired) {
|
|
283
283
|
Logger.info('BullMQ: Job deduplicated (lock collision)', {
|
|
284
284
|
queue,
|
|
@@ -294,10 +294,10 @@ export const BullMQRedisQueue = (() => {
|
|
|
294
294
|
});
|
|
295
295
|
return true;
|
|
296
296
|
};
|
|
297
|
-
const scheduleLockRelease = (
|
|
297
|
+
const scheduleLockRelease = (scopedDeduplicationKey, provider, ttl, releaseAfter) => {
|
|
298
298
|
const timeoutId = globalThis.setTimeout(() => {
|
|
299
299
|
provider.release({
|
|
300
|
-
key:
|
|
300
|
+
key: scopedDeduplicationKey,
|
|
301
301
|
ttl: ttl ?? 0,
|
|
302
302
|
acquired: true,
|
|
303
303
|
expires: new Date(Date.now() + (ttl ?? 0)),
|
|
@@ -325,6 +325,7 @@ export const BullMQRedisQueue = (() => {
|
|
|
325
325
|
return { payloadToSend: payloadData, shouldReturn: false };
|
|
326
326
|
}
|
|
327
327
|
const provider = getLockProviderForQueue(payloadData.uniqueVia);
|
|
328
|
+
const scopedDeduplicationKey = resolveDeduplicationLockKey(queue, deduplicationId);
|
|
328
329
|
const ttl = typeof deduplication.ttl === 'number' && deduplication.ttl > 0
|
|
329
330
|
? deduplication.ttl
|
|
330
331
|
: undefined;
|
|
@@ -332,12 +333,12 @@ export const BullMQRedisQueue = (() => {
|
|
|
332
333
|
const jobId = jobOptions.jobId ?? generateUuid();
|
|
333
334
|
jobOptions.jobId = jobId;
|
|
334
335
|
// Check existing lock
|
|
335
|
-
const hasExistingLock = await checkExistingLock(deduplicationId, provider, replace, queue, jobId);
|
|
336
|
+
const hasExistingLock = await checkExistingLock(scopedDeduplicationKey, deduplicationId, provider, replace, queue, jobId);
|
|
336
337
|
if (hasExistingLock) {
|
|
337
338
|
return { payloadToSend: payloadData, shouldReturn: true, returnValue: deduplicationId };
|
|
338
339
|
}
|
|
339
340
|
// Acquire lock
|
|
340
|
-
const lockAcquired = await acquireDeduplicationLock(deduplicationId, provider, ttl, queue, jobId);
|
|
341
|
+
const lockAcquired = await acquireDeduplicationLock(scopedDeduplicationKey, deduplicationId, provider, ttl, queue, jobId);
|
|
341
342
|
if (!lockAcquired) {
|
|
342
343
|
return { payloadToSend: payloadData, shouldReturn: true, returnValue: deduplicationId };
|
|
343
344
|
}
|
|
@@ -347,7 +348,7 @@ export const BullMQRedisQueue = (() => {
|
|
|
347
348
|
let payloadToSend = payloadData;
|
|
348
349
|
// Handle releaseAfter numeric
|
|
349
350
|
if (typeof deduplication.releaseAfter === 'number' && deduplication.releaseAfter > 0) {
|
|
350
|
-
scheduleLockRelease(
|
|
351
|
+
scheduleLockRelease(scopedDeduplicationKey, provider, ttl, deduplication.releaseAfter);
|
|
351
352
|
}
|
|
352
353
|
// Handle releaseAfter non-numeric
|
|
353
354
|
if (deduplication.releaseAfter !== undefined &&
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { QueueMessage } from '@zintrust/core';
|
|
2
|
+
interface IQueueDriver {
|
|
3
|
+
enqueue<T = unknown>(queue: string, payload: T): Promise<string>;
|
|
4
|
+
dequeue<T = unknown>(queue: string): Promise<QueueMessage<T> | undefined>;
|
|
5
|
+
ack(queue: string, id: string): Promise<void>;
|
|
6
|
+
length(queue: string): Promise<number>;
|
|
7
|
+
drain(queue: string): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
export declare const RedisQueue: IQueueDriver;
|
|
10
|
+
export default RedisQueue;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { ErrorFactory, generateUuid, getRedisUrl, Logger } from '@zintrust/core';
|
|
2
|
+
export const RedisQueue = (() => {
|
|
3
|
+
let client = null;
|
|
4
|
+
let connected = false;
|
|
5
|
+
const ensureClient = async () => {
|
|
6
|
+
if (connected && client !== null)
|
|
7
|
+
return client;
|
|
8
|
+
const url = getRedisUrl();
|
|
9
|
+
if (url === null)
|
|
10
|
+
throw ErrorFactory.createConfigError('Redis queue driver requires REDIS_URL');
|
|
11
|
+
// Import lazily so package is optional for environments that don't use Redis
|
|
12
|
+
try {
|
|
13
|
+
// Prefer the redis package when available
|
|
14
|
+
try {
|
|
15
|
+
const mod = (await import('redis'));
|
|
16
|
+
const createClient = mod.createClient;
|
|
17
|
+
client = createClient({ url });
|
|
18
|
+
if (typeof client.connect === 'function') {
|
|
19
|
+
try {
|
|
20
|
+
await client.connect();
|
|
21
|
+
connected = true;
|
|
22
|
+
}
|
|
23
|
+
catch (connectionError) {
|
|
24
|
+
connected = false;
|
|
25
|
+
Logger.warn('Redis client connect failed:', String(connectionError));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
connected = true;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// Fallback to ioredis when available (used by queue-monitor)
|
|
34
|
+
const mod = (await import('ioredis'));
|
|
35
|
+
const redis = mod.default(url);
|
|
36
|
+
client = {
|
|
37
|
+
rPush: (queue, value) => redis.rpush(queue, value),
|
|
38
|
+
lPop: (queue) => redis.lpop(queue),
|
|
39
|
+
lLen: (queue) => redis.llen(queue),
|
|
40
|
+
del: (queue) => redis.del(queue),
|
|
41
|
+
};
|
|
42
|
+
connected = true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
const globalFake = globalThis
|
|
47
|
+
.__fakeRedisClient;
|
|
48
|
+
if (globalFake === undefined) {
|
|
49
|
+
throw ErrorFactory.createConfigError("Redis queue driver requires the 'redis' or 'ioredis' package (run `zin add queue:redis` / `zin plugin install queue:redis`, or `npm install redis` / `npm install ioredis`) or a test fake client set in globalThis.__fakeRedisClient", error);
|
|
50
|
+
}
|
|
51
|
+
client = globalFake;
|
|
52
|
+
connected = true;
|
|
53
|
+
}
|
|
54
|
+
if (client === null)
|
|
55
|
+
throw ErrorFactory.createConfigError('Redis client could not be initialized');
|
|
56
|
+
return client;
|
|
57
|
+
};
|
|
58
|
+
return {
|
|
59
|
+
async enqueue(queue, payload) {
|
|
60
|
+
const cli = await ensureClient();
|
|
61
|
+
const id = generateUuid();
|
|
62
|
+
const msg = JSON.stringify({ id, payload, attempts: 0 });
|
|
63
|
+
await cli.rPush(queue, msg);
|
|
64
|
+
return id;
|
|
65
|
+
},
|
|
66
|
+
async dequeue(queue) {
|
|
67
|
+
const cli = await ensureClient();
|
|
68
|
+
const raw = await cli.lPop(queue);
|
|
69
|
+
if (raw === null)
|
|
70
|
+
return undefined;
|
|
71
|
+
try {
|
|
72
|
+
const parsed = JSON.parse(raw);
|
|
73
|
+
return parsed;
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
throw ErrorFactory.createTryCatchError('Failed to parse queue message', err);
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
async ack(_queue, _id) {
|
|
80
|
+
return Promise.resolve(); // NOSONAR
|
|
81
|
+
},
|
|
82
|
+
async length(queue) {
|
|
83
|
+
const cli = await ensureClient();
|
|
84
|
+
return cli.lLen(queue);
|
|
85
|
+
},
|
|
86
|
+
async drain(queue) {
|
|
87
|
+
const cli = await ensureClient();
|
|
88
|
+
await cli.del(queue);
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
})();
|
|
92
|
+
export default RedisQueue;
|
package/dist/build-manifest.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zintrust/queue-redis",
|
|
3
|
-
"version": "0.4.
|
|
4
|
-
"buildDate": "2026-04-
|
|
3
|
+
"version": "0.4.74",
|
|
4
|
+
"buildDate": "2026-04-07T11:00:15.411Z",
|
|
5
5
|
"buildEnvironment": {
|
|
6
|
-
"node": "
|
|
7
|
-
"platform": "
|
|
8
|
-
"arch": "
|
|
6
|
+
"node": "v22.22.1",
|
|
7
|
+
"platform": "darwin",
|
|
8
|
+
"arch": "arm64"
|
|
9
9
|
},
|
|
10
10
|
"git": {
|
|
11
|
-
"commit": "
|
|
12
|
-
"branch": "
|
|
11
|
+
"commit": "9bdfc570",
|
|
12
|
+
"branch": "release"
|
|
13
13
|
},
|
|
14
14
|
"package": {
|
|
15
15
|
"engines": {
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"sha256": "52fb0f688cd17cc7d8e8128e60df6f5eea4c803282382ac75550e6aacee7cbb0"
|
|
30
30
|
},
|
|
31
31
|
"BullMQRedisQueue.js": {
|
|
32
|
-
"size":
|
|
33
|
-
"sha256": "
|
|
32
|
+
"size": 21579,
|
|
33
|
+
"sha256": "042803997f2f9895827ce34359bb62c80b2b889ab4ddc9cbbe191924a376684a"
|
|
34
34
|
},
|
|
35
35
|
"HttpQueueDriver.d.ts": {
|
|
36
36
|
"size": 835,
|
|
@@ -56,13 +56,25 @@
|
|
|
56
56
|
"size": 5464,
|
|
57
57
|
"sha256": "18b785c47b4df689f9969625880d1b559e6cf8c95bb8c3cd3a4112fa75e0d870"
|
|
58
58
|
},
|
|
59
|
+
"RedisQueue.d.ts": {
|
|
60
|
+
"size": 438,
|
|
61
|
+
"sha256": "eefad366ae71d63044b23038265bf3eb60a0277e41ac70f57c5973d79f2af5ce"
|
|
62
|
+
},
|
|
63
|
+
"RedisQueue.js": {
|
|
64
|
+
"size": 3593,
|
|
65
|
+
"sha256": "dc8b2c28b2e288e048423067f90ffbe0389ac813086246a1c8fafeeeab5c142d"
|
|
66
|
+
},
|
|
67
|
+
"build-manifest.json": {
|
|
68
|
+
"size": 2513,
|
|
69
|
+
"sha256": "640dd3671f0f79dedc8ec4432ccf15cfdd351793c2a379cbda83d6a61bd424e3"
|
|
70
|
+
},
|
|
59
71
|
"index.d.ts": {
|
|
60
72
|
"size": 565,
|
|
61
73
|
"sha256": "7bc063419989a39530b3a4213627e0aea33fbaacac8c307d9e7e654c99bd5b8d"
|
|
62
74
|
},
|
|
63
75
|
"index.js": {
|
|
64
76
|
"size": 677,
|
|
65
|
-
"sha256": "
|
|
77
|
+
"sha256": "62b975ffe5348710c55e4a589ff35a54b992293b68f23479a33360f6f482b310"
|
|
66
78
|
},
|
|
67
79
|
"register.d.ts": {
|
|
68
80
|
"size": 170,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zintrust/queue-redis",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.74",
|
|
4
4
|
"description": "Redis queue driver for ZinTrust.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"node": ">=20.0.0"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
|
-
"@zintrust/core": "^0.4.
|
|
26
|
+
"@zintrust/core": "^0.4.74"
|
|
27
27
|
},
|
|
28
28
|
"publishConfig": {
|
|
29
29
|
"access": "public"
|