glide-mq 0.3.0 → 0.4.1
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 +196 -13
- package/demo/README.md +169 -0
- package/demo/dashboard-server.ts +474 -0
- package/demo/index.ts +502 -0
- package/demo/package-lock.json +2051 -0
- package/demo/package.json +26 -0
- package/dist/functions/index.d.ts +9 -2
- package/dist/functions/index.d.ts.map +1 -1
- package/dist/functions/index.js +81 -11
- package/dist/functions/index.js.map +1 -1
- package/dist/graceful-shutdown.d.ts +5 -1
- package/dist/graceful-shutdown.d.ts.map +1 -1
- package/dist/graceful-shutdown.js +31 -11
- package/dist/graceful-shutdown.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/queue.d.ts +17 -1
- package/dist/queue.d.ts.map +1 -1
- package/dist/queue.js +168 -27
- package/dist/queue.js.map +1 -1
- package/dist/testing.d.ts +109 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +284 -0
- package/dist/testing.js.map +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +13 -1
package/demo/index.ts
ADDED
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Queue, Worker, FlowProducer, QueueEvents, chain, group } from 'glide-mq';
|
|
3
|
+
import type { Job } from 'glide-mq';
|
|
4
|
+
import chalk from 'chalk';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
import Table from 'cli-table3';
|
|
7
|
+
import { setTimeout } from 'timers/promises';
|
|
8
|
+
|
|
9
|
+
// Connection config
|
|
10
|
+
const connection = {
|
|
11
|
+
addresses: [{ host: 'localhost', port: 6379 }],
|
|
12
|
+
clusterMode: false,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// Color-coded console logging
|
|
16
|
+
const log = {
|
|
17
|
+
info: (msg: string) => console.log(chalk.cyan('[INFO]'), msg),
|
|
18
|
+
success: (msg: string) => console.log(chalk.green('[SUCCESS]'), msg),
|
|
19
|
+
error: (msg: string) => console.log(chalk.red('[ERROR]'), msg),
|
|
20
|
+
warn: (msg: string) => console.log(chalk.yellow('[WARN]'), msg),
|
|
21
|
+
job: (msg: string) => console.log(chalk.magenta('[JOB]'), msg),
|
|
22
|
+
flow: (msg: string) => console.log(chalk.blue('[FLOW]'), msg),
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// Queue definitions with different features
|
|
26
|
+
const queues = {
|
|
27
|
+
orders: new Queue('orders', { connection, compression: 'gzip' }),
|
|
28
|
+
payments: new Queue('payments', { connection }),
|
|
29
|
+
inventory: new Queue('inventory', { connection }),
|
|
30
|
+
shipping: new Queue('shipping', { connection }),
|
|
31
|
+
notifications: new Queue('notifications', { connection }),
|
|
32
|
+
analytics: new Queue('analytics', { connection }),
|
|
33
|
+
recommendations: new Queue('recommendations', { connection }),
|
|
34
|
+
reports: new Queue('reports', { connection }),
|
|
35
|
+
deadLetter: new Queue('dead-letter', { connection }),
|
|
36
|
+
priority: new Queue('priority-tasks', { connection }),
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// Flow producer for complex workflows
|
|
40
|
+
const flowProducer = new FlowProducer({ connection });
|
|
41
|
+
|
|
42
|
+
// Event listeners for all queues
|
|
43
|
+
const setupQueueEvents = () => {
|
|
44
|
+
Object.entries(queues).forEach(([name, queue]) => {
|
|
45
|
+
const events = new QueueEvents(queue.name, { connection });
|
|
46
|
+
|
|
47
|
+
events.on('added', ({ jobId }) => {
|
|
48
|
+
log.job(`${name}: Job ${jobId} added`);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
events.on('completed', ({ jobId, returnvalue }) => {
|
|
52
|
+
log.success(`${name}: Job ${jobId} completed with result: ${JSON.stringify(returnvalue)}`);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
events.on('failed', ({ jobId, failedReason }) => {
|
|
56
|
+
log.error(`${name}: Job ${jobId} failed - ${failedReason}`);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
events.on('progress', ({ jobId, data }) => {
|
|
60
|
+
log.info(`${name}: Job ${jobId} progress: ${data}%`);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
events.on('stalled', ({ jobId }) => {
|
|
64
|
+
log.warn(`${name}: Job ${jobId} stalled`);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Worker implementations
|
|
70
|
+
const setupWorkers = () => {
|
|
71
|
+
// Order processing worker with progress tracking
|
|
72
|
+
const orderWorker = new Worker('orders', async (job: Job) => {
|
|
73
|
+
await job.log(`Processing order ${job.data.orderId}`);
|
|
74
|
+
await job.updateProgress(25);
|
|
75
|
+
|
|
76
|
+
// Simulate order validation
|
|
77
|
+
await setTimeout(500);
|
|
78
|
+
await job.updateProgress(50);
|
|
79
|
+
|
|
80
|
+
// Simulate inventory check
|
|
81
|
+
await setTimeout(500);
|
|
82
|
+
await job.updateProgress(75);
|
|
83
|
+
|
|
84
|
+
// Complete order
|
|
85
|
+
await setTimeout(500);
|
|
86
|
+
await job.updateProgress(100);
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
orderId: job.data.orderId,
|
|
90
|
+
status: 'processed',
|
|
91
|
+
items: job.data.items,
|
|
92
|
+
total: job.data.total
|
|
93
|
+
};
|
|
94
|
+
}, {
|
|
95
|
+
connection,
|
|
96
|
+
concurrency: 5,
|
|
97
|
+
stalledInterval: 30000,
|
|
98
|
+
deadLetterQueue: { name: 'dead-letter' },
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// Payment processor with retries
|
|
102
|
+
const paymentWorker = new Worker('payments', async (job: Job) => {
|
|
103
|
+
await job.log(`Processing payment for order ${job.data.orderId}`);
|
|
104
|
+
|
|
105
|
+
// Simulate payment gateway call
|
|
106
|
+
await setTimeout(1000);
|
|
107
|
+
|
|
108
|
+
// Random failure for retry demonstration
|
|
109
|
+
if (Math.random() < 0.2 && job.attemptsMade < 2) {
|
|
110
|
+
throw new Error('Payment gateway timeout');
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
orderId: job.data.orderId,
|
|
115
|
+
transactionId: `TXN-${Date.now()}`,
|
|
116
|
+
amount: job.data.amount,
|
|
117
|
+
status: 'completed'
|
|
118
|
+
};
|
|
119
|
+
}, {
|
|
120
|
+
connection,
|
|
121
|
+
concurrency: 3,
|
|
122
|
+
backoffStrategies: {
|
|
123
|
+
exponential: (attemptsMade) => 2 ** attemptsMade * 1000,
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Inventory worker with rate limiting
|
|
128
|
+
const inventoryWorker = new Worker('inventory', async (job: Job) => {
|
|
129
|
+
await job.log(`Updating inventory for SKUs: ${job.data.skus.join(', ')}`);
|
|
130
|
+
await setTimeout(300);
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
updated: job.data.skus.length,
|
|
134
|
+
timestamp: new Date().toISOString()
|
|
135
|
+
};
|
|
136
|
+
}, {
|
|
137
|
+
connection,
|
|
138
|
+
concurrency: 10,
|
|
139
|
+
limiter: { max: 50, duration: 60000 }, // 50 jobs per minute
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
// Shipping worker
|
|
143
|
+
const shippingWorker = new Worker('shipping', async (job: Job) => {
|
|
144
|
+
await job.log(`Creating shipping label for order ${job.data.orderId}`);
|
|
145
|
+
await setTimeout(800);
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
orderId: job.data.orderId,
|
|
149
|
+
trackingNumber: `TRACK-${Date.now()}`,
|
|
150
|
+
carrier: ['FedEx', 'UPS', 'USPS'][Math.floor(Math.random() * 3)],
|
|
151
|
+
estimatedDelivery: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000).toISOString()
|
|
152
|
+
};
|
|
153
|
+
}, {
|
|
154
|
+
connection,
|
|
155
|
+
concurrency: 5,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// Notification worker with multiple channels
|
|
159
|
+
const notificationWorker = new Worker('notifications', async (job: Job) => {
|
|
160
|
+
const { type, recipient, message } = job.data;
|
|
161
|
+
await job.log(`Sending ${type} notification to ${recipient}`);
|
|
162
|
+
|
|
163
|
+
// Simulate different notification channels
|
|
164
|
+
const delay = type === 'email' ? 500 : type === 'sms' ? 300 : 100;
|
|
165
|
+
await setTimeout(delay);
|
|
166
|
+
|
|
167
|
+
return {
|
|
168
|
+
sent: true,
|
|
169
|
+
channel: type,
|
|
170
|
+
recipient,
|
|
171
|
+
timestamp: new Date().toISOString()
|
|
172
|
+
};
|
|
173
|
+
}, {
|
|
174
|
+
connection,
|
|
175
|
+
concurrency: 20,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Analytics worker for aggregation
|
|
179
|
+
const analyticsWorker = new Worker('analytics', async (job: Job) => {
|
|
180
|
+
await job.log(`Processing analytics event: ${job.data.event}`);
|
|
181
|
+
await setTimeout(200);
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
event: job.data.event,
|
|
185
|
+
processed: true,
|
|
186
|
+
metrics: {
|
|
187
|
+
views: Math.floor(Math.random() * 1000),
|
|
188
|
+
clicks: Math.floor(Math.random() * 100),
|
|
189
|
+
conversions: Math.floor(Math.random() * 10)
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
}, {
|
|
193
|
+
connection,
|
|
194
|
+
concurrency: 15,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// Recommendation engine worker
|
|
198
|
+
const recommendationWorker = new Worker('recommendations', async (job: Job) => {
|
|
199
|
+
await job.log(`Generating recommendations for user ${job.data.userId}`);
|
|
200
|
+
await setTimeout(1500);
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
userId: job.data.userId,
|
|
204
|
+
recommendations: [
|
|
205
|
+
{ productId: 'PROD-001', score: 0.95 },
|
|
206
|
+
{ productId: 'PROD-002', score: 0.87 },
|
|
207
|
+
{ productId: 'PROD-003', score: 0.82 },
|
|
208
|
+
]
|
|
209
|
+
};
|
|
210
|
+
}, {
|
|
211
|
+
connection,
|
|
212
|
+
concurrency: 2,
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// Report generator with long-running tasks
|
|
216
|
+
const reportWorker = new Worker('reports', async (job: Job) => {
|
|
217
|
+
await job.log(`Generating ${job.data.type} report`);
|
|
218
|
+
|
|
219
|
+
// Simulate long-running report generation
|
|
220
|
+
for (let i = 0; i <= 100; i += 10) {
|
|
221
|
+
await setTimeout(500);
|
|
222
|
+
await job.updateProgress(i);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
reportId: `REPORT-${Date.now()}`,
|
|
227
|
+
type: job.data.type,
|
|
228
|
+
url: `https://reports.example.com/${Date.now()}.pdf`,
|
|
229
|
+
pages: Math.floor(Math.random() * 50) + 10
|
|
230
|
+
};
|
|
231
|
+
}, {
|
|
232
|
+
connection,
|
|
233
|
+
concurrency: 1,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
// Priority task worker
|
|
237
|
+
const priorityWorker = new Worker('priority-tasks', async (job: Job) => {
|
|
238
|
+
await job.log(`Processing priority task: ${job.data.task}`);
|
|
239
|
+
await setTimeout(100);
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
task: job.data.task,
|
|
243
|
+
priority: job.opts.priority,
|
|
244
|
+
completed: new Date().toISOString()
|
|
245
|
+
};
|
|
246
|
+
}, {
|
|
247
|
+
connection,
|
|
248
|
+
concurrency: 10,
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// Dead letter queue worker for investigation
|
|
252
|
+
const deadLetterWorker = new Worker('dead-letter', async (job: Job) => {
|
|
253
|
+
await job.log(`Investigating failed job from ${job.data.originalQueue}`);
|
|
254
|
+
// Here you would typically log to external system or alert
|
|
255
|
+
return { investigated: true };
|
|
256
|
+
}, {
|
|
257
|
+
connection,
|
|
258
|
+
concurrency: 1,
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
return {
|
|
262
|
+
orderWorker,
|
|
263
|
+
paymentWorker,
|
|
264
|
+
inventoryWorker,
|
|
265
|
+
shippingWorker,
|
|
266
|
+
notificationWorker,
|
|
267
|
+
analyticsWorker,
|
|
268
|
+
recommendationWorker,
|
|
269
|
+
reportWorker,
|
|
270
|
+
priorityWorker,
|
|
271
|
+
deadLetterWorker,
|
|
272
|
+
};
|
|
273
|
+
};
|
|
274
|
+
|
|
275
|
+
// Demo scenarios
|
|
276
|
+
async function runDemoScenarios() {
|
|
277
|
+
const spinner = ora('Starting demo scenarios...').start();
|
|
278
|
+
|
|
279
|
+
// Scenario 1: Simple job with progress
|
|
280
|
+
spinner.text = 'Scenario 1: Simple order processing';
|
|
281
|
+
await queues.orders.add('process-order', {
|
|
282
|
+
orderId: 'ORD-001',
|
|
283
|
+
items: ['SKU-123', 'SKU-456'],
|
|
284
|
+
total: 299.99
|
|
285
|
+
});
|
|
286
|
+
await setTimeout(2000);
|
|
287
|
+
|
|
288
|
+
// Scenario 2: Bulk operations
|
|
289
|
+
spinner.text = 'Scenario 2: Bulk inventory update';
|
|
290
|
+
const bulkJobs = Array.from({ length: 20 }, (_, i) => ({
|
|
291
|
+
name: 'update-inventory',
|
|
292
|
+
data: { skus: [`SKU-${i}00`, `SKU-${i}01`, `SKU-${i}02`] }
|
|
293
|
+
}));
|
|
294
|
+
await queues.inventory.addBulk(bulkJobs);
|
|
295
|
+
await setTimeout(1000);
|
|
296
|
+
|
|
297
|
+
// Scenario 3: Priority jobs
|
|
298
|
+
spinner.text = 'Scenario 3: Priority tasks';
|
|
299
|
+
await queues.priority.add('urgent-task',
|
|
300
|
+
{ task: 'Critical system update' },
|
|
301
|
+
{ priority: 1 }
|
|
302
|
+
);
|
|
303
|
+
await queues.priority.add('normal-task',
|
|
304
|
+
{ task: 'Regular maintenance' },
|
|
305
|
+
{ priority: 5 }
|
|
306
|
+
);
|
|
307
|
+
await queues.priority.add('low-task',
|
|
308
|
+
{ task: 'Cleanup operation' },
|
|
309
|
+
{ priority: 10 }
|
|
310
|
+
);
|
|
311
|
+
await setTimeout(1000);
|
|
312
|
+
|
|
313
|
+
// Scenario 4: Delayed jobs
|
|
314
|
+
spinner.text = 'Scenario 4: Scheduled notifications';
|
|
315
|
+
await queues.notifications.add('reminder',
|
|
316
|
+
{ type: 'email', recipient: 'user@example.com', message: 'Order shipped!' },
|
|
317
|
+
{ delay: 5000 }
|
|
318
|
+
);
|
|
319
|
+
await setTimeout(1000);
|
|
320
|
+
|
|
321
|
+
// Scenario 5: Job with retries
|
|
322
|
+
spinner.text = 'Scenario 5: Payment processing with retries';
|
|
323
|
+
await queues.payments.add('process-payment',
|
|
324
|
+
{ orderId: 'ORD-002', amount: 199.99 },
|
|
325
|
+
{ attempts: 3, backoff: { type: 'exponential', delay: 1000 } }
|
|
326
|
+
);
|
|
327
|
+
await setTimeout(2000);
|
|
328
|
+
|
|
329
|
+
// Scenario 6: Deduplication
|
|
330
|
+
spinner.text = 'Scenario 6: Deduplicated analytics events';
|
|
331
|
+
for (let i = 0; i < 5; i++) {
|
|
332
|
+
await queues.analytics.add('track-event',
|
|
333
|
+
{ event: 'page_view', userId: 'USER-123', page: '/home' },
|
|
334
|
+
{ deduplication: { id: 'pageview-home-123', mode: 'simple' } }
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
await setTimeout(1000);
|
|
338
|
+
|
|
339
|
+
// Scenario 7: Complex workflow with FlowProducer
|
|
340
|
+
spinner.text = 'Scenario 7: E-commerce order workflow';
|
|
341
|
+
await flowProducer.add({
|
|
342
|
+
name: 'complete-order',
|
|
343
|
+
queueName: 'orders',
|
|
344
|
+
data: { orderId: 'ORD-003', items: ['PROD-A', 'PROD-B'], total: 499.99 },
|
|
345
|
+
children: [
|
|
346
|
+
{
|
|
347
|
+
name: 'process-payment',
|
|
348
|
+
queueName: 'payments',
|
|
349
|
+
data: { orderId: 'ORD-003', amount: 499.99 },
|
|
350
|
+
children: [
|
|
351
|
+
{
|
|
352
|
+
name: 'update-inventory',
|
|
353
|
+
queueName: 'inventory',
|
|
354
|
+
data: { skus: ['PROD-A', 'PROD-B'] }
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
name: 'create-shipping',
|
|
358
|
+
queueName: 'shipping',
|
|
359
|
+
data: { orderId: 'ORD-003', address: '123 Main St' }
|
|
360
|
+
}
|
|
361
|
+
]
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
name: 'send-confirmation',
|
|
365
|
+
queueName: 'notifications',
|
|
366
|
+
data: { type: 'email', recipient: 'customer@example.com', message: 'Order confirmed!' }
|
|
367
|
+
}
|
|
368
|
+
]
|
|
369
|
+
});
|
|
370
|
+
await setTimeout(3000);
|
|
371
|
+
|
|
372
|
+
// Scenario 8: Chain pattern - Sequential pipeline
|
|
373
|
+
spinner.text = 'Scenario 8: Sequential data pipeline';
|
|
374
|
+
await chain('analytics', [
|
|
375
|
+
{ name: 'collect-data', data: { source: 'api', endpoint: '/metrics' } },
|
|
376
|
+
{ name: 'transform-data', data: { format: 'json' } },
|
|
377
|
+
{ name: 'aggregate-data', data: { window: '1h' } },
|
|
378
|
+
{ name: 'store-data', data: { destination: 'warehouse' } }
|
|
379
|
+
], connection);
|
|
380
|
+
await setTimeout(2000);
|
|
381
|
+
|
|
382
|
+
// Scenario 9: Group pattern - Parallel execution
|
|
383
|
+
spinner.text = 'Scenario 9: Parallel notification broadcast';
|
|
384
|
+
await group('notifications', [
|
|
385
|
+
{ name: 'send-email', data: { type: 'email', recipient: 'user1@example.com', message: 'New feature!' } },
|
|
386
|
+
{ name: 'send-sms', data: { type: 'sms', recipient: '+1234567890', message: 'New feature!' } },
|
|
387
|
+
{ name: 'send-push', data: { type: 'push', recipient: 'device-token-123', message: 'New feature!' } }
|
|
388
|
+
], connection);
|
|
389
|
+
await setTimeout(2000);
|
|
390
|
+
|
|
391
|
+
// Scenario 10: Large payload with compression
|
|
392
|
+
spinner.text = 'Scenario 10: Large report generation with compression';
|
|
393
|
+
const largeData = {
|
|
394
|
+
reportType: 'annual',
|
|
395
|
+
data: Array.from({ length: 1000 }, (_, i) => ({
|
|
396
|
+
id: i,
|
|
397
|
+
timestamp: new Date().toISOString(),
|
|
398
|
+
metrics: {
|
|
399
|
+
revenue: Math.random() * 10000,
|
|
400
|
+
orders: Math.floor(Math.random() * 100),
|
|
401
|
+
customers: Math.floor(Math.random() * 50)
|
|
402
|
+
}
|
|
403
|
+
}))
|
|
404
|
+
};
|
|
405
|
+
await queues.reports.add('generate-annual-report', largeData);
|
|
406
|
+
await setTimeout(3000);
|
|
407
|
+
|
|
408
|
+
// Scenario 11: Rate-limited batch processing
|
|
409
|
+
spinner.text = 'Scenario 11: Rate-limited recommendation generation';
|
|
410
|
+
const userIds = Array.from({ length: 10 }, (_, i) => `USER-${i}`);
|
|
411
|
+
for (const userId of userIds) {
|
|
412
|
+
await queues.recommendations.add('generate-recommendations', { userId });
|
|
413
|
+
}
|
|
414
|
+
await setTimeout(5000);
|
|
415
|
+
|
|
416
|
+
// Scenario 12: Job with timeout demonstration
|
|
417
|
+
spinner.text = 'Scenario 12: Job timeout handling';
|
|
418
|
+
await queues.reports.add('slow-report',
|
|
419
|
+
{ type: 'detailed-analysis' },
|
|
420
|
+
{ timeout: 3000 } // Will timeout if takes > 3s
|
|
421
|
+
);
|
|
422
|
+
await setTimeout(2000);
|
|
423
|
+
|
|
424
|
+
spinner.succeed('All demo scenarios launched!');
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Display metrics
|
|
428
|
+
async function displayMetrics() {
|
|
429
|
+
console.log('\n' + chalk.bold.cyan('Queue Metrics:'));
|
|
430
|
+
|
|
431
|
+
const table = new Table({
|
|
432
|
+
head: ['Queue', 'Waiting', 'Active', 'Delayed', 'Completed', 'Failed'],
|
|
433
|
+
colWidths: [20, 10, 10, 10, 12, 10],
|
|
434
|
+
style: { head: ['cyan'] }
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
for (const [name, queue] of Object.entries(queues)) {
|
|
438
|
+
const counts = await queue.getJobCounts();
|
|
439
|
+
table.push([
|
|
440
|
+
name,
|
|
441
|
+
counts.waiting || 0,
|
|
442
|
+
counts.active || 0,
|
|
443
|
+
counts.delayed || 0,
|
|
444
|
+
counts.completed || 0,
|
|
445
|
+
counts.failed || 0
|
|
446
|
+
]);
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
console.log(table.toString());
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// Main execution
|
|
453
|
+
async function main() {
|
|
454
|
+
console.log(chalk.bold.green('\n[OK] Starting glide-mq Comprehensive Demo\n'));
|
|
455
|
+
console.log(chalk.gray('This demo showcases all major features of glide-mq:\n'));
|
|
456
|
+
console.log(chalk.gray('- Job processing with progress tracking'));
|
|
457
|
+
console.log(chalk.gray('- Bulk operations and compression'));
|
|
458
|
+
console.log(chalk.gray('- Priority queues and delayed jobs'));
|
|
459
|
+
console.log(chalk.gray('- Retries with backoff strategies'));
|
|
460
|
+
console.log(chalk.gray('- Deduplication and rate limiting'));
|
|
461
|
+
console.log(chalk.gray('- Complex workflows and patterns'));
|
|
462
|
+
console.log(chalk.gray('- Dead letter queue handling'));
|
|
463
|
+
console.log(chalk.gray('- And much more...\n'));
|
|
464
|
+
|
|
465
|
+
// Setup
|
|
466
|
+
setupQueueEvents();
|
|
467
|
+
const workers = setupWorkers();
|
|
468
|
+
|
|
469
|
+
// Run demo scenarios
|
|
470
|
+
await runDemoScenarios();
|
|
471
|
+
|
|
472
|
+
// Display metrics periodically
|
|
473
|
+
const metricsInterval = setInterval(displayMetrics, 5000);
|
|
474
|
+
await displayMetrics();
|
|
475
|
+
|
|
476
|
+
// Keep running
|
|
477
|
+
console.log(chalk.green('\n[OK] Demo is running. Press Ctrl+C to stop.\n'));
|
|
478
|
+
|
|
479
|
+
// Graceful shutdown
|
|
480
|
+
process.on('SIGINT', async () => {
|
|
481
|
+
console.log(chalk.yellow('\n[WARN] Shutting down gracefully...'));
|
|
482
|
+
clearInterval(metricsInterval);
|
|
483
|
+
|
|
484
|
+
// Close all workers
|
|
485
|
+
await Promise.all(Object.values(workers).map(w => w.close()));
|
|
486
|
+
|
|
487
|
+
// Close all queues
|
|
488
|
+
await Promise.all(Object.values(queues).map(q => q.close()));
|
|
489
|
+
|
|
490
|
+
console.log(chalk.green('[OK] Demo stopped.'));
|
|
491
|
+
process.exit(0);
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Error handling
|
|
496
|
+
process.on('unhandledRejection', (err) => {
|
|
497
|
+
log.error(`Unhandled rejection: ${err}`);
|
|
498
|
+
console.error(err);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
// Start the demo
|
|
502
|
+
main().catch(console.error);
|