glide-mq 0.1.0
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/README.md +190 -0
- package/dist/connection.d.ts +36 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/connection.js +100 -0
- package/dist/connection.js.map +1 -0
- package/dist/errors.d.ts +10 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +25 -0
- package/dist/errors.js.map +1 -0
- package/dist/flow-producer.d.ts +36 -0
- package/dist/flow-producer.d.ts.map +1 -0
- package/dist/flow-producer.js +185 -0
- package/dist/flow-producer.js.map +1 -0
- package/dist/functions/index.d.ts +136 -0
- package/dist/functions/index.d.ts.map +1 -0
- package/dist/functions/index.js +1062 -0
- package/dist/functions/index.js.map +1 -0
- package/dist/graceful-shutdown.d.ts +17 -0
- package/dist/graceful-shutdown.d.ts.map +1 -0
- package/dist/graceful-shutdown.js +27 -0
- package/dist/graceful-shutdown.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/job.d.ts +106 -0
- package/dist/job.d.ts.map +1 -0
- package/dist/job.js +252 -0
- package/dist/job.js.map +1 -0
- package/dist/queue-events.d.ts +33 -0
- package/dist/queue-events.d.ts.map +1 -0
- package/dist/queue-events.js +138 -0
- package/dist/queue-events.js.map +1 -0
- package/dist/queue.d.ts +140 -0
- package/dist/queue.d.ts.map +1 -0
- package/dist/queue.js +483 -0
- package/dist/queue.js.map +1 -0
- package/dist/scheduler.d.ts +48 -0
- package/dist/scheduler.d.ts.map +1 -0
- package/dist/scheduler.js +140 -0
- package/dist/scheduler.js.map +1 -0
- package/dist/telemetry.d.ts +29 -0
- package/dist/telemetry.d.ts.map +1 -0
- package/dist/telemetry.js +90 -0
- package/dist/telemetry.js.map +1 -0
- package/dist/types.d.ts +125 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils.d.ts +65 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +217 -0
- package/dist/utils.js.map +1 -0
- package/dist/worker.d.ts +138 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +574 -0
- package/dist/worker.js.map +1 -0
- package/dist/workflows.d.ts +34 -0
- package/dist/workflows.d.ts.map +1 -0
- package/dist/workflows.js +117 -0
- package/dist/workflows.js.map +1 -0
- package/package.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# glide-mq
|
|
2
|
+
|
|
3
|
+
High-performance message queue for Node.js, built on Valkey/Redis Streams with [Valkey GLIDE](https://github.com/valkey-io/valkey-glide) native NAPI bindings.
|
|
4
|
+
|
|
5
|
+
## Performance
|
|
6
|
+
|
|
7
|
+
| Concurrency | Throughput |
|
|
8
|
+
|-------------|-----------|
|
|
9
|
+
| c=1 | 4,376 jobs/s |
|
|
10
|
+
| c=10 | 20,979 jobs/s |
|
|
11
|
+
| c=50 | 44,643 jobs/s |
|
|
12
|
+
|
|
13
|
+
No-op processor, Valkey 8.0, single node.
|
|
14
|
+
|
|
15
|
+
## Why
|
|
16
|
+
|
|
17
|
+
- **Streams-first** - uses Redis Streams + consumer groups + PEL instead of Lists + BRPOPLPUSH. Fewer moving parts, built-in at-least-once delivery.
|
|
18
|
+
- **Server Functions** - single `FUNCTION LOAD` instead of dozens of EVAL scripts. Persistent across restarts, no NOSCRIPT cache-miss errors.
|
|
19
|
+
- **1 RTT per job** - `completeAndFetchNext` combines job completion + next job fetch + activation in a single FCALL round trip.
|
|
20
|
+
- **Cluster-native** - hash-tagged keys from day one. No afterthought `{braces}` requirement.
|
|
21
|
+
- **Native bindings** - built on [@glidemq/speedkey](https://github.com/avifenesh/speedkey) (valkey-glide with Rust core + NAPI).
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install glide-mq
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Requires Node.js 20+ and a running Valkey (7.2+) or Redis (6.2+) instance.
|
|
30
|
+
|
|
31
|
+
## Quick Start
|
|
32
|
+
|
|
33
|
+
```typescript
|
|
34
|
+
import { Queue, Worker } from 'glide-mq';
|
|
35
|
+
|
|
36
|
+
const connection = { addresses: [{ host: 'localhost', port: 6379 }] };
|
|
37
|
+
|
|
38
|
+
// Producer
|
|
39
|
+
const queue = new Queue('tasks', { connection });
|
|
40
|
+
await queue.add('send-email', { to: 'user@example.com', subject: 'Hello' });
|
|
41
|
+
|
|
42
|
+
// Consumer
|
|
43
|
+
const worker = new Worker('tasks', async (job) => {
|
|
44
|
+
console.log(`Processing ${job.name}: ${JSON.stringify(job.data)}`);
|
|
45
|
+
return { sent: true };
|
|
46
|
+
}, { connection, concurrency: 10 });
|
|
47
|
+
|
|
48
|
+
worker.on('completed', (job) => console.log(`Job ${job.id} completed`));
|
|
49
|
+
worker.on('failed', (job, err) => console.log(`Job ${job.id} failed: ${err.message}`));
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Features
|
|
53
|
+
|
|
54
|
+
### Core
|
|
55
|
+
- **Queue** - add, addBulk, getJob, getJobs, pause, resume, obliterate, drain
|
|
56
|
+
- **Worker** - concurrent processing, XREADGROUP polling, graceful shutdown
|
|
57
|
+
- **Job** - progress, updateData, retry, remove, log, state queries, waitUntilFinished
|
|
58
|
+
|
|
59
|
+
### Reliability
|
|
60
|
+
- **Retries** - fixed, exponential, jitter backoff + custom strategy functions
|
|
61
|
+
- **Stalled recovery** - XAUTOCLAIM with heartbeat-based lock renewal
|
|
62
|
+
- **Per-job timeout** - automatic failure if processor exceeds timeout
|
|
63
|
+
- **Dead letter queue** - permanently failed jobs routed to a separate queue
|
|
64
|
+
|
|
65
|
+
### Advanced
|
|
66
|
+
- **Deduplication** - simple, throttle, debounce modes
|
|
67
|
+
- **Rate limiting** - sliding window, per-queue
|
|
68
|
+
- **Global concurrency** - limit active jobs across all workers
|
|
69
|
+
- **Job retention** - removeOnComplete/removeOnFail (count, age-based)
|
|
70
|
+
- **Priorities** - encoded in sorted set scores, FIFO within same priority
|
|
71
|
+
|
|
72
|
+
### Workflows
|
|
73
|
+
- **FlowProducer** - atomic parent-child job trees with nested flows
|
|
74
|
+
- **chain(queue, jobs)** - sequential pipeline, each job receives previous result
|
|
75
|
+
- **group(queue, jobs)** - parallel execution, parent completes when all children done
|
|
76
|
+
- **chord(queue, group, callback)** - run group, then callback with all results
|
|
77
|
+
|
|
78
|
+
### Observability
|
|
79
|
+
- **QueueEvents** - stream-based event subscription (added, completed, failed, stalled, etc.)
|
|
80
|
+
- **Job schedulers** - cron patterns and fixed intervals for repeatable jobs
|
|
81
|
+
- **Metrics** - getJobCounts, getMetrics
|
|
82
|
+
- **OpenTelemetry** - optional spans for Queue.add, Worker.process, FlowProducer.add
|
|
83
|
+
|
|
84
|
+
### Operations
|
|
85
|
+
- **Graceful shutdown** - SIGTERM/SIGINT handler, waits for active jobs
|
|
86
|
+
- **Connection recovery** - exponential backoff reconnect with function library reload
|
|
87
|
+
- **Job revocation** - cooperative cancellation via AbortSignal
|
|
88
|
+
- **Cluster mode** - all features work in Valkey Cluster (pass `clusterMode: true`)
|
|
89
|
+
|
|
90
|
+
## API
|
|
91
|
+
|
|
92
|
+
### Queue
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
const queue = new Queue('name', {
|
|
96
|
+
connection: { addresses: [{ host, port }], clusterMode: false },
|
|
97
|
+
prefix: 'glide', // key prefix (default: 'glide')
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
await queue.add('jobName', data, {
|
|
101
|
+
delay: 5000, // delayed job (ms)
|
|
102
|
+
priority: 1, // lower = higher priority
|
|
103
|
+
attempts: 3, // max retry attempts
|
|
104
|
+
backoff: { type: 'exponential', delay: 1000 },
|
|
105
|
+
timeout: 30000, // per-job timeout (ms)
|
|
106
|
+
removeOnComplete: true, // or count, or { age, count }
|
|
107
|
+
deduplication: { id: 'unique-key', mode: 'simple' },
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
await queue.pause();
|
|
111
|
+
await queue.resume();
|
|
112
|
+
await queue.getJobCounts(); // { waiting, active, delayed, completed, failed }
|
|
113
|
+
await queue.obliterate({ force: true });
|
|
114
|
+
await queue.close();
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Worker
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
const worker = new Worker('name', async (job) => {
|
|
121
|
+
await job.updateProgress(50);
|
|
122
|
+
return result;
|
|
123
|
+
}, {
|
|
124
|
+
connection,
|
|
125
|
+
concurrency: 10,
|
|
126
|
+
blockTimeout: 5000,
|
|
127
|
+
stalledInterval: 30000,
|
|
128
|
+
lockDuration: 30000,
|
|
129
|
+
limiter: { max: 100, duration: 60000 },
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
worker.on('completed', (job, result) => {});
|
|
133
|
+
worker.on('failed', (job, error) => {});
|
|
134
|
+
await worker.close();
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Flows
|
|
138
|
+
|
|
139
|
+
```typescript
|
|
140
|
+
import { FlowProducer, chain, group, chord } from 'glide-mq';
|
|
141
|
+
|
|
142
|
+
// Parent-child
|
|
143
|
+
const flow = new FlowProducer({ connection });
|
|
144
|
+
await flow.add({
|
|
145
|
+
name: 'parent', queueName: 'tasks', data: {},
|
|
146
|
+
children: [
|
|
147
|
+
{ name: 'child1', queueName: 'tasks', data: {} },
|
|
148
|
+
{ name: 'child2', queueName: 'tasks', data: {} },
|
|
149
|
+
],
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Chain: A -> B -> C (sequential)
|
|
153
|
+
await chain(connection, 'tasks', [
|
|
154
|
+
{ name: 'step1', data: {} },
|
|
155
|
+
{ name: 'step2', data: {} },
|
|
156
|
+
]);
|
|
157
|
+
|
|
158
|
+
// Group: A + B + C (parallel)
|
|
159
|
+
await group(connection, 'tasks', [
|
|
160
|
+
{ name: 'task1', data: {} },
|
|
161
|
+
{ name: 'task2', data: {} },
|
|
162
|
+
]);
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### Events
|
|
166
|
+
|
|
167
|
+
```typescript
|
|
168
|
+
import { QueueEvents } from 'glide-mq';
|
|
169
|
+
|
|
170
|
+
const events = new QueueEvents('tasks', { connection });
|
|
171
|
+
events.on('completed', ({ jobId, returnvalue }) => {});
|
|
172
|
+
events.on('failed', ({ jobId, failedReason }) => {});
|
|
173
|
+
events.on('stalled', ({ jobId }) => {});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Cluster Mode
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
const connection = {
|
|
180
|
+
addresses: [{ host: 'cluster-node', port: 7000 }],
|
|
181
|
+
clusterMode: true,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
// Everything works the same - keys are hash-tagged automatically
|
|
185
|
+
const queue = new Queue('tasks', { connection });
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
|
|
190
|
+
Apache-2.0
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { ConnectionOptions, Client } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Create a GlideClient (standalone) or GlideClusterClient (cluster) based on options.
|
|
4
|
+
*/
|
|
5
|
+
export declare function createClient(opts: ConnectionOptions): Promise<Client>;
|
|
6
|
+
/**
|
|
7
|
+
* Create a dedicated client for XREADGROUP BLOCK / XREAD BLOCK.
|
|
8
|
+
* Must not be shared with non-blocking commands.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createBlockingClient(opts: ConnectionOptions): Promise<Client>;
|
|
11
|
+
/**
|
|
12
|
+
* Ensure the glidemq function library is loaded and up-to-date on the server.
|
|
13
|
+
*
|
|
14
|
+
* Strategy:
|
|
15
|
+
* 1. Try FCALL glidemq_version with empty keys/args
|
|
16
|
+
* 2. If succeeds and version matches, return (already loaded)
|
|
17
|
+
* 3. If fails (function not found) or version mismatch, call functionLoad with replace
|
|
18
|
+
* 4. For cluster clients, load to all primaries via route option
|
|
19
|
+
*
|
|
20
|
+
* @param client - The Valkey client
|
|
21
|
+
* @param librarySource - The Lua library source code to load
|
|
22
|
+
* @param clusterMode - Whether the client is a cluster client (determines routing for FUNCTION LOAD)
|
|
23
|
+
*/
|
|
24
|
+
export declare function ensureFunctionLibrary(client: Client, librarySource?: string, clusterMode?: boolean): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Create a consumer group for the given stream key.
|
|
27
|
+
* Uses XGROUP CREATE with mkStream to auto-create the stream if it doesn't exist.
|
|
28
|
+
* Handles BUSYGROUP error (group already exists) gracefully.
|
|
29
|
+
*
|
|
30
|
+
* @param client - The Valkey client
|
|
31
|
+
* @param streamKey - The stream key to create the group on
|
|
32
|
+
* @param groupName - The consumer group name
|
|
33
|
+
* @param startId - The ID to start reading from (default: '0' = beginning)
|
|
34
|
+
*/
|
|
35
|
+
export declare function createConsumerGroup(client: Client, streamKey: string, groupName: string, startId?: string): Promise<void>;
|
|
36
|
+
//# sourceMappingURL=connection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.d.ts","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAIzD;;GAEG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAiB3E;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAEnF;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,MAAM,EACd,aAAa,GAAE,MAAuB,EACtC,WAAW,GAAE,OAAe,GAC3B,OAAO,CAAC,IAAI,CAAC,CA0Bf;AAED;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,GAAE,MAAY,GACpB,OAAO,CAAC,IAAI,CAAC,CAYf"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createClient = createClient;
|
|
4
|
+
exports.createBlockingClient = createBlockingClient;
|
|
5
|
+
exports.ensureFunctionLibrary = ensureFunctionLibrary;
|
|
6
|
+
exports.createConsumerGroup = createConsumerGroup;
|
|
7
|
+
const speedkey_1 = require("@glidemq/speedkey");
|
|
8
|
+
const errors_1 = require("./errors");
|
|
9
|
+
const index_1 = require("./functions/index");
|
|
10
|
+
/**
|
|
11
|
+
* Create a GlideClient (standalone) or GlideClusterClient (cluster) based on options.
|
|
12
|
+
*/
|
|
13
|
+
async function createClient(opts) {
|
|
14
|
+
const config = {
|
|
15
|
+
addresses: opts.addresses,
|
|
16
|
+
useTLS: opts.useTLS,
|
|
17
|
+
credentials: opts.credentials,
|
|
18
|
+
};
|
|
19
|
+
try {
|
|
20
|
+
if (opts.clusterMode) {
|
|
21
|
+
return await speedkey_1.GlideClusterClient.createClient(config);
|
|
22
|
+
}
|
|
23
|
+
return await speedkey_1.GlideClient.createClient(config);
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
throw new errors_1.ConnectionError(`Failed to create ${opts.clusterMode ? 'cluster' : 'standalone'} client: ${err instanceof Error ? err.message : String(err)}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Create a dedicated client for XREADGROUP BLOCK / XREAD BLOCK.
|
|
31
|
+
* Must not be shared with non-blocking commands.
|
|
32
|
+
*/
|
|
33
|
+
async function createBlockingClient(opts) {
|
|
34
|
+
return createClient(opts);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Ensure the glidemq function library is loaded and up-to-date on the server.
|
|
38
|
+
*
|
|
39
|
+
* Strategy:
|
|
40
|
+
* 1. Try FCALL glidemq_version with empty keys/args
|
|
41
|
+
* 2. If succeeds and version matches, return (already loaded)
|
|
42
|
+
* 3. If fails (function not found) or version mismatch, call functionLoad with replace
|
|
43
|
+
* 4. For cluster clients, load to all primaries via route option
|
|
44
|
+
*
|
|
45
|
+
* @param client - The Valkey client
|
|
46
|
+
* @param librarySource - The Lua library source code to load
|
|
47
|
+
* @param clusterMode - Whether the client is a cluster client (determines routing for FUNCTION LOAD)
|
|
48
|
+
*/
|
|
49
|
+
async function ensureFunctionLibrary(client, librarySource = index_1.LIBRARY_SOURCE, clusterMode = false) {
|
|
50
|
+
if (clusterMode) {
|
|
51
|
+
// In cluster mode, always load with REPLACE to all primaries.
|
|
52
|
+
// FUNCTION LOAD REPLACE is idempotent - safe to call every time.
|
|
53
|
+
// Skipping the version check avoids FCALL routing to replicas (empty keys = random node).
|
|
54
|
+
await client.functionLoad(librarySource, {
|
|
55
|
+
replace: true,
|
|
56
|
+
route: 'allPrimaries',
|
|
57
|
+
});
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
// Standalone: check version first to avoid unnecessary reload
|
|
61
|
+
try {
|
|
62
|
+
const result = await client.fcall('glidemq_version', [], []);
|
|
63
|
+
if (String(result) === index_1.LIBRARY_VERSION)
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
68
|
+
if (!msg.includes('Function not loaded') && !msg.includes('Function not found') && !msg.includes('No matching script') && !msg.includes('NOSCRIPT')) {
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
await client.functionLoad(librarySource, {
|
|
73
|
+
replace: true,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Create a consumer group for the given stream key.
|
|
78
|
+
* Uses XGROUP CREATE with mkStream to auto-create the stream if it doesn't exist.
|
|
79
|
+
* Handles BUSYGROUP error (group already exists) gracefully.
|
|
80
|
+
*
|
|
81
|
+
* @param client - The Valkey client
|
|
82
|
+
* @param streamKey - The stream key to create the group on
|
|
83
|
+
* @param groupName - The consumer group name
|
|
84
|
+
* @param startId - The ID to start reading from (default: '0' = beginning)
|
|
85
|
+
*/
|
|
86
|
+
async function createConsumerGroup(client, streamKey, groupName, startId = '0') {
|
|
87
|
+
try {
|
|
88
|
+
await client.xgroupCreate(streamKey, groupName, startId, {
|
|
89
|
+
mkStream: true,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
catch (err) {
|
|
93
|
+
// BUSYGROUP means the group already exists - that's fine
|
|
94
|
+
if (err instanceof Error && err.message.includes('BUSYGROUP')) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
throw err;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=connection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connection.js","sourceRoot":"","sources":["../src/connection.ts"],"names":[],"mappings":"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"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare class GlideMQError extends Error {
|
|
2
|
+
constructor(message: string);
|
|
3
|
+
}
|
|
4
|
+
export declare class ConnectionError extends GlideMQError {
|
|
5
|
+
constructor(message: string);
|
|
6
|
+
}
|
|
7
|
+
export declare class ScriptError extends GlideMQError {
|
|
8
|
+
constructor(message: string);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,qBAAa,YAAa,SAAQ,KAAK;gBACzB,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,eAAgB,SAAQ,YAAY;gBACnC,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,WAAY,SAAQ,YAAY;gBAC/B,OAAO,EAAE,MAAM;CAI5B"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ScriptError = exports.ConnectionError = exports.GlideMQError = void 0;
|
|
4
|
+
class GlideMQError extends Error {
|
|
5
|
+
constructor(message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'GlideMQError';
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
exports.GlideMQError = GlideMQError;
|
|
11
|
+
class ConnectionError extends GlideMQError {
|
|
12
|
+
constructor(message) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = 'ConnectionError';
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
exports.ConnectionError = ConnectionError;
|
|
18
|
+
class ScriptError extends GlideMQError {
|
|
19
|
+
constructor(message) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.name = 'ScriptError';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.ScriptError = ScriptError;
|
|
25
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":";;;AAAA,MAAa,YAAa,SAAQ,KAAK;IACrC,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;IAC7B,CAAC;CACF;AALD,oCAKC;AAED,MAAa,eAAgB,SAAQ,YAAY;IAC/C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AALD,0CAKC;AAED,MAAa,WAAY,SAAQ,YAAY;IAC3C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,aAAa,CAAC;IAC5B,CAAC;CACF;AALD,kCAKC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { FlowProducerOptions, FlowJob } from './types';
|
|
2
|
+
import { Job } from './job';
|
|
3
|
+
export interface JobNode {
|
|
4
|
+
job: Job;
|
|
5
|
+
children?: JobNode[];
|
|
6
|
+
}
|
|
7
|
+
export declare class FlowProducer {
|
|
8
|
+
private opts;
|
|
9
|
+
private client;
|
|
10
|
+
private closing;
|
|
11
|
+
constructor(opts: FlowProducerOptions);
|
|
12
|
+
/** @internal */
|
|
13
|
+
private getClient;
|
|
14
|
+
/**
|
|
15
|
+
* Add a flow (parent with children) atomically.
|
|
16
|
+
* Children can have their own children (recursive flows), which are flattened
|
|
17
|
+
* into multiple addFlow calls (one per level with children).
|
|
18
|
+
*/
|
|
19
|
+
add(flow: FlowJob): Promise<JobNode>;
|
|
20
|
+
/**
|
|
21
|
+
* Add multiple independent flows.
|
|
22
|
+
*/
|
|
23
|
+
addBulk(flows: FlowJob[]): Promise<JobNode[]>;
|
|
24
|
+
/**
|
|
25
|
+
* Recursively add a flow. If children themselves have children,
|
|
26
|
+
* those sub-flows are added first (bottom-up), and the resulting
|
|
27
|
+
* child jobs are used as direct children of the current parent.
|
|
28
|
+
*/
|
|
29
|
+
private addFlowRecursive;
|
|
30
|
+
/**
|
|
31
|
+
* Close the FlowProducer and release the underlying client connection.
|
|
32
|
+
* Idempotent: safe to call multiple times.
|
|
33
|
+
*/
|
|
34
|
+
close(): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=flow-producer.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.FlowProducer = void 0;
|
|
37
|
+
const job_1 = require("./job");
|
|
38
|
+
const utils_1 = require("./utils");
|
|
39
|
+
const connection_1 = require("./connection");
|
|
40
|
+
const index_1 = require("./functions/index");
|
|
41
|
+
const telemetry_1 = require("./telemetry");
|
|
42
|
+
class FlowProducer {
|
|
43
|
+
opts;
|
|
44
|
+
client = null;
|
|
45
|
+
closing = false;
|
|
46
|
+
constructor(opts) {
|
|
47
|
+
this.opts = opts;
|
|
48
|
+
}
|
|
49
|
+
/** @internal */
|
|
50
|
+
async getClient() {
|
|
51
|
+
if (!this.client) {
|
|
52
|
+
this.client = await (0, connection_1.createClient)(this.opts.connection);
|
|
53
|
+
await (0, connection_1.ensureFunctionLibrary)(this.client, index_1.LIBRARY_SOURCE, this.opts.connection.clusterMode ?? false);
|
|
54
|
+
}
|
|
55
|
+
return this.client;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Add a flow (parent with children) atomically.
|
|
59
|
+
* Children can have their own children (recursive flows), which are flattened
|
|
60
|
+
* into multiple addFlow calls (one per level with children).
|
|
61
|
+
*/
|
|
62
|
+
async add(flow) {
|
|
63
|
+
return (0, telemetry_1.withSpan)('glide-mq.flow.add', {
|
|
64
|
+
'glide-mq.queue': flow.queueName,
|
|
65
|
+
'glide-mq.flow.name': flow.name,
|
|
66
|
+
'glide-mq.flow.childCount': flow.children?.length ?? 0,
|
|
67
|
+
}, async () => {
|
|
68
|
+
const client = await this.getClient();
|
|
69
|
+
return this.addFlowRecursive(client, flow);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Add multiple independent flows.
|
|
74
|
+
*/
|
|
75
|
+
async addBulk(flows) {
|
|
76
|
+
const client = await this.getClient();
|
|
77
|
+
const results = [];
|
|
78
|
+
for (const flow of flows) {
|
|
79
|
+
results.push(await this.addFlowRecursive(client, flow));
|
|
80
|
+
}
|
|
81
|
+
return results;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Recursively add a flow. If children themselves have children,
|
|
85
|
+
* those sub-flows are added first (bottom-up), and the resulting
|
|
86
|
+
* child jobs are used as direct children of the current parent.
|
|
87
|
+
*/
|
|
88
|
+
async addFlowRecursive(client, flow) {
|
|
89
|
+
const prefix = this.opts.prefix ?? 'glide';
|
|
90
|
+
const parentQueueName = flow.queueName;
|
|
91
|
+
const parentKeys = (0, utils_1.buildKeys)(parentQueueName, prefix);
|
|
92
|
+
// If no children, this is a leaf - add as a regular job (not a flow)
|
|
93
|
+
if (!flow.children || flow.children.length === 0) {
|
|
94
|
+
const { addJob } = await Promise.resolve().then(() => __importStar(require('./functions/index')));
|
|
95
|
+
const timestamp = Date.now();
|
|
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);
|
|
98
|
+
const job = new job_1.Job(client, parentKeys, String(jobId), flow.name, flow.data, opts);
|
|
99
|
+
job.timestamp = timestamp;
|
|
100
|
+
return { job };
|
|
101
|
+
}
|
|
102
|
+
// Recursively process children that themselves have children (bottom-up).
|
|
103
|
+
const childNodeMap = new Map();
|
|
104
|
+
for (let i = 0; i < flow.children.length; i++) {
|
|
105
|
+
const child = flow.children[i];
|
|
106
|
+
if (child.children && child.children.length > 0) {
|
|
107
|
+
childNodeMap.set(i, await this.addFlowRecursive(client, child));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Build leaf children data for addFlow (children without sub-children)
|
|
111
|
+
const childrenForLua = flow.children
|
|
112
|
+
.filter((_, i) => !childNodeMap.has(i))
|
|
113
|
+
.map((child) => {
|
|
114
|
+
const childOpts = child.opts ?? {};
|
|
115
|
+
return {
|
|
116
|
+
name: child.name,
|
|
117
|
+
data: JSON.stringify(child.data),
|
|
118
|
+
opts: JSON.stringify(childOpts),
|
|
119
|
+
delay: childOpts.delay ?? 0,
|
|
120
|
+
priority: childOpts.priority ?? 0,
|
|
121
|
+
maxAttempts: childOpts.attempts ?? 0,
|
|
122
|
+
keys: (0, utils_1.buildKeys)(child.queueName, prefix),
|
|
123
|
+
queuePrefix: (0, utils_1.keyPrefix)(prefix, child.queueName),
|
|
124
|
+
parentQueueName: parentQueueName,
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
// Build extra deps for sub-flow children (already created recursively)
|
|
128
|
+
const extraDeps = [];
|
|
129
|
+
for (const [i, subNode] of childNodeMap.entries()) {
|
|
130
|
+
const child = flow.children[i];
|
|
131
|
+
extraDeps.push(`${(0, utils_1.keyPrefix)(prefix, child.queueName)}:${subNode.job.id}`);
|
|
132
|
+
}
|
|
133
|
+
const timestamp = Date.now();
|
|
134
|
+
const parentOpts = flow.opts ?? {};
|
|
135
|
+
const ids = await (0, index_1.addFlow)(client, parentKeys, flow.name, JSON.stringify(flow.data), JSON.stringify(parentOpts), timestamp, parentOpts.delay ?? 0, parentOpts.priority ?? 0, parentOpts.attempts ?? 0, childrenForLua, extraDeps);
|
|
136
|
+
const parentId = ids[0];
|
|
137
|
+
// Set parentId and parentQueue on pre-existing sub-flow children
|
|
138
|
+
for (const [i, subNode] of childNodeMap.entries()) {
|
|
139
|
+
const child = flow.children[i];
|
|
140
|
+
const childKeys = (0, utils_1.buildKeys)(child.queueName, prefix);
|
|
141
|
+
await client.hset(childKeys.job(subNode.job.id), {
|
|
142
|
+
parentId: parentId,
|
|
143
|
+
parentQueue: parentQueueName,
|
|
144
|
+
});
|
|
145
|
+
subNode.job.parentId = parentId;
|
|
146
|
+
subNode.job.parentQueue = parentQueueName;
|
|
147
|
+
}
|
|
148
|
+
// Build JobNode tree - interleave sub-flow and leaf child nodes in original order
|
|
149
|
+
const childNodes = [];
|
|
150
|
+
let leafIdx = 0;
|
|
151
|
+
for (let i = 0; i < flow.children.length; i++) {
|
|
152
|
+
if (childNodeMap.has(i)) {
|
|
153
|
+
childNodes.push(childNodeMap.get(i));
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
const childId = ids[1 + leafIdx];
|
|
157
|
+
const child = flow.children[i];
|
|
158
|
+
const childJob = new job_1.Job(client, (0, utils_1.buildKeys)(child.queueName, prefix), childId, child.name, child.data, child.opts ?? {});
|
|
159
|
+
childJob.timestamp = timestamp;
|
|
160
|
+
childJob.parentId = parentId;
|
|
161
|
+
childJob.parentQueue = parentQueueName;
|
|
162
|
+
childNodes.push({ job: childJob });
|
|
163
|
+
leafIdx++;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
const parentJob = new job_1.Job(client, parentKeys, parentId, flow.name, flow.data, parentOpts);
|
|
167
|
+
parentJob.timestamp = timestamp;
|
|
168
|
+
return { job: parentJob, children: childNodes };
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Close the FlowProducer and release the underlying client connection.
|
|
172
|
+
* Idempotent: safe to call multiple times.
|
|
173
|
+
*/
|
|
174
|
+
async close() {
|
|
175
|
+
if (this.closing)
|
|
176
|
+
return;
|
|
177
|
+
this.closing = true;
|
|
178
|
+
if (this.client) {
|
|
179
|
+
this.client.close();
|
|
180
|
+
this.client = null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
exports.FlowProducer = FlowProducer;
|
|
185
|
+
//# sourceMappingURL=flow-producer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flow-producer.js","sourceRoot":"","sources":["../src/flow-producer.ts"],"names":[],"mappings":"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"}
|