@pingpolls/redisq 0.1.1 ā 0.2.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 +31 -156
- package/app.test.ts +59 -14
- package/app.ts +48 -53
- package/benchmark/medium.txt +1 -0
- package/benchmark/small.txt +1 -0
- package/benchmark/stress-worker.ts +21 -8
- package/benchmark/stress.ts +43 -38
- package/benchmark/tiny.txt +1 -0
- package/biome.json +0 -1
- package/compose.yml +5 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -45,8 +45,8 @@ await queue.sendMessage({
|
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
// Start a worker to process messages
|
|
48
|
-
await queue.startWorker('emails', async (
|
|
49
|
-
const email = JSON.parse(
|
|
48
|
+
await queue.startWorker('emails', async (received) => {
|
|
49
|
+
const email = JSON.parse(received.message);
|
|
50
50
|
console.log(`Sending email to ${email.to}`);
|
|
51
51
|
|
|
52
52
|
// Process your message here
|
|
@@ -70,8 +70,6 @@ Batch queues (suffix `:batch`) collect messages over a time period and process t
|
|
|
70
70
|
|
|
71
71
|
### SvelteKit
|
|
72
72
|
|
|
73
|
-
#### Option 1: Direct Import (Recommended for Simple Apps)
|
|
74
|
-
|
|
75
73
|
Create a queue service in `src/lib/server/queue.ts`:
|
|
76
74
|
|
|
77
75
|
```typescript
|
|
@@ -102,8 +100,8 @@ import { queue, initQueues } from '$lib/server/queue';
|
|
|
102
100
|
await initQueues();
|
|
103
101
|
|
|
104
102
|
// Start email worker
|
|
105
|
-
queue.startWorker('emails', async (
|
|
106
|
-
const data = JSON.parse(
|
|
103
|
+
queue.startWorker('emails', async (received) => {
|
|
104
|
+
const data = JSON.parse(received.message);
|
|
107
105
|
await sendEmail(data);
|
|
108
106
|
return { success: true };
|
|
109
107
|
}, { silent: false });
|
|
@@ -160,140 +158,6 @@ export async function load() {
|
|
|
160
158
|
}
|
|
161
159
|
```
|
|
162
160
|
|
|
163
|
-
#### Option 2: Using event.locals (Recommended for Larger Apps)
|
|
164
|
-
|
|
165
|
-
This approach provides better dependency injection and testing capabilities with SvelteKit 5.
|
|
166
|
-
|
|
167
|
-
Create the queue service in `src/lib/server/queue.ts`:
|
|
168
|
-
|
|
169
|
-
```typescript
|
|
170
|
-
import RedisQueue from '@pingpolls/redisq';
|
|
171
|
-
|
|
172
|
-
export function createQueue() {
|
|
173
|
-
return new RedisQueue({
|
|
174
|
-
host: import.meta.env.REDIS_HOST || '127.0.0.1',
|
|
175
|
-
port: import.meta.env.REDIS_PORT || '6379',
|
|
176
|
-
namespace: 'sveltekit-app'
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
export async function initQueues(queue: RedisQueue) {
|
|
181
|
-
await queue.createQueue({ qname: 'emails', maxRetries: 3 });
|
|
182
|
-
await queue.createQueue({
|
|
183
|
-
qname: 'analytics:batch',
|
|
184
|
-
every: 60,
|
|
185
|
-
maxRetries: 2
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
Setup in `src/hooks.server.ts`:
|
|
191
|
-
|
|
192
|
-
```typescript
|
|
193
|
-
import type { Handle } from '@sveltejs/kit';
|
|
194
|
-
import { createQueue, initQueues } from '$lib/server/queue';
|
|
195
|
-
|
|
196
|
-
// Create single queue instance
|
|
197
|
-
const queue = createQueue();
|
|
198
|
-
await initQueues(queue);
|
|
199
|
-
|
|
200
|
-
// Start workers
|
|
201
|
-
queue.startWorker('emails', async (message) => {
|
|
202
|
-
const data = JSON.parse(message.message);
|
|
203
|
-
await sendEmail(data);
|
|
204
|
-
return { success: true };
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
queue.startWorker('analytics:batch', async (batch) => {
|
|
208
|
-
await saveToDB(batch.messages);
|
|
209
|
-
return { success: true };
|
|
210
|
-
});
|
|
211
|
-
|
|
212
|
-
export const handle: Handle = async ({ event, resolve }) => {
|
|
213
|
-
// Attach queue to event.locals
|
|
214
|
-
event.locals.queue = queue;
|
|
215
|
-
|
|
216
|
-
return resolve(event);
|
|
217
|
-
};
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
Add TypeScript types in `src/app.d.ts`:
|
|
221
|
-
|
|
222
|
-
```typescript
|
|
223
|
-
import type RedisQueue from '@pingpolls/redisq';
|
|
224
|
-
|
|
225
|
-
declare global {
|
|
226
|
-
namespace App {
|
|
227
|
-
interface Locals {
|
|
228
|
-
queue: RedisQueue;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
export {};
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
Now use `event.locals.queue` in your routes:
|
|
237
|
-
|
|
238
|
-
```typescript
|
|
239
|
-
// src/routes/api/signup/+server.ts
|
|
240
|
-
import { json } from '@sveltejs/kit';
|
|
241
|
-
import type { RequestHandler } from './$types';
|
|
242
|
-
|
|
243
|
-
export const POST: RequestHandler = async ({ request, locals }) => {
|
|
244
|
-
const { email, name } = await request.json();
|
|
245
|
-
|
|
246
|
-
// Access queue from locals
|
|
247
|
-
await locals.queue.sendMessage({
|
|
248
|
-
qname: 'emails',
|
|
249
|
-
message: JSON.stringify({ to: email, template: 'welcome', name })
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
await locals.queue.sendBatchMessage({
|
|
253
|
-
qname: 'analytics:batch',
|
|
254
|
-
batchId: 'signups',
|
|
255
|
-
message: JSON.stringify({ event: 'signup', email, timestamp: Date.now() })
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
return json({ success: true });
|
|
259
|
-
};
|
|
260
|
-
```
|
|
261
|
-
|
|
262
|
-
```typescript
|
|
263
|
-
// src/routes/orders/[id]/+page.server.ts
|
|
264
|
-
import type { PageServerLoad, Actions } from './$types';
|
|
265
|
-
|
|
266
|
-
export const load: PageServerLoad = async ({ params, locals }) => {
|
|
267
|
-
// Queue background task
|
|
268
|
-
await locals.queue.sendMessage({
|
|
269
|
-
qname: 'emails',
|
|
270
|
-
message: JSON.stringify({ type: 'order-confirmation', orderId: params.id })
|
|
271
|
-
});
|
|
272
|
-
|
|
273
|
-
return {
|
|
274
|
-
orderId: params.id
|
|
275
|
-
};
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
export const actions: Actions = {
|
|
279
|
-
cancel: async ({ params, locals }) => {
|
|
280
|
-
await locals.queue.sendMessage({
|
|
281
|
-
qname: 'emails',
|
|
282
|
-
message: JSON.stringify({ type: 'order-cancellation', orderId: params.id })
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
return { success: true };
|
|
286
|
-
}
|
|
287
|
-
};
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
**Benefits of using `event.locals`:**
|
|
291
|
-
- Single queue instance across your app
|
|
292
|
-
- Better for dependency injection and testing
|
|
293
|
-
- Cleaner imports in routes
|
|
294
|
-
- Type-safe access with TypeScript
|
|
295
|
-
- Follows SvelteKit 5 best practices
|
|
296
|
-
|
|
297
161
|
### Next.js (App Router)
|
|
298
162
|
|
|
299
163
|
Create queue in `lib/queue.ts`:
|
|
@@ -317,8 +181,8 @@ import { queue } from '@/lib/queue';
|
|
|
317
181
|
await queue.createQueue({ qname: 'notifications', maxRetries: 3 });
|
|
318
182
|
|
|
319
183
|
// Start worker
|
|
320
|
-
queue.startWorker('notifications', async (
|
|
321
|
-
const notification = JSON.parse(
|
|
184
|
+
queue.startWorker('notifications', async (received) => {
|
|
185
|
+
const notification = JSON.parse(received.message);
|
|
322
186
|
await sendPushNotification(notification);
|
|
323
187
|
return { success: true };
|
|
324
188
|
});
|
|
@@ -365,8 +229,8 @@ export default defineNitroPlugin(async (nitroApp) => {
|
|
|
365
229
|
await queue.createQueue({ qname: 'tasks', maxRetries: 3 });
|
|
366
230
|
|
|
367
231
|
// Start worker
|
|
368
|
-
queue.startWorker('tasks', async (
|
|
369
|
-
const task = JSON.parse(
|
|
232
|
+
queue.startWorker('tasks', async (received) => {
|
|
233
|
+
const task = JSON.parse(received.message);
|
|
370
234
|
await processTask(task);
|
|
371
235
|
return { success: true };
|
|
372
236
|
});
|
|
@@ -415,8 +279,8 @@ await queue.createQueue({
|
|
|
415
279
|
});
|
|
416
280
|
|
|
417
281
|
// Start regular queue worker
|
|
418
|
-
queue.startWorker('webhooks', async (
|
|
419
|
-
const webhook = JSON.parse(
|
|
282
|
+
queue.startWorker('webhooks', async (received) => {
|
|
283
|
+
const webhook = JSON.parse(received.message);
|
|
420
284
|
await fetch(webhook.url, {
|
|
421
285
|
method: 'POST',
|
|
422
286
|
body: JSON.stringify(webhook.data),
|
|
@@ -543,11 +407,11 @@ Starts a worker to process messages.
|
|
|
543
407
|
|
|
544
408
|
```typescript
|
|
545
409
|
// Regular queue worker
|
|
546
|
-
await queue.startWorker('my-queue', async (
|
|
547
|
-
console.log(
|
|
548
|
-
console.log(
|
|
549
|
-
console.log(
|
|
550
|
-
console.log(
|
|
410
|
+
await queue.startWorker('my-queue', async (received) => {
|
|
411
|
+
console.log(received.id);
|
|
412
|
+
console.log(received.message);
|
|
413
|
+
console.log(received.attempt);
|
|
414
|
+
console.log(received.sent);
|
|
551
415
|
|
|
552
416
|
return { success: true }; // or { success: false } to retry
|
|
553
417
|
}, {
|
|
@@ -757,14 +621,25 @@ Run the stress test to benchmark on your hardware:
|
|
|
757
621
|
bun stress
|
|
758
622
|
```
|
|
759
623
|
|
|
760
|
-
|
|
761
|
-
- **
|
|
762
|
-
- **
|
|
763
|
-
- **
|
|
764
|
-
|
|
624
|
+
Individual Queue Performance:
|
|
625
|
+
- **Tiny messages (100B)**: 47181 msg/s (p50: 49.97ms)
|
|
626
|
+
- **Small messages (1KB)**: 33709 msg/s (p50: 69.05ms)
|
|
627
|
+
- **Medium messages (10KB)**: 4056 msg/s (p50: 261.32ms)
|
|
628
|
+
|
|
629
|
+
Overall:
|
|
630
|
+
- **Throughput**: ~28,315 messages/second
|
|
631
|
+
- **Latency (p50)**: 126.78 ms
|
|
632
|
+
- **Latency (p95)**: 299.27 ms
|
|
633
|
+
- **Latency (p99)**: 383.86 ms
|
|
634
|
+
|
|
635
|
+
Tested on Dockerized `redis:alpine` through WSL2 with 1 CPU and 1GB instance.
|
|
765
636
|
|
|
766
637
|
*Results may vary based on hardware, Redis configuration, and network conditions.*
|
|
767
638
|
|
|
639
|
+
## Contributing
|
|
640
|
+
|
|
641
|
+
Check our github repository [here](https://github.com/pingpolls/redisq).
|
|
642
|
+
|
|
768
643
|
## License
|
|
769
644
|
|
|
770
645
|
Apache License 2.0
|
package/app.test.ts
CHANGED
|
@@ -22,7 +22,7 @@ describe("RedisQueue Tests", () => {
|
|
|
22
22
|
|
|
23
23
|
await queue.createQueue({ qname: "test-basic" });
|
|
24
24
|
|
|
25
|
-
const message =
|
|
25
|
+
const message = JSON.stringify({ id: 1, name: "John Doe" });
|
|
26
26
|
let receivedCount = 0;
|
|
27
27
|
|
|
28
28
|
const id = await queue.sendMessage({
|
|
@@ -61,9 +61,9 @@ describe("RedisQueue Tests", () => {
|
|
|
61
61
|
|
|
62
62
|
const sentAt = Date.now();
|
|
63
63
|
const id = await queue.sendMessage({
|
|
64
|
+
delay: delayMs,
|
|
64
65
|
message,
|
|
65
66
|
qname: "test-delayed",
|
|
66
|
-
delay: delayMs,
|
|
67
67
|
});
|
|
68
68
|
|
|
69
69
|
await new Promise<void>((resolve) => {
|
|
@@ -100,9 +100,9 @@ describe("RedisQueue Tests", () => {
|
|
|
100
100
|
const queue = new RedisQueue(redisConfig);
|
|
101
101
|
|
|
102
102
|
await queue.createQueue({
|
|
103
|
-
qname: "test-retry",
|
|
104
|
-
maxRetries: 3,
|
|
105
103
|
maxBackoffSeconds: 1,
|
|
104
|
+
maxRetries: 3,
|
|
105
|
+
qname: "test-retry",
|
|
106
106
|
});
|
|
107
107
|
|
|
108
108
|
const message = "Retry me!";
|
|
@@ -144,9 +144,9 @@ describe("RedisQueue Tests", () => {
|
|
|
144
144
|
const queue = new RedisQueue(redisConfig);
|
|
145
145
|
|
|
146
146
|
await queue.createQueue({
|
|
147
|
-
qname: "test-delayed-retry",
|
|
148
|
-
maxRetries: 2,
|
|
149
147
|
maxBackoffSeconds: 1,
|
|
148
|
+
maxRetries: 2,
|
|
149
|
+
qname: "test-delayed-retry",
|
|
150
150
|
});
|
|
151
151
|
|
|
152
152
|
const message = "Delayed retry message";
|
|
@@ -155,9 +155,9 @@ describe("RedisQueue Tests", () => {
|
|
|
155
155
|
|
|
156
156
|
const sentAt = Date.now();
|
|
157
157
|
await queue.sendMessage({
|
|
158
|
+
delay: delayMs,
|
|
158
159
|
message,
|
|
159
160
|
qname: "test-delayed-retry",
|
|
160
|
-
delay: delayMs,
|
|
161
161
|
});
|
|
162
162
|
|
|
163
163
|
await new Promise<void>((resolve) => {
|
|
@@ -205,9 +205,9 @@ describe("RedisQueue Tests", () => {
|
|
|
205
205
|
* This simulates a spreadsheet-queue:batch with shorter period for testing
|
|
206
206
|
*/
|
|
207
207
|
await queue.createQueue({
|
|
208
|
-
qname: "spreadsheet-queue:batch",
|
|
209
208
|
every: 3,
|
|
210
209
|
maxRetries: 0,
|
|
210
|
+
qname: "spreadsheet-queue:batch",
|
|
211
211
|
});
|
|
212
212
|
|
|
213
213
|
/**
|
|
@@ -235,9 +235,9 @@ describe("RedisQueue Tests", () => {
|
|
|
235
235
|
for (let i = 1; i <= 6; i++) {
|
|
236
236
|
const message = `Spreadsheet row ${i} for batch-001`;
|
|
237
237
|
await queue.sendBatchMessage({
|
|
238
|
-
qname: "spreadsheet-queue:batch",
|
|
239
238
|
batchId: "batch-001",
|
|
240
239
|
message,
|
|
240
|
+
qname: "spreadsheet-queue:batch",
|
|
241
241
|
});
|
|
242
242
|
batch001Messages.push(message);
|
|
243
243
|
}
|
|
@@ -248,9 +248,9 @@ describe("RedisQueue Tests", () => {
|
|
|
248
248
|
for (let i = 1; i <= 4; i++) {
|
|
249
249
|
const message = `Spreadsheet row ${i} for batch-002`;
|
|
250
250
|
await queue.sendBatchMessage({
|
|
251
|
-
qname: "spreadsheet-queue:batch",
|
|
252
251
|
batchId: "batch-002",
|
|
253
252
|
message,
|
|
253
|
+
qname: "spreadsheet-queue:batch",
|
|
254
254
|
});
|
|
255
255
|
batch002Messages.push(message);
|
|
256
256
|
}
|
|
@@ -334,10 +334,10 @@ describe("RedisQueue Tests", () => {
|
|
|
334
334
|
* Create batch queue with 2-second interval and retry enabled
|
|
335
335
|
*/
|
|
336
336
|
await queue.createQueue({
|
|
337
|
-
qname: "retry-test:batch",
|
|
338
337
|
every: 2,
|
|
339
|
-
maxRetries: 3,
|
|
340
338
|
maxBackoffSeconds: 1,
|
|
339
|
+
maxRetries: 3,
|
|
340
|
+
qname: "retry-test:batch",
|
|
341
341
|
});
|
|
342
342
|
|
|
343
343
|
const processedBatches: Array<{
|
|
@@ -351,9 +351,9 @@ describe("RedisQueue Tests", () => {
|
|
|
351
351
|
*/
|
|
352
352
|
for (let i = 1; i <= 3; i++) {
|
|
353
353
|
await queue.sendBatchMessage({
|
|
354
|
-
qname: "retry-test:batch",
|
|
355
354
|
batchId: `batch-00${i}`,
|
|
356
355
|
message: `Message ${i}`,
|
|
356
|
+
qname: "retry-test:batch",
|
|
357
357
|
});
|
|
358
358
|
}
|
|
359
359
|
|
|
@@ -365,8 +365,8 @@ describe("RedisQueue Tests", () => {
|
|
|
365
365
|
"retry-test:batch",
|
|
366
366
|
async (received) => {
|
|
367
367
|
processedBatches.push({
|
|
368
|
-
batchId: received.batchId,
|
|
369
368
|
attempt: received.attempt,
|
|
369
|
+
batchId: received.batchId,
|
|
370
370
|
success: false,
|
|
371
371
|
});
|
|
372
372
|
|
|
@@ -505,6 +505,51 @@ describe("RedisQueue Tests", () => {
|
|
|
505
505
|
|
|
506
506
|
await queue.close();
|
|
507
507
|
});
|
|
508
|
+
|
|
509
|
+
test("7. Can handle high volume and concurrency", async () => {
|
|
510
|
+
const totalMessages = 10_000;
|
|
511
|
+
|
|
512
|
+
const queue = new RedisQueue(redisConfig);
|
|
513
|
+
|
|
514
|
+
await queue.createQueue({ qname: "test-concurrency" });
|
|
515
|
+
|
|
516
|
+
const processedIds: string[] = [];
|
|
517
|
+
|
|
518
|
+
Array.from({ length: totalMessages }, async (_, i) => {
|
|
519
|
+
await queue.sendMessage({
|
|
520
|
+
message: JSON.stringify({
|
|
521
|
+
id: `${i}`,
|
|
522
|
+
name: `John Doe ${i}`,
|
|
523
|
+
}),
|
|
524
|
+
qname: "test-concurrency",
|
|
525
|
+
});
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
let receivedCount = 0;
|
|
529
|
+
|
|
530
|
+
await new Promise<void>((resolve) => {
|
|
531
|
+
queue.startWorker(
|
|
532
|
+
"test-concurrency",
|
|
533
|
+
async (received) => {
|
|
534
|
+
receivedCount++;
|
|
535
|
+
processedIds.push(JSON.parse(received.message).id);
|
|
536
|
+
if (receivedCount === totalMessages) {
|
|
537
|
+
resolve();
|
|
538
|
+
}
|
|
539
|
+
return { success: true };
|
|
540
|
+
},
|
|
541
|
+
workerConfig,
|
|
542
|
+
);
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
for (let i = 0; i < totalMessages; i++) {
|
|
546
|
+
expect(processedIds[i]).toBe(`${i}`);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
expect(receivedCount).toBe(totalMessages);
|
|
550
|
+
|
|
551
|
+
await queue.close();
|
|
552
|
+
});
|
|
508
553
|
});
|
|
509
554
|
|
|
510
555
|
afterAll(async () => {
|
package/app.ts
CHANGED
|
@@ -200,14 +200,25 @@ export class RedisQueue {
|
|
|
200
200
|
|
|
201
201
|
async deleteQueue(qname: string): Promise<boolean> {
|
|
202
202
|
const pattern = `${this.ns}:${qname}*`;
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
203
|
+
let cursor = "0";
|
|
204
|
+
let keysFound = false;
|
|
205
|
+
|
|
206
|
+
do {
|
|
207
|
+
const [nextCursor, keys] = await this.redis.scan(
|
|
208
|
+
cursor,
|
|
209
|
+
"MATCH",
|
|
210
|
+
pattern,
|
|
211
|
+
"COUNT",
|
|
212
|
+
500,
|
|
213
|
+
);
|
|
214
|
+
cursor = nextCursor as string;
|
|
215
|
+
if (keys.length > 0) {
|
|
216
|
+
keysFound = true;
|
|
217
|
+
await this.redis.del(...keys);
|
|
218
|
+
}
|
|
219
|
+
} while (cursor !== "0");
|
|
208
220
|
|
|
209
|
-
|
|
210
|
-
return true;
|
|
221
|
+
return keysFound;
|
|
211
222
|
}
|
|
212
223
|
|
|
213
224
|
async sendMessage(options: SendMessageOptions): Promise<string> {
|
|
@@ -295,10 +306,11 @@ export class RedisQueue {
|
|
|
295
306
|
const batchKey = this.getKey(qname, `batch:${batchId}`);
|
|
296
307
|
const batchMetaKey = this.getKey(qname, "batch-meta");
|
|
297
308
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
309
|
+
const [_, __, batchExists] = await Promise.all([
|
|
310
|
+
this.redis.hset(messagesKey, { [id]: encoded }),
|
|
311
|
+
this.redis.sadd(batchKey, id),
|
|
312
|
+
this.redis.hexists(batchMetaKey, batchId),
|
|
313
|
+
]);
|
|
302
314
|
|
|
303
315
|
if (!batchExists) {
|
|
304
316
|
const batchMeta: StoredBatchMeta = {
|
|
@@ -345,27 +357,6 @@ export class RedisQueue {
|
|
|
345
357
|
return JSON.parse(encoded);
|
|
346
358
|
}
|
|
347
359
|
|
|
348
|
-
private async fetchMessage(
|
|
349
|
-
qname: string,
|
|
350
|
-
id: string,
|
|
351
|
-
): Promise<Message | null> {
|
|
352
|
-
const messagesKey = this.getKey(qname, "messages");
|
|
353
|
-
const encoded = await this.redis.hget(messagesKey, id);
|
|
354
|
-
|
|
355
|
-
if (!encoded) {
|
|
356
|
-
return null;
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
const stored = this.decodeMessage(encoded);
|
|
360
|
-
return {
|
|
361
|
-
attempt: stored.attempt,
|
|
362
|
-
id: stored.id,
|
|
363
|
-
message: stored.message,
|
|
364
|
-
sent: stored.sent,
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
// OPTIMIZED: Batch fetch multiple messages at once
|
|
369
360
|
private async fetchMessages(
|
|
370
361
|
qname: string,
|
|
371
362
|
ids: string[],
|
|
@@ -559,7 +550,7 @@ export class RedisQueue {
|
|
|
559
550
|
|
|
560
551
|
private async processBatches(
|
|
561
552
|
qname: string,
|
|
562
|
-
handler: (
|
|
553
|
+
handler: (batch: BatchMessage) => Promise<{ success: boolean }>,
|
|
563
554
|
silent: boolean,
|
|
564
555
|
): Promise<void> {
|
|
565
556
|
if (this.isClosing) return;
|
|
@@ -609,7 +600,7 @@ export class RedisQueue {
|
|
|
609
600
|
async startWorker<QueueName extends `${string}:batch` | (string & {})>(
|
|
610
601
|
qname: QueueName,
|
|
611
602
|
handler: (
|
|
612
|
-
|
|
603
|
+
received: QueueName extends `${string}:batch`
|
|
613
604
|
? BatchMessage
|
|
614
605
|
: Message,
|
|
615
606
|
) => Promise<{ success: boolean }>,
|
|
@@ -639,7 +630,7 @@ export class RedisQueue {
|
|
|
639
630
|
await this.processBatches(
|
|
640
631
|
qname,
|
|
641
632
|
handler as (
|
|
642
|
-
|
|
633
|
+
received: BatchMessage,
|
|
643
634
|
) => Promise<{ success: boolean }>,
|
|
644
635
|
silent,
|
|
645
636
|
);
|
|
@@ -662,7 +653,7 @@ export class RedisQueue {
|
|
|
662
653
|
this.runWorker(
|
|
663
654
|
qname,
|
|
664
655
|
handler as (
|
|
665
|
-
|
|
656
|
+
received: Message,
|
|
666
657
|
) => Promise<{ success: boolean }>,
|
|
667
658
|
controller.signal,
|
|
668
659
|
i,
|
|
@@ -674,37 +665,38 @@ export class RedisQueue {
|
|
|
674
665
|
}
|
|
675
666
|
}
|
|
676
667
|
|
|
677
|
-
// OPTIMIZED: Process messages in parallel within worker
|
|
678
668
|
private async runWorker(
|
|
679
669
|
qname: string,
|
|
680
|
-
handler: (
|
|
670
|
+
handler: (received: Message) => Promise<{ success: boolean }>,
|
|
681
671
|
signal: AbortSignal,
|
|
682
672
|
workerId: number,
|
|
683
673
|
silent: boolean,
|
|
684
674
|
): Promise<void> {
|
|
685
675
|
const queueKey = this.getKey(qname, "queue");
|
|
676
|
+
const BATCH_SIZE = 50;
|
|
686
677
|
|
|
687
678
|
while (!signal.aborted) {
|
|
688
679
|
try {
|
|
689
680
|
await this.processDelayedMessages(qname);
|
|
690
681
|
|
|
691
|
-
const
|
|
692
|
-
queueKey,
|
|
693
|
-
"0.1",
|
|
694
|
-
]);
|
|
682
|
+
const ids = await this.redis.rpop(queueKey, BATCH_SIZE);
|
|
695
683
|
|
|
696
|
-
if (
|
|
697
|
-
const
|
|
698
|
-
|
|
684
|
+
if (ids && ids.length > 0) {
|
|
685
|
+
const messages = await this.fetchMessages(
|
|
686
|
+
qname,
|
|
687
|
+
ids as string[],
|
|
688
|
+
);
|
|
699
689
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
handler(message)
|
|
690
|
+
const processingPromises = messages.map((received) =>
|
|
691
|
+
handler(received)
|
|
703
692
|
.then(async ({ success }) => {
|
|
704
693
|
if (success) {
|
|
705
|
-
await this.deleteMessage(
|
|
694
|
+
await this.deleteMessage(
|
|
695
|
+
qname,
|
|
696
|
+
received.id,
|
|
697
|
+
);
|
|
706
698
|
} else {
|
|
707
|
-
await this.retryMessage(qname,
|
|
699
|
+
await this.retryMessage(qname, received.id);
|
|
708
700
|
}
|
|
709
701
|
})
|
|
710
702
|
.catch(async (error) => {
|
|
@@ -714,9 +706,12 @@ export class RedisQueue {
|
|
|
714
706
|
(error as Error).message,
|
|
715
707
|
);
|
|
716
708
|
}
|
|
717
|
-
await this.retryMessage(qname,
|
|
718
|
-
})
|
|
719
|
-
|
|
709
|
+
await this.retryMessage(qname, received.id);
|
|
710
|
+
}),
|
|
711
|
+
);
|
|
712
|
+
await Promise.all(processingPromises);
|
|
713
|
+
} else {
|
|
714
|
+
await Bun.sleep(50);
|
|
720
715
|
}
|
|
721
716
|
} catch (error) {
|
|
722
717
|
if (!signal.aborted) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"_id":"6973b4f29af00ad243d8dfb1","index":0,"guid":"3da3f9d7-2406-4f07-8cc5-20c77a18ade4","isActive":false,"balance":"$1,361.82","picture":"http://placehold.it/32x32","age":37,"eyeColor":"green","name":"Hart Booth","gender":"male","company":"SULTRAX","email":"hartbooth@sultrax.com","phone":"+1 (966) 498-3673","address":"843 Hall Street, Trona, Oregon, 1712","about":"Deserunt fugiat nisi voluptate quis ex nisi reprehenderit est eiusmod officia sunt quis elit ea. Quis non mollit consectetur amet nulla anim ipsum consequat aliqua reprehenderit tempor reprehenderit. Sint pariatur pariatur laboris sint dolor sint voluptate pariatur ut adipisicing officia Lorem deserunt. Ullamco minim duis sit consequat aliqua in sunt nostrud amet nisi deserunt voluptate pariatur. Qui ullamco aliquip sunt veniam occaecat nulla ex incididunt. Aute mollit incididunt ad in dolor culpa pariatur non commodo magna nostrud non.\r\n","registered":"2025-07-22T10:26:43 -07:00","latitude":-3.647469,"longitude":69.437957,"tags":["qui","est","aliqua","ullamco","eiusmod","eu","velit"],"friends":[{"id":0,"name":"Lakisha Turner"},{"id":1,"name":"Leonor Ewing"},{"id":2,"name":"Frazier Irwin"}],"greeting":"Hello, Hart Booth! You have 6 unread messages.","favoriteFruit":"banana"},{"_id":"6973b4f2021b2c498f984bb6","index":1,"guid":"25034ff5-fbc8-4d09-8c63-e60f919e9ad4","isActive":false,"balance":"$2,141.41","picture":"http://placehold.it/32x32","age":33,"eyeColor":"brown","name":"Bethany Davenport","gender":"female","company":"STROZEN","email":"bethanydavenport@strozen.com","phone":"+1 (818) 499-2890","address":"506 Quay Street, Springville, Texas, 5249","about":"Consectetur occaecat sunt consectetur culpa nulla qui sunt labore proident et irure fugiat eu nulla. Voluptate est dolore labore aliqua velit duis. Proident occaecat culpa esse deserunt nulla aliqua. Nulla ea minim esse consectetur fugiat nostrud in anim esse cillum. Quis dolor quis reprehenderit ex ex ad esse minim est.\r\n","registered":"2020-08-03T11:36:56 -07:00","latitude":-10.479417,"longitude":28.341411,"tags":["non","eiusmod","amet","cillum","incididunt","elit","exercitation"],"friends":[{"id":0,"name":"Phyllis Bryan"},{"id":1,"name":"Mallory Gill"},{"id":2,"name":"Schneider Mercado"}],"greeting":"Hello, Bethany Davenport! You have 9 unread messages.","favoriteFruit":"apple"},{"_id":"6973b4f2220a607ed78df07a","index":2,"guid":"871776e1-d133-4ae7-8343-4fca929bb75c","isActive":true,"balance":"$2,630.76","picture":"http://placehold.it/32x32","age":35,"eyeColor":"brown","name":"Bertie Peters","gender":"female","company":"PERKLE","email":"bertiepeters@perkle.com","phone":"+1 (841) 517-3179","address":"972 Nassau Street, Ventress, Wyoming, 585","about":"Fugiat reprehenderit excepteur deserunt magna laboris aute culpa. Voluptate amet nisi commodo nostrud ipsum do nisi consectetur sint deserunt qui sint proident. Consequat cupidatat deserunt aliquip aliqua occaecat velit ad nostrud deserunt qui. Sunt amet sunt voluptate reprehenderit nisi eiusmod deserunt ea cillum Lorem qui anim aute duis.\r\n","registered":"2025-08-27T03:50:35 -07:00","latitude":32.284355,"longitude":-78.324272,"tags":["consequat","id","voluptate","tempor","proident","non","occaecat"],"friends":[{"id":0,"name":"Foreman Quinn"},{"id":1,"name":"Rojas Kemp"},{"id":2,"name":"Hanson Buck"}],"greeting":"Hello, Bertie Peters! You have 3 unread messages.","favoriteFruit":"banana"},{"_id":"6973b4f2bd348f1a31aca9f8","index":3,"guid":"fc19928a-b4f9-441e-ade9-efd7bad779af","isActive":false,"balance":"$1,273.51","picture":"http://placehold.it/32x32","age":40,"eyeColor":"blue","name":"Rosales Sims","gender":"male","company":"CANOPOLY","email":"rosalessims@canopoly.com","phone":"+1 (869) 427-3088","address":"153 Guernsey Street, Ona, New Jersey, 183","about":"Laborum sit sunt ad deserunt velit nisi culpa pariatur est ea mollit. Velit excepteur nulla cupidatat reprehenderit laboris sint amet duis exercitation dolore voluptate commodo sit Lorem. Velit exercitation ea incididunt adipisicing voluptate occaecat.\r\n","registered":"2017-11-04T12:50:51 -07:00","latitude":0.771702,"longitude":82.991386,"tags":["cillum","qui","sint","consectetur","eiusmod","consequat","ex"],"friends":[{"id":0,"name":"Imogene Lamb"},{"id":1,"name":"Key Burris"},{"id":2,"name":"Reyna Mayer"}],"greeting":"Hello, Rosales Sims! You have 7 unread messages.","favoriteFruit":"apple"},{"_id":"6973b4f207f65e544265d355","index":4,"guid":"abc15ba6-3214-4ef8-ac30-8afb642b82c2","isActive":true,"balance":"$2,945.08","picture":"http://placehold.it/32x32","age":26,"eyeColor":"brown","name":"Gretchen Jordan","gender":"female","company":"ANOCHA","email":"gretchenjordan@anocha.com","phone":"+1 (948) 454-2346","address":"890 Monaco Place, Loomis, Alabama, 4341","about":"Nulla excepteur est consequat velit aute laborum sunt voluptate aute eu eiusmod. Anim sunt eu veniam in commodo sit eu. Anim commodo incididunt ad reprehenderit anim culpa sit ut. Laborum esse in pariatur velit. Ut elit adipisicing magna ut officia non magna exercitation incididunt enim esse magna ad veniam. Deserunt irure ut dolor dolore labore pariatur veniam ipsum voluptate sint enim dolor. Ad exercitation sint sit occaecat excepteur eiusmod et sit.\r\n","registered":"2022-03-30T04:45:03 -07:00","latitude":29.089418,"longitude":-41.417872,"tags":["minim","amet","aliqua","excepteur","irure","et","culpa"],"friends":[{"id":0,"name":"Sonia Hayden"},{"id":1,"name":"Levy Burks"},{"id":2,"name":"Wolfe Hancock"}],"greeting":"Hello, Gretchen Jordan! You have 5 unread messages.","favoriteFruit":"banana"},{"_id":"6973b4f250d546997251a3d3","index":5,"guid":"ec6af806-cb4c-4ecc-b208-68b4b3cafd93","isActive":false,"balance":"$2,535.09","picture":"http://placehold.it/32x32","age":33,"eyeColor":"brown","name":"Ruiz Tran","gender":"male","company":"AQUASURE","email":"ruiztran@aquasure.com","phone":"+1 (962) 478-3889","address":"705 Farragut Road, Broadlands, Michigan, 1955","about":"Sunt enim Lorem excepteur sint mollit deserunt. Aute commodo amet sit cillum voluptate mollit exercitation ad cillum aute fugiat mollit. Quis ad cupidatat veniam reprehenderit aute cupidatat quis. Minim laboris est fugiat et in cillum id nisi dolore reprehenderit fugiat minim. Nulla ut consequat do sit dolor laboris.\r\n","registered":"2018-09-27T01:51:44 -07:00","latitude":-59.151306,"longitude":143.667188,"tags":["laboris","anim","eiusmod","quis","nulla","aliqua","duis"],"friends":[{"id":0,"name":"Ramona Brock"},{"id":1,"name":"Kayla Webb"},{"id":2,"name":"Rutledge Mcclure"}],"greeting":"Hello, Ruiz Tran! You have 6 unread messages.","favoriteFruit":"apple"},{"_id":"6973b4f20e1b602ed3098386","index":6,"guid":"d63efc08-287a-487e-a159-bbb4f231c37a","isActive":false,"balance":"$3,941.09","picture":"http://placehold.it/32x32","age":37,"eyeColor":"blue","name":"Reba Oneal","gender":"female","company":"NIQUENT","email":"rebaoneal@niquent.com","phone":"+1 (994) 443-2343","address":"229 Clifton Place, Cochranville, Pennsylvania, 6468","about":"Officia veniam do minim elit duis in nulla. Deserunt veniam in ut esse duis nulla cillum magna ullamco dolore veniam exercitation Lorem. Eiusmod pariatur irure dolor aute dolore minim adipisicing sint ullamco. Ipsum id voluptate est ipsum adipisicing est. Do anim exercitation commodo mollit fugiat minim est ea.\r\n","registered":"2020-05-02T09:23:38 -07:00","latitude":-45.360857,"longitude":-17.113491,"tags":["laborum","dolor","incididunt","elit","adipisicing","adipisicing","veniam"],"friends":[{"id":0,"name":"Janine Russell"},{"id":1,"name":"Abby Short"},{"id":2,"name":"Savannah Grant"}],"greeting":"Hello, Reba Oneal! You have 7 unread messages.","favoriteFruit":"banana"},{"_id":"6973b4f208d0cb4b5c53bb93","index":7,"guid":"152d21f2-05cd-41d2-a048-ba499944b2bf","isActive":false,"balance":"$1,155.29","picture":"http://placehold.it/32x32","age":25,"eyeColor":"green","name":"Vanessa Rodriquez","gender":"female","company":"OPTYK","email":"vanessarodriquez@optyk.com","phone":"+1 (873) 492-2951","address":"102 Thatford Avenue, Keyport, Minnesota, 7404","about":"Adipisicing sit dolor reprehenderit Lorem et voluptate culpa ullamco cillum officia dolor culpa et sit. Lorem minim eu laboris nulla do est labore nulla eu occaecat. Est occaecat sit id irure. Enim sint consequat amet mollit occaecat in mollit duis. Eiusmod et tempor laborum est tempor fugiat anim ullamco.\r\n","registered":"2019-11-03T12:06:46 -07:00","latitude":30.194412,"longitude":155.798353,"tags":["aliqua","cupidatat","quis","eiusmod","ad","veniam","velit"],"friends":[{"id":0,"name":"June Little"},{"id":1,"name":"Lamb Marsh"},{"id":2,"name":"Richards Conley"}],"greeting":"Hello, Vanessa Rodriquez! You have 5 unread messages.","favoriteFruit":"banana"},{"_id":"6973b4f244d84ce83babb8e8","index":8,"guid":"2d743ccf-cffe-46ac-821a-1a32180f8401","isActive":true,"balance":"$1,531.94","picture":"http://placehold.it/32x32","age":33,"eyeColor":"brown","name":"Kathy Warren","gender":"female","company":"NURPLEX","email":"kathywarren@nurplex.com","phone":"+1 (958) 514-2617","address":"211 Cypress Court, Rosewood, Massachusetts, 2530","about":"Cillum nisi ad esse aliqua do amet eu dolor. Culpa culpa adipisicing officia nisi magna occaecat aliquip labore nostrud anim. Eu esse in aliquip exercitation reprehenderit cupidatat eu veniam deserunt cillum excepteur id. Id dolor reprehenderit do deserunt. Enim eiusmod nostrud exercitation fugiat consequat. Nulla deserunt cillum mollit excepteur ea ad veniam. Id nostrud laboris ipsum cupidatat ex non commodo ad sunt.\r\n","registered":"2017-06-14T06:01:20 -07:00","latitude":34.733711,"longitude":-163.804679,"tags":["reprehenderit","elit","exercitation","voluptate","aute","adipisicing","occaecat"],"friends":[{"id":0,"name":"Allie Holcomb"},{"id":1,"name":"Muriel Weaver"},{"id":2,"name":"Tommie Carr"}],"greeting":"Hello, Kathy Warren! You have 4 unread messages.","favoriteFruit":"apple"},{"_id":"6973b4f297dc750e1750eb58","index":9,"guid":"1ccffd92-ff08-429d-9232-e86c565e70da","isActive":true,"balance":"$1,662.22","picture":"http://placehold.it/32x32","age":31,"eyeColor":"green","name":"Hodges Richardson","gender":"male","company":"POSHOME","email":"hodgesrichardson@poshome.com","phone":"+1 (970) 590-2870","address":"686 Anna Court, Elizaville, Marshall Islands, 5762","about":"Ullamco cupidatat nisi do ut nostrud est pariatur labore dolore exercitation quis minim ipsum quis. Aute enim excepteur voluptate ullamco laboris adipisicing commodo. Do amet sunt occaecat id commodo. Consectetur veniam excepteur nisi in commodo. Laborum anim dolor velit deserunt culpa. Exercitation do amet laborum do ipsum voluptate commodo. Aliqua exercitation consequat nisi pariatur ea veniam officia nostrud excepteur tempor.\r\n","registered":"2016-07-28T02:54:58 -07:00","latitude":-14.492817,"longitude":134.965697,"tags":["qui","aliquip","pariatur","magna","ad","et","irure"],"friends":[{"id":0,"name":"Robyn Willis"},{"id":1,"name":"Gabrielle Holt"},{"id":2,"name":"Wilkerson Bailey"}],"greeting":"Hello, Hodges Richardson! You have 5 unread messages.","favoriteFruit":"strawberry"},{"_id":"6973b4f277b5d5924e85e34c","index":10,"guid":"24b6f2d6-f153-4826-9c2f-f50c42d907e5","isActive":true,"balance":"$1,715.09","picture":"http://placehold.it/32x32","age":33,"eyeColor":"blue","name":"Floyd Petty","gender":"male","company":"GINKOGENE","email":"floydpetty@ginkogene.com","phone":"+1 (832) 499-3944","address":"910 Canal Avenue, Mammoth, North Dakota, 1708","about":"Eiusmod velit proident sit proident ipsum irure. Excepteur dolore consequat labore duis tempor nulla laborum do amet labore. Amet nisi ullamco ad enim officia id exercitation aliquip in fugiat in. Anim quis et enim proident dolore magna mollit consectetur ea elit velit nostrud. Consectetur ea labore esse esse est nisi consectetur magna ut ullamco nulla ipsum exercitation.\r\n","registered":"2023-09-18T01:14:07 -07:00","latitude":-51.559233,"longitude":134.694377,"tags":["cupidatat","elit","commodo","ullamco","do","reprehenderit","nostrud"],"friends":[{"id":0,"name":"Naomi Garrison"},{"id":1,"name":"Betty English"},{"id":2,"name":"Prince Singleton"}],"greeting":"Hello, Floyd Petty! You have 7 unread messages.","favoriteFruit":"strawberry"},{"_id":"6973b4f2e7ad4e31bb2510c3","index":11,"guid":"74ccb6d9-7f24-4451-a3a0-e632bab385b3","isActive":true,"balance":"$1,490.53","picture":"http://placehold.it/32x32","age":36,"eyeColor":"blue","name":"Johnnie Simon","gender":"female","company":"JAMNATION","email":"johnniesimon@jamnation.com","phone":"+1 (868) 417-3009","address":"125 Chester Street, Whipholt, Maryland, 9466","about":"Dolor anim esse enim Lorem nostrud id officia incididunt amet laboris laboris sunt. Pariatur dolor aute Lorem aute magna. Laborum enim non voluptate commodo.\r\n","registered":"2015-03-03T08:28:39 -07:00","latitude":-19.882734,"longitude":-45.527604,"tags":["qui","sit","culpa","id","dolor","irure","id"],"friends":[{"id":0,"name":"Fry Ayers"},{"id":1,"name":"Donovan Foley"},{"id":2,"name":"Finch Dunn"}],"greeting":"Hello, Johnnie Simon! You have 9 unread messages.","favoriteFruit":"apple"},{"_id":"6973b4f24d0d75c705a54911","index":12,"guid":"f972a1ef-0d3a-41ab-b080-eaf7d26184f1","isActive":true,"balance":"$3,429.83","picture":"http://placehold.it/32x32","age":36,"eyeColor":"brown","name":"Rosalind Duran","gender":"female","company":"ZORROMOP","email":"rosalindduran@zorromop.com","phone":"+1 (987) 589-3940","address":"720 Mill Avenue, Gardiner, Nebraska, 6579","about":"Dolor deserunt nisi laborum eiusmod aliquip adipisicing ullamco eiusmod non qui consectetur est nostrud. Incididunt nostrud labore minim Lorem elit qui commodo consectetur ex culpa veniam. Quis ad quis cillum esse nisi ea ipsum consequat dolore. Non minim minim sint excepteur anim consectetur est voluptate laborum commodo. Mollit ea magna dolor sit ut magna voluptate ipsum reprehenderit id ex cillum deserunt.\r\n","registered":"2014-07-03T10:56:30 -07:00","latitude":-39.091253,"longitude":-131.671201,"tags":["labore","enim","nulla","est","exercitation","ipsum","deserunt"],"friends":[{"id":0,"name":"Emerson Dalton"},{"id":1,"name":"Owen Bass"},{"id":2,"name":"Bell Shannon"}],"greeting":"Hello, Rosalind Duran! You have 1 unread messages.","favoriteFruit":"banana"},{"_id":"6973b4f2edea35a5d2cea142","index":13,"guid":"18cf8f91-9735-4fa6-9381-ae8c1060890c","isActive":true,"balance":"$1,811.69","picture":"http://placehold.it/32x32","age":26,"eyeColor":"blue","name":"Pauline Cain","gender":"female","company":"ENAUT","email":"paulinecain@enaut.com","phone":"+1 (972) 408-3809","address":"508 Rapelye Street, Thynedale, Maine, 9391","about":"Qui enim fugiat do eu in eu aliqua elit cillum occaecat. Consectetur amet pariatur ea dolore Lorem eu ipsum veniam commodo commodo. Mollit dolore velit aliqua cillum elit consequat sunt nostrud in qui ipsum sit eu irure. Cupidatat reprehenderit labore in officia laborum.\r\n","registered":"2023-12-23T01:34:49 -07:00","latitude":-18.152332,"longitude":6.220963,"tags":["quis","sint","proident","officia","dolore","elit","quis"],"friends":[{"id":0,"name":"Kristie Shepherd"},{"id":1,"name":"Jensen Webster"},{"id":2,"name":"Langley Young"}],"greeting":"Hello, Pauline Cain! You have 7 unread messages.","favoriteFruit":"apple"}]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"batch_id":"batch_analytics_2201","source_system":"inventory_management_v2","generated_at":"2023-10-27T10:00:00Z","record_count":5,"data":[{"sku":"KEY-MECH-RGB-001","warehouse_id":"WH-NY-01","stock_level":450,"reserved":12,"incoming":100,"last_audit":"2023-10-25","dimensions":{"w":15,"h":5,"l":40,"unit":"cm"},"tags":["electronics","peripherals","hot_item"]},{"sku":"MON-4K-IPS-002","warehouse_id":"WH-CA-02","stock_level":85,"reserved":5,"incoming":0,"last_audit":"2023-10-26","dimensions":{"w":60,"h":40,"l":10,"unit":"cm"},"tags":["electronics","display","fragile"]},{"sku":"LAP-PRO-M2-003","warehouse_id":"WH-TX-05","stock_level":200,"reserved":45,"incoming":50,"last_audit":"2023-10-20","dimensions":{"w":30,"h":2,"l":20,"unit":"cm"},"tags":["computer","high_value","insured"]},{"sku":"MOU-ERG-WL-004","warehouse_id":"WH-NY-01","stock_level":1200,"reserved":0,"incoming":200,"last_audit":"2023-10-22","dimensions":{"w":8,"h":5,"l":12,"unit":"cm"},"tags":["peripherals","office","sale"]},{"sku":"CAB-HDMI-2M-005","warehouse_id":"WH-WA-03","stock_level":5000,"reserved":120,"incoming":1000,"last_audit":"2023-10-01","dimensions":{"w":10,"h":2,"l":10,"unit":"cm"},"tags":["accessory","cable","generic"]}],"system_logs":{"warnings":[],"errors":[{"code":404,"msg":"SKU-IMG-MISSING for CAB-HDMI-2M-005","timestamp":1678892500}],"performance_metrics":{"query_time_ms":145,"db_reads":25,"cpu_load_percent":12}}}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import RedisQueue from "../app";
|
|
2
|
+
declare var self: Worker;
|
|
2
3
|
|
|
3
4
|
interface WorkerMessage {
|
|
4
5
|
type: "start";
|
|
@@ -23,15 +24,27 @@ self.addEventListener("message", async (event: MessageEvent<WorkerMessage>) => {
|
|
|
23
24
|
try {
|
|
24
25
|
const queue = new RedisQueue(redisConfig);
|
|
25
26
|
const latencies: number[] = [];
|
|
27
|
+
const CHUNK_SIZE = 1000;
|
|
26
28
|
|
|
27
|
-
for (let i = 0; i < messagesPerWorker; i
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
29
|
+
for (let i = 0; i < messagesPerWorker; i += CHUNK_SIZE) {
|
|
30
|
+
const promises = [];
|
|
31
|
+
const chunkEnd = Math.min(i + CHUNK_SIZE, messagesPerWorker);
|
|
32
|
+
|
|
33
|
+
for (let j = i; j < chunkEnd; j++) {
|
|
34
|
+
const start = performance.now();
|
|
35
|
+
promises.push(
|
|
36
|
+
queue
|
|
37
|
+
.sendMessage({
|
|
38
|
+
message: testMessage,
|
|
39
|
+
qname,
|
|
40
|
+
})
|
|
41
|
+
.then(() => {
|
|
42
|
+
const latency = performance.now() - start;
|
|
43
|
+
latencies.push(latency);
|
|
44
|
+
}),
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
await Promise.all(promises);
|
|
35
48
|
}
|
|
36
49
|
|
|
37
50
|
await queue.close();
|
package/benchmark/stress.ts
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
import type { Worker } from "bun";
|
|
4
4
|
import RedisQueue from "../app";
|
|
5
|
+
import mediumMsg from "./medium.txt";
|
|
6
|
+
import smallMsg from "./small.txt";
|
|
7
|
+
import tinyMsg from "./tiny.txt";
|
|
5
8
|
|
|
6
9
|
const COLORS = {
|
|
7
10
|
blue: "\x1b[34m",
|
|
@@ -14,8 +17,8 @@ const COLORS = {
|
|
|
14
17
|
yellow: "\x1b[33m",
|
|
15
18
|
};
|
|
16
19
|
|
|
17
|
-
const WORKER_COUNT =
|
|
18
|
-
const CONCURRENCY =
|
|
20
|
+
const WORKER_COUNT = 8;
|
|
21
|
+
const CONCURRENCY = 2;
|
|
19
22
|
const MSG_COUNT = 100_000;
|
|
20
23
|
|
|
21
24
|
interface StressTestConfig {
|
|
@@ -112,7 +115,7 @@ async function testRegularQueueParallel(
|
|
|
112
115
|
});
|
|
113
116
|
|
|
114
117
|
const qname = "stress-regular";
|
|
115
|
-
await queue.createQueue({ maxRetries: 0,
|
|
118
|
+
await queue.createQueue({ maxRetries: 0, maxsize: 150_000, qname });
|
|
116
119
|
|
|
117
120
|
const allLatencies: number[] = [];
|
|
118
121
|
let receivedCount = 0;
|
|
@@ -173,7 +176,6 @@ async function testRegularQueueParallel(
|
|
|
173
176
|
|
|
174
177
|
worker.postMessage({
|
|
175
178
|
data: {
|
|
176
|
-
testMessage: config.testMessage,
|
|
177
179
|
messagesPerWorker,
|
|
178
180
|
qname,
|
|
179
181
|
redisConfig: {
|
|
@@ -181,6 +183,7 @@ async function testRegularQueueParallel(
|
|
|
181
183
|
namespace: "stress-test",
|
|
182
184
|
port: process.env.REDIS_PORT || "6379",
|
|
183
185
|
},
|
|
186
|
+
testMessage: config.testMessage,
|
|
184
187
|
workerIndex: i,
|
|
185
188
|
},
|
|
186
189
|
type: "start",
|
|
@@ -261,91 +264,93 @@ async function main() {
|
|
|
261
264
|
|
|
262
265
|
await cleanup();
|
|
263
266
|
|
|
264
|
-
|
|
265
|
-
const mediumMessage = '{"batch_id":"batch_analytics_2201","source_system":"inventory_management_v2","generated_at":"2023-10-27T10:00:00Z","record_count":5,"data":[{"sku":"KEY-MECH-RGB-001","warehouse_id":"WH-NY-01","stock_level":450,"reserved":12,"incoming":100,"last_audit":"2023-10-25","dimensions":{"w":15,"h":5,"l":40,"unit":"cm"},"tags":["electronics","peripherals","hot_item"]},{"sku":"MON-4K-IPS-002","warehouse_id":"WH-CA-02","stock_level":85,"reserved":5,"incoming":0,"last_audit":"2023-10-26","dimensions":{"w":60,"h":40,"l":10,"unit":"cm"},"tags":["electronics","display","fragile"]},{"sku":"LAP-PRO-M2-003","warehouse_id":"WH-TX-05","stock_level":200,"reserved":45,"incoming":50,"last_audit":"2023-10-20","dimensions":{"w":30,"h":2,"l":20,"unit":"cm"},"tags":["computer","high_value","insured"]},{"sku":"MOU-ERG-WL-004","warehouse_id":"WH-NY-01","stock_level":1200,"reserved":0,"incoming":200,"last_audit":"2023-10-22","dimensions":{"w":8,"h":5,"l":12,"unit":"cm"},"tags":["peripherals","office","sale"]},{"sku":"CAB-HDMI-2M-005","warehouse_id":"WH-WA-03","stock_level":5000,"reserved":120,"incoming":1000,"last_audit":"2023-10-01","dimensions":{"w":10,"h":2,"l":10,"unit":"cm"},"tags":["accessory","cable","generic"]}],"system_logs":{"warnings":[],"errors":[{"code":404,"msg":"SKU-IMG-MISSING for CAB-HDMI-2M-005","timestamp":1678892500}],"performance_metrics":{"query_time_ms":145,"db_reads":25,"cpu_load_percent":12}}}'
|
|
266
|
-
|
|
267
267
|
const availableCPUs = navigator.hardwareConcurrency;
|
|
268
268
|
log("yellow", `\nš» Detected ${availableCPUs} CPU cores`);
|
|
269
269
|
|
|
270
270
|
log("blue", `\nš§ Using ${WORKER_COUNT} workers for tests`);
|
|
271
271
|
|
|
272
272
|
try {
|
|
273
|
-
|
|
274
|
-
log("cyan", "\nš Test 1: Small messages (100 bytes)");
|
|
273
|
+
log("cyan", "\nš Test 1: Tiny messages (100 bytes)");
|
|
275
274
|
const test1Results = await testRegularQueueParallel({
|
|
276
275
|
concurrency: CONCURRENCY,
|
|
277
276
|
messageCount: MSG_COUNT,
|
|
278
|
-
testMessage:
|
|
279
|
-
workerCount:WORKER_COUNT,
|
|
277
|
+
testMessage: tinyMsg,
|
|
278
|
+
workerCount: WORKER_COUNT,
|
|
280
279
|
});
|
|
281
280
|
printResults(
|
|
282
|
-
"Test 1:
|
|
281
|
+
"Test 1: Tiny Messages (100K msgs, 100 bytes)",
|
|
283
282
|
test1Results,
|
|
284
283
|
);
|
|
285
284
|
|
|
285
|
+
await cleanup();
|
|
286
286
|
|
|
287
|
-
|
|
288
|
-
log("cyan", "\nš Test 2: Medium messages (1KB)");
|
|
287
|
+
log("cyan", "\nš Test 2: Small messages (1KB)");
|
|
289
288
|
const test2Results = await testRegularQueueParallel({
|
|
290
289
|
concurrency: CONCURRENCY,
|
|
291
290
|
messageCount: MSG_COUNT,
|
|
292
|
-
testMessage:
|
|
293
|
-
workerCount:
|
|
291
|
+
testMessage: smallMsg,
|
|
292
|
+
workerCount: WORKER_COUNT,
|
|
294
293
|
});
|
|
295
|
-
printResults(
|
|
296
|
-
"Test 2: Medium Messages (50K msgs, 1KB)",
|
|
297
|
-
test2Results,
|
|
298
|
-
);
|
|
294
|
+
printResults("Test 2: Small Messages (100K msgs, 1KB)", test2Results);
|
|
299
295
|
|
|
296
|
+
await cleanup();
|
|
300
297
|
|
|
301
|
-
|
|
302
|
-
log("cyan", "\nš Test 3: Large messages (10KB)");
|
|
298
|
+
log("cyan", "\nš Test 3: Medium messages (10KB)");
|
|
303
299
|
const test3Results = await testRegularQueueParallel({
|
|
304
300
|
concurrency: CONCURRENCY,
|
|
305
301
|
messageCount: MSG_COUNT,
|
|
306
|
-
testMessage: '[{"_id":"6973b4f29af00ad243d8dfb1","index":0,"guid":"3da3f9d7-2406-4f07-8cc5-20c77a18ade4","isActive":false,"balance":"$1,361.82","picture":"http://placehold.it/32x32","age":37,"eyeColor":"green","name":"Hart Booth","gender":"male","company":"SULTRAX","email":"hartbooth@sultrax.com","phone":"+1 (966) 498-3673","address":"843 Hall Street, Trona, Oregon, 1712","about":"Deserunt fugiat nisi voluptate quis ex nisi reprehenderit est eiusmod officia sunt quis elit ea. Quis non mollit consectetur amet nulla anim ipsum consequat aliqua reprehenderit tempor reprehenderit. Sint pariatur pariatur laboris sint dolor sint voluptate pariatur ut adipisicing officia Lorem deserunt. Ullamco minim duis sit consequat aliqua in sunt nostrud amet nisi deserunt voluptate pariatur. Qui ullamco aliquip sunt veniam occaecat nulla ex incididunt. Aute mollit incididunt ad in dolor culpa pariatur non commodo magna nostrud non.\r\n","registered":"2025-07-22T10:26:43 -07:00","latitude":-3.647469,"longitude":69.437957,"tags":["qui","est","aliqua","ullamco","eiusmod","eu","velit"],"friends":[{"id":0,"name":"Lakisha Turner"},{"id":1,"name":"Leonor Ewing"},{"id":2,"name":"Frazier Irwin"}],"greeting":"Hello, Hart Booth! You have 6 unread messages.","favoriteFruit":"banana"},{"_id":"6973b4f2021b2c498f984bb6","index":1,"guid":"25034ff5-fbc8-4d09-8c63-e60f919e9ad4","isActive":false,"balance":"$2,141.41","picture":"http://placehold.it/32x32","age":33,"eyeColor":"brown","name":"Bethany Davenport","gender":"female","company":"STROZEN","email":"bethanydavenport@strozen.com","phone":"+1 (818) 499-2890","address":"506 Quay Street, Springville, Texas, 5249","about":"Consectetur occaecat sunt consectetur culpa nulla qui sunt labore proident et irure fugiat eu nulla. Voluptate est dolore labore aliqua velit duis. Proident occaecat culpa esse deserunt nulla aliqua. Nulla ea minim esse consectetur fugiat nostrud in anim esse cillum. Quis dolor quis reprehenderit ex ex ad esse minim est.\r\n","registered":"2020-08-03T11:36:56 -07:00","latitude":-10.479417,"longitude":28.341411,"tags":["non","eiusmod","amet","cillum","incididunt","elit","exercitation"],"friends":[{"id":0,"name":"Phyllis Bryan"},{"id":1,"name":"Mallory Gill"},{"id":2,"name":"Schneider Mercado"}],"greeting":"Hello, Bethany Davenport! You have 9 unread messages.","favoriteFruit":"apple"},{"_id":"6973b4f2220a607ed78df07a","index":2,"guid":"871776e1-d133-4ae7-8343-4fca929bb75c","isActive":true,"balance":"$2,630.76","picture":"http://placehold.it/32x32","age":35,"eyeColor":"brown","name":"Bertie Peters","gender":"female","company":"PERKLE","email":"bertiepeters@perkle.com","phone":"+1 (841) 517-3179","address":"972 Nassau Street, Ventress, Wyoming, 585","about":"Fugiat reprehenderit excepteur deserunt magna laboris aute culpa. Voluptate amet nisi commodo nostrud ipsum do nisi consectetur sint deserunt qui sint proident. Consequat cupidatat deserunt aliquip aliqua occaecat velit ad nostrud deserunt qui. Sunt amet sunt voluptate reprehenderit nisi eiusmod deserunt ea cillum Lorem qui anim aute duis.\r\n","registered":"2025-08-27T03:50:35 -07:00","latitude":32.284355,"longitude":-78.324272,"tags":["consequat","id","voluptate","tempor","proident","non","occaecat"],"friends":[{"id":0,"name":"Foreman Quinn"},{"id":1,"name":"Rojas Kemp"},{"id":2,"name":"Hanson Buck"}],"greeting":"Hello, Bertie Peters! You have 3 unread messages.","favoriteFruit":"banana"},{"_id":"6973b4f2bd348f1a31aca9f8","index":3,"guid":"fc19928a-b4f9-441e-ade9-efd7bad779af","isActive":false,"balance":"$1,273.51","picture":"http://placehold.it/32x32","age":40,"eyeColor":"blue","name":"Rosales Sims","gender":"male","company":"CANOPOLY","email":"rosalessims@canopoly.com","phone":"+1 (869) 427-3088","address":"153 Guernsey Street, Ona, New Jersey, 183","about":"Laborum sit sunt ad deserunt velit nisi culpa pariatur est ea mollit. Velit excepteur nulla cupidatat reprehenderit laboris sint amet duis exercitation dolore voluptate commodo sit Lorem. Velit exercitation ea incididunt adipisicing voluptate occaecat.\r\n","registered":"2017-11-04T12:50:51 -07:00","latitude":0.771702,"longitude":82.991386,"tags":["cillum","qui","sint","consectetur","eiusmod","consequat","ex"],"friends":[{"id":0,"name":"Imogene Lamb"},{"id":1,"name":"Key Burris"},{"id":2,"name":"Reyna Mayer"}],"greeting":"Hello, Rosales Sims! You have 7 unread messages.","favoriteFruit":"apple"},{"_id":"6973b4f207f65e544265d355","index":4,"guid":"abc15ba6-3214-4ef8-ac30-8afb642b82c2","isActive":true,"balance":"$2,945.08","picture":"http://placehold.it/32x32","age":26,"eyeColor":"brown","name":"Gretchen Jordan","gender":"female","company":"ANOCHA","email":"gretchenjordan@anocha.com","phone":"+1 (948) 454-2346","address":"890 Monaco Place, Loomis, Alabama, 4341","about":"Nulla excepteur est consequat velit aute laborum sunt voluptate aute eu eiusmod. Anim sunt eu veniam in commodo sit eu. Anim commodo incididunt ad reprehenderit anim culpa sit ut. Laborum esse in pariatur velit. Ut elit adipisicing magna ut officia non magna exercitation incididunt enim esse magna ad veniam. Deserunt irure ut dolor dolore labore pariatur veniam ipsum voluptate sint enim dolor. Ad exercitation sint sit occaecat excepteur eiusmod et sit.\r\n","registered":"2022-03-30T04:45:03 -07:00","latitude":29.089418,"longitude":-41.417872,"tags":["minim","amet","aliqua","excepteur","irure","et","culpa"],"friends":[{"id":0,"name":"Sonia Hayden"},{"id":1,"name":"Levy Burks"},{"id":2,"name":"Wolfe Hancock"}],"greeting":"Hello, Gretchen Jordan! You have 5 unread messages.","favoriteFruit":"banana"},{"_id":"6973b4f250d546997251a3d3","index":5,"guid":"ec6af806-cb4c-4ecc-b208-68b4b3cafd93","isActive":false,"balance":"$2,535.09","picture":"http://placehold.it/32x32","age":33,"eyeColor":"brown","name":"Ruiz Tran","gender":"male","company":"AQUASURE","email":"ruiztran@aquasure.com","phone":"+1 (962) 478-3889","address":"705 Farragut Road, Broadlands, Michigan, 1955","about":"Sunt enim Lorem excepteur sint mollit deserunt. Aute commodo amet sit cillum voluptate mollit exercitation ad cillum aute fugiat mollit. Quis ad cupidatat veniam reprehenderit aute cupidatat quis. Minim laboris est fugiat et in cillum id nisi dolore reprehenderit fugiat minim. Nulla ut consequat do sit dolor laboris.\r\n","registered":"2018-09-27T01:51:44 -07:00","latitude":-59.151306,"longitude":143.667188,"tags":["laboris","anim","eiusmod","quis","nulla","aliqua","duis"],"friends":[{"id":0,"name":"Ramona Brock"},{"id":1,"name":"Kayla Webb"},{"id":2,"name":"Rutledge Mcclure"}],"greeting":"Hello, Ruiz Tran! You have 6 unread messages.","favoriteFruit":"apple"},{"_id":"6973b4f20e1b602ed3098386","index":6,"guid":"d63efc08-287a-487e-a159-bbb4f231c37a","isActive":false,"balance":"$3,941.09","picture":"http://placehold.it/32x32","age":37,"eyeColor":"blue","name":"Reba Oneal","gender":"female","company":"NIQUENT","email":"rebaoneal@niquent.com","phone":"+1 (994) 443-2343","address":"229 Clifton Place, Cochranville, Pennsylvania, 6468","about":"Officia veniam do minim elit duis in nulla. Deserunt veniam in ut esse duis nulla cillum magna ullamco dolore veniam exercitation Lorem. Eiusmod pariatur irure dolor aute dolore minim adipisicing sint ullamco. Ipsum id voluptate est ipsum adipisicing est. Do anim exercitation commodo mollit fugiat minim est ea.\r\n","registered":"2020-05-02T09:23:38 -07:00","latitude":-45.360857,"longitude":-17.113491,"tags":["laborum","dolor","incididunt","elit","adipisicing","adipisicing","veniam"],"friends":[{"id":0,"name":"Janine Russell"},{"id":1,"name":"Abby Short"},{"id":2,"name":"Savannah Grant"}],"greeting":"Hello, Reba Oneal! You have 7 unread messages.","favoriteFruit":"banana"},{"_id":"6973b4f208d0cb4b5c53bb93","index":7,"guid":"152d21f2-05cd-41d2-a048-ba499944b2bf","isActive":false,"balance":"$1,155.29","picture":"http://placehold.it/32x32","age":25,"eyeColor":"green","name":"Vanessa Rodriquez","gender":"female","company":"OPTYK","email":"vanessarodriquez@optyk.com","phone":"+1 (873) 492-2951","address":"102 Thatford Avenue, Keyport, Minnesota, 7404","about":"Adipisicing sit dolor reprehenderit Lorem et voluptate culpa ullamco cillum officia dolor culpa et sit. Lorem minim eu laboris nulla do est labore nulla eu occaecat. Est occaecat sit id irure. Enim sint consequat amet mollit occaecat in mollit duis. Eiusmod et tempor laborum est tempor fugiat anim ullamco.\r\n","registered":"2019-11-03T12:06:46 -07:00","latitude":30.194412,"longitude":155.798353,"tags":["aliqua","cupidatat","quis","eiusmod","ad","veniam","velit"],"friends":[{"id":0,"name":"June Little"},{"id":1,"name":"Lamb Marsh"},{"id":2,"name":"Richards Conley"}],"greeting":"Hello, Vanessa Rodriquez! You have 5 unread messages.","favoriteFruit":"banana"},{"_id":"6973b4f244d84ce83babb8e8","index":8,"guid":"2d743ccf-cffe-46ac-821a-1a32180f8401","isActive":true,"balance":"$1,531.94","picture":"http://placehold.it/32x32","age":33,"eyeColor":"brown","name":"Kathy Warren","gender":"female","company":"NURPLEX","email":"kathywarren@nurplex.com","phone":"+1 (958) 514-2617","address":"211 Cypress Court, Rosewood, Massachusetts, 2530","about":"Cillum nisi ad esse aliqua do amet eu dolor. Culpa culpa adipisicing officia nisi magna occaecat aliquip labore nostrud anim. Eu esse in aliquip exercitation reprehenderit cupidatat eu veniam deserunt cillum excepteur id. Id dolor reprehenderit do deserunt. Enim eiusmod nostrud exercitation fugiat consequat. Nulla deserunt cillum mollit excepteur ea ad veniam. Id nostrud laboris ipsum cupidatat ex non commodo ad sunt.\r\n","registered":"2017-06-14T06:01:20 -07:00","latitude":34.733711,"longitude":-163.804679,"tags":["reprehenderit","elit","exercitation","voluptate","aute","adipisicing","occaecat"],"friends":[{"id":0,"name":"Allie Holcomb"},{"id":1,"name":"Muriel Weaver"},{"id":2,"name":"Tommie Carr"}],"greeting":"Hello, Kathy Warren! You have 4 unread messages.","favoriteFruit":"apple"},{"_id":"6973b4f297dc750e1750eb58","index":9,"guid":"1ccffd92-ff08-429d-9232-e86c565e70da","isActive":true,"balance":"$1,662.22","picture":"http://placehold.it/32x32","age":31,"eyeColor":"green","name":"Hodges Richardson","gender":"male","company":"POSHOME","email":"hodgesrichardson@poshome.com","phone":"+1 (970) 590-2870","address":"686 Anna Court, Elizaville, Marshall Islands, 5762","about":"Ullamco cupidatat nisi do ut nostrud est pariatur labore dolore exercitation quis minim ipsum quis. Aute enim excepteur voluptate ullamco laboris adipisicing commodo. Do amet sunt occaecat id commodo. Consectetur veniam excepteur nisi in commodo. Laborum anim dolor velit deserunt culpa. Exercitation do amet laborum do ipsum voluptate commodo. Aliqua exercitation consequat nisi pariatur ea veniam officia nostrud excepteur tempor.\r\n","registered":"2016-07-28T02:54:58 -07:00","latitude":-14.492817,"longitude":134.965697,"tags":["qui","aliquip","pariatur","magna","ad","et","irure"],"friends":[{"id":0,"name":"Robyn Willis"},{"id":1,"name":"Gabrielle Holt"},{"id":2,"name":"Wilkerson Bailey"}],"greeting":"Hello, Hodges Richardson! You have 5 unread messages.","favoriteFruit":"strawberry"},{"_id":"6973b4f277b5d5924e85e34c","index":10,"guid":"24b6f2d6-f153-4826-9c2f-f50c42d907e5","isActive":true,"balance":"$1,715.09","picture":"http://placehold.it/32x32","age":33,"eyeColor":"blue","name":"Floyd Petty","gender":"male","company":"GINKOGENE","email":"floydpetty@ginkogene.com","phone":"+1 (832) 499-3944","address":"910 Canal Avenue, Mammoth, North Dakota, 1708","about":"Eiusmod velit proident sit proident ipsum irure. Excepteur dolore consequat labore duis tempor nulla laborum do amet labore. Amet nisi ullamco ad enim officia id exercitation aliquip in fugiat in. Anim quis et enim proident dolore magna mollit consectetur ea elit velit nostrud. Consectetur ea labore esse esse est nisi consectetur magna ut ullamco nulla ipsum exercitation.\r\n","registered":"2023-09-18T01:14:07 -07:00","latitude":-51.559233,"longitude":134.694377,"tags":["cupidatat","elit","commodo","ullamco","do","reprehenderit","nostrud"],"friends":[{"id":0,"name":"Naomi Garrison"},{"id":1,"name":"Betty English"},{"id":2,"name":"Prince Singleton"}],"greeting":"Hello, Floyd Petty! You have 7 unread messages.","favoriteFruit":"strawberry"},{"_id":"6973b4f2e7ad4e31bb2510c3","index":11,"guid":"74ccb6d9-7f24-4451-a3a0-e632bab385b3","isActive":true,"balance":"$1,490.53","picture":"http://placehold.it/32x32","age":36,"eyeColor":"blue","name":"Johnnie Simon","gender":"female","company":"JAMNATION","email":"johnniesimon@jamnation.com","phone":"+1 (868) 417-3009","address":"125 Chester Street, Whipholt, Maryland, 9466","about":"Dolor anim esse enim Lorem nostrud id officia incididunt amet laboris laboris sunt. Pariatur dolor aute Lorem aute magna. Laborum enim non voluptate commodo.\r\n","registered":"2015-03-03T08:28:39 -07:00","latitude":-19.882734,"longitude":-45.527604,"tags":["qui","sit","culpa","id","dolor","irure","id"],"friends":[{"id":0,"name":"Fry Ayers"},{"id":1,"name":"Donovan Foley"},{"id":2,"name":"Finch Dunn"}],"greeting":"Hello, Johnnie Simon! You have 9 unread messages.","favoriteFruit":"apple"},{"_id":"6973b4f24d0d75c705a54911","index":12,"guid":"f972a1ef-0d3a-41ab-b080-eaf7d26184f1","isActive":true,"balance":"$3,429.83","picture":"http://placehold.it/32x32","age":36,"eyeColor":"brown","name":"Rosalind Duran","gender":"female","company":"ZORROMOP","email":"rosalindduran@zorromop.com","phone":"+1 (987) 589-3940","address":"720 Mill Avenue, Gardiner, Nebraska, 6579","about":"Dolor deserunt nisi laborum eiusmod aliquip adipisicing ullamco eiusmod non qui consectetur est nostrud. Incididunt nostrud labore minim Lorem elit qui commodo consectetur ex culpa veniam. Quis ad quis cillum esse nisi ea ipsum consequat dolore. Non minim minim sint excepteur anim consectetur est voluptate laborum commodo. Mollit ea magna dolor sit ut magna voluptate ipsum reprehenderit id ex cillum deserunt.\r\n","registered":"2014-07-03T10:56:30 -07:00","latitude":-39.091253,"longitude":-131.671201,"tags":["labore","enim","nulla","est","exercitation","ipsum","deserunt"],"friends":[{"id":0,"name":"Emerson Dalton"},{"id":1,"name":"Owen Bass"},{"id":2,"name":"Bell Shannon"}],"greeting":"Hello, Rosalind Duran! You have 1 unread messages.","favoriteFruit":"banana"},{"_id":"6973b4f2edea35a5d2cea142","index":13,"guid":"18cf8f91-9735-4fa6-9381-ae8c1060890c","isActive":true,"balance":"$1,811.69","picture":"http://placehold.it/32x32","age":26,"eyeColor":"blue","name":"Pauline Cain","gender":"female","company":"ENAUT","email":"paulinecain@enaut.com","phone":"+1 (972) 408-3809","address":"508 Rapelye Street, Thynedale, Maine, 9391","about":"Qui enim fugiat do eu in eu aliqua elit cillum occaecat. Consectetur amet pariatur ea dolore Lorem eu ipsum veniam commodo commodo. Mollit dolore velit aliqua cillum elit consequat sunt nostrud in qui ipsum sit eu irure. Cupidatat reprehenderit labore in officia laborum.\r\n","registered":"2023-12-23T01:34:49 -07:00","latitude":-18.152332,"longitude":6.220963,"tags":["quis","sint","proident","officia","dolore","elit","quis"],"friends":[{"id":0,"name":"Kristie Shepherd"},{"id":1,"name":"Jensen Webster"},{"id":2,"name":"Langley Young"}],"greeting":"Hello, Pauline Cain! You have 7 unread messages.","favoriteFruit":"apple"}]',
|
|
302
|
+
testMessage: mediumMsg,
|
|
307
303
|
workerCount: WORKER_COUNT,
|
|
308
304
|
});
|
|
309
|
-
printResults(
|
|
310
|
-
"Test 3: Large Messages (50K msgs, 10KB)",
|
|
311
|
-
test3Results,
|
|
312
|
-
);
|
|
305
|
+
printResults("Test 3: Medium Messages (100K msgs, 10KB)", test3Results);
|
|
313
306
|
|
|
314
307
|
await cleanup();
|
|
315
308
|
|
|
316
|
-
|
|
309
|
+
// Calculate combined averages
|
|
310
|
+
const avgThroughput =
|
|
311
|
+
(test1Results.throughput +
|
|
312
|
+
test2Results.throughput +
|
|
313
|
+
test3Results.throughput) /
|
|
314
|
+
3;
|
|
315
|
+
const avgP50 =
|
|
316
|
+
(test1Results.p50 + test2Results.p50 + test3Results.p50) / 3;
|
|
317
|
+
const avgP95 =
|
|
318
|
+
(test1Results.p95 + test2Results.p95 + test3Results.p95) / 3;
|
|
319
|
+
const avgP99 =
|
|
320
|
+
(test1Results.p99 + test2Results.p99 + test3Results.p99) / 3;
|
|
317
321
|
|
|
318
322
|
// Summary
|
|
319
323
|
log("bright", `\n${"=".repeat(60)}`);
|
|
320
324
|
log("magenta", " š BENCHMARK SUMMARY");
|
|
321
325
|
log("bright", "=".repeat(60));
|
|
322
326
|
|
|
323
|
-
console.log("\n
|
|
327
|
+
console.log("\n Individual Queue Performance:");
|
|
324
328
|
console.log(
|
|
325
|
-
` -
|
|
329
|
+
` - Tiny messages (100B): ${test1Results.throughput.toFixed(0)} msg/s (p50: ${test1Results.p50.toFixed(2)}ms)`,
|
|
326
330
|
);
|
|
327
331
|
console.log(
|
|
328
|
-
` -
|
|
332
|
+
` - Small messages (1KB): ${test2Results.throughput.toFixed(0)} msg/s (p50: ${test2Results.p50.toFixed(2)}ms)`,
|
|
329
333
|
);
|
|
330
334
|
console.log(
|
|
331
|
-
` -
|
|
335
|
+
` - Medium messages (10KB): ${test3Results.throughput.toFixed(0)} msg/s (p50: ${test3Results.p50.toFixed(2)}ms)`,
|
|
332
336
|
);
|
|
333
337
|
|
|
334
|
-
log("bright",
|
|
338
|
+
log("bright", `\n${"=".repeat(60)}`);
|
|
335
339
|
|
|
336
340
|
log("green", "\nā
All stress tests completed successfully!");
|
|
337
341
|
|
|
338
342
|
log(
|
|
339
343
|
"yellow",
|
|
340
|
-
"\nš” To update README.md Performance section, use these values:",
|
|
344
|
+
"\nš” To update README.md Performance section, use these values (averaged across all tests):",
|
|
341
345
|
);
|
|
346
|
+
console.log("\n Overall:");
|
|
342
347
|
log(
|
|
343
348
|
"cyan",
|
|
344
|
-
` - Throughput
|
|
349
|
+
` - **Throughput**: ~${Math.round(avgThroughput).toLocaleString()} messages/second`,
|
|
345
350
|
);
|
|
346
|
-
log("cyan", ` - Latency (p50)
|
|
347
|
-
log("cyan", ` - Latency (p95)
|
|
348
|
-
log("cyan", ` - Latency (p99)
|
|
351
|
+
log("cyan", ` - **Latency (p50)**: ${avgP50.toFixed(2)} ms`);
|
|
352
|
+
log("cyan", ` - **Latency (p95)**: ${avgP95.toFixed(2)} ms`);
|
|
353
|
+
log("cyan", ` - **Latency (p99)**: ${avgP99.toFixed(2)} ms`);
|
|
349
354
|
} catch (error) {
|
|
350
355
|
log(
|
|
351
356
|
"red",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"id":"msg_82103","type":"notification","timestamp":1678892301,"payload":{"user_id":4092,"event":"login_attempt","status":"success"}}
|
package/biome.json
CHANGED
package/compose.yml
CHANGED
package/package.json
CHANGED