@pingpolls/redisq 0.2.1 → 1.0.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 +23 -23
- package/app.test.ts +9 -9
- package/app.ts +2 -2
- package/benchmark/stress-worker.ts +3 -2
- package/benchmark/stress.ts +2 -2
- package/package.json +1 -1
- package/tsconfig.json +24 -24
package/README.md
CHANGED
|
@@ -26,10 +26,10 @@ bun install @pingpolls/redisq
|
|
|
26
26
|
## Quick Start
|
|
27
27
|
|
|
28
28
|
```typescript
|
|
29
|
-
import
|
|
29
|
+
import { RedisQ } from '@pingpolls/redisq';
|
|
30
30
|
|
|
31
31
|
// Initialize the queue
|
|
32
|
-
const queue = new
|
|
32
|
+
const queue = new RedisQ({
|
|
33
33
|
host: '127.0.0.1',
|
|
34
34
|
port: '6379',
|
|
35
35
|
namespace: 'myapp'
|
|
@@ -73,9 +73,9 @@ Batch queues (suffix `:batch`) collect messages over a time period and process t
|
|
|
73
73
|
Create a queue service in `src/lib/server/queue.ts`:
|
|
74
74
|
|
|
75
75
|
```typescript
|
|
76
|
-
import
|
|
76
|
+
import { RedisQ } from '@pingpolls/redisq';
|
|
77
77
|
|
|
78
|
-
export const queue = new
|
|
78
|
+
export const queue = new RedisQ({
|
|
79
79
|
host: import.meta.env.REDIS_HOST || '127.0.0.1',
|
|
80
80
|
port: import.meta.env.REDIS_PORT || '6379',
|
|
81
81
|
namespace: 'sveltekit-app'
|
|
@@ -163,9 +163,9 @@ export async function load() {
|
|
|
163
163
|
Create queue in `lib/queue.ts`:
|
|
164
164
|
|
|
165
165
|
```typescript
|
|
166
|
-
import
|
|
166
|
+
import { RedisQ } from '@pingpolls/redisq';
|
|
167
167
|
|
|
168
|
-
export const queue = new
|
|
168
|
+
export const queue = new RedisQ({
|
|
169
169
|
host: process.env.REDIS_HOST!,
|
|
170
170
|
port: process.env.REDIS_PORT!,
|
|
171
171
|
namespace: 'nextjs-app'
|
|
@@ -217,10 +217,10 @@ export async function POST(request: Request) {
|
|
|
217
217
|
Create queue plugin in `server/plugins/queue.ts`:
|
|
218
218
|
|
|
219
219
|
```typescript
|
|
220
|
-
import
|
|
220
|
+
import { RedisQ } from '@pingpolls/redisq';
|
|
221
221
|
|
|
222
222
|
export default defineNitroPlugin(async (nitroApp) => {
|
|
223
|
-
const queue = new
|
|
223
|
+
const queue = new RedisQ({
|
|
224
224
|
host: process.env.REDIS_HOST || '127.0.0.1',
|
|
225
225
|
port: process.env.REDIS_PORT || '6379',
|
|
226
226
|
namespace: 'nuxt-app'
|
|
@@ -260,11 +260,11 @@ export default defineEventHandler(async (event) => {
|
|
|
260
260
|
|
|
261
261
|
```typescript
|
|
262
262
|
import { Hono } from 'hono';
|
|
263
|
-
import
|
|
263
|
+
import { RedisQ } from '@pingpolls/redisq';
|
|
264
264
|
|
|
265
265
|
const app = new Hono();
|
|
266
266
|
|
|
267
|
-
const queue = new
|
|
267
|
+
const queue = new RedisQ({
|
|
268
268
|
host: '127.0.0.1',
|
|
269
269
|
port: '6379',
|
|
270
270
|
namespace: 'hono-app'
|
|
@@ -329,7 +329,7 @@ export default app;
|
|
|
329
329
|
### Constructor
|
|
330
330
|
|
|
331
331
|
```typescript
|
|
332
|
-
new
|
|
332
|
+
new RedisQ(options: QueueOptions)
|
|
333
333
|
```
|
|
334
334
|
|
|
335
335
|
**Options:**
|
|
@@ -586,11 +586,11 @@ REDIS_TLS=false
|
|
|
586
586
|
|
|
587
587
|
```typescript
|
|
588
588
|
import { describe, test, expect } from 'bun:test';
|
|
589
|
-
import
|
|
589
|
+
import { RedisQ } from '@pingpolls/redisq';
|
|
590
590
|
|
|
591
591
|
describe('Queue Tests', () => {
|
|
592
592
|
test('processes messages', async () => {
|
|
593
|
-
const queue = new
|
|
593
|
+
const queue = new RedisQ({ host: '127.0.0.1', port: '6379' });
|
|
594
594
|
|
|
595
595
|
await queue.createQueue({ qname: 'test' });
|
|
596
596
|
|
|
@@ -621,16 +621,16 @@ Run the stress test to benchmark on your hardware:
|
|
|
621
621
|
bun stress
|
|
622
622
|
```
|
|
623
623
|
|
|
624
|
-
Individual Queue Performance:
|
|
625
|
-
- **Tiny messages (100B)**:
|
|
626
|
-
- **Small messages (1KB)**:
|
|
627
|
-
- **Medium messages (10KB)**:
|
|
628
|
-
|
|
629
|
-
Overall:
|
|
630
|
-
- **Throughput**: ~
|
|
631
|
-
- **Latency (p50)**:
|
|
632
|
-
- **Latency (p95)**:
|
|
633
|
-
- **Latency (p99)**:
|
|
624
|
+
✅ Individual Queue Performance:
|
|
625
|
+
- **Tiny messages (100B)**: 49,302 msg/s (p50: 49.75ms)
|
|
626
|
+
- **Small messages (1KB)**: 35,061 msg/s (p50: 74.34ms)
|
|
627
|
+
- **Medium messages (10KB)**: 9,437 msg/s (p50: 213.58ms)
|
|
628
|
+
|
|
629
|
+
💡 Overall (Averaged across all tests):
|
|
630
|
+
- **Throughput**: ~31,267 messages/second
|
|
631
|
+
- **Latency (p50)**: 112.56 ms
|
|
632
|
+
- **Latency (p95)**: 312.11 ms
|
|
633
|
+
- **Latency (p99)**: 676.23 ms
|
|
634
634
|
|
|
635
635
|
Tested on Dockerized `redis:alpine` through WSL2 with 1 CPU and 1GB instance.
|
|
636
636
|
|
package/app.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { afterAll, describe, expect, test } from "bun:test";
|
|
2
|
-
import
|
|
2
|
+
import { RedisQ } from "./app";
|
|
3
3
|
|
|
4
4
|
const redisConfig = {
|
|
5
5
|
host: process.env.REDIS_HOST || "127.0.0.1",
|
|
@@ -16,9 +16,9 @@ const workerConfig = {
|
|
|
16
16
|
*/
|
|
17
17
|
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
18
18
|
|
|
19
|
-
describe("
|
|
19
|
+
describe("RedisQ Tests", () => {
|
|
20
20
|
test("1. Can send and receive single message", async () => {
|
|
21
|
-
const queue = new
|
|
21
|
+
const queue = new RedisQ(redisConfig);
|
|
22
22
|
|
|
23
23
|
await queue.createQueue({ qname: "test-basic" });
|
|
24
24
|
|
|
@@ -51,7 +51,7 @@ describe("RedisQueue Tests", () => {
|
|
|
51
51
|
});
|
|
52
52
|
|
|
53
53
|
test("2. Can send and receive delayed message", async () => {
|
|
54
|
-
const queue = new
|
|
54
|
+
const queue = new RedisQ(redisConfig);
|
|
55
55
|
|
|
56
56
|
await queue.createQueue({ qname: "test-delayed" });
|
|
57
57
|
|
|
@@ -97,7 +97,7 @@ describe("RedisQueue Tests", () => {
|
|
|
97
97
|
});
|
|
98
98
|
|
|
99
99
|
test("3. Can retry send and receive single message", async () => {
|
|
100
|
-
const queue = new
|
|
100
|
+
const queue = new RedisQ(redisConfig);
|
|
101
101
|
|
|
102
102
|
await queue.createQueue({
|
|
103
103
|
maxBackoffSeconds: 1,
|
|
@@ -141,7 +141,7 @@ describe("RedisQueue Tests", () => {
|
|
|
141
141
|
});
|
|
142
142
|
|
|
143
143
|
test("4. Can retry send and receive delayed message", async () => {
|
|
144
|
-
const queue = new
|
|
144
|
+
const queue = new RedisQ(redisConfig);
|
|
145
145
|
|
|
146
146
|
await queue.createQueue({
|
|
147
147
|
maxBackoffSeconds: 1,
|
|
@@ -198,7 +198,7 @@ describe("RedisQueue Tests", () => {
|
|
|
198
198
|
});
|
|
199
199
|
|
|
200
200
|
test("5. Can send and receive batched messages (multiple batches in same period)", async () => {
|
|
201
|
-
const queue = new
|
|
201
|
+
const queue = new RedisQ(redisConfig);
|
|
202
202
|
|
|
203
203
|
/**
|
|
204
204
|
* Create batch queue with 3-second interval
|
|
@@ -328,7 +328,7 @@ describe("RedisQueue Tests", () => {
|
|
|
328
328
|
});
|
|
329
329
|
|
|
330
330
|
test("6. Can send and receive batched messages with selective retry", async () => {
|
|
331
|
-
const queue = new
|
|
331
|
+
const queue = new RedisQ(redisConfig);
|
|
332
332
|
|
|
333
333
|
/**
|
|
334
334
|
* Create batch queue with 2-second interval and retry enabled
|
|
@@ -509,7 +509,7 @@ describe("RedisQueue Tests", () => {
|
|
|
509
509
|
test("7. Can handle high volume and concurrency", async () => {
|
|
510
510
|
const totalMessages = 10_000;
|
|
511
511
|
|
|
512
|
-
const queue = new
|
|
512
|
+
const queue = new RedisQ(redisConfig);
|
|
513
513
|
|
|
514
514
|
await queue.createQueue({ qname: "test-concurrency" });
|
|
515
515
|
|
package/app.ts
CHANGED
|
@@ -79,7 +79,7 @@ interface StoredBatchMeta {
|
|
|
79
79
|
attempt: number;
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
export class
|
|
82
|
+
export class RedisQ {
|
|
83
83
|
private redis: BunRedisClient;
|
|
84
84
|
private redisUrl: string;
|
|
85
85
|
private ns: string;
|
|
@@ -766,4 +766,4 @@ export class RedisQueue {
|
|
|
766
766
|
}
|
|
767
767
|
}
|
|
768
768
|
|
|
769
|
-
export default
|
|
769
|
+
export default { RedisQ };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { RedisQ } from "../app";
|
|
2
|
+
|
|
2
3
|
declare var self: Worker;
|
|
3
4
|
|
|
4
5
|
interface WorkerMessage {
|
|
@@ -22,7 +23,7 @@ self.addEventListener("message", async (event: MessageEvent<WorkerMessage>) => {
|
|
|
22
23
|
event.data.data;
|
|
23
24
|
|
|
24
25
|
try {
|
|
25
|
-
const queue = new
|
|
26
|
+
const queue = new RedisQ(redisConfig);
|
|
26
27
|
const latencies: number[] = [];
|
|
27
28
|
const CHUNK_SIZE = 1000;
|
|
28
29
|
|
package/benchmark/stress.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
3
|
import type { Worker } from "bun";
|
|
4
|
-
import
|
|
4
|
+
import { RedisQ } from "../app";
|
|
5
5
|
import mediumMsg from "./medium.txt";
|
|
6
6
|
import smallMsg from "./small.txt";
|
|
7
7
|
import tinyMsg from "./tiny.txt";
|
|
@@ -108,7 +108,7 @@ function printResults(testName: string, results: TestResults) {
|
|
|
108
108
|
async function testRegularQueueParallel(
|
|
109
109
|
config: StressTestConfig,
|
|
110
110
|
): Promise<TestResults> {
|
|
111
|
-
const queue = new
|
|
111
|
+
const queue = new RedisQ({
|
|
112
112
|
host: process.env.REDIS_HOST || "127.0.0.1",
|
|
113
113
|
namespace: "stress-test",
|
|
114
114
|
port: process.env.REDIS_PORT || "6379",
|
package/package.json
CHANGED
package/tsconfig.json
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"allowImportingTsExtensions": true,
|
|
4
|
+
"allowJs": true,
|
|
5
|
+
"jsx": "react-jsx",
|
|
6
|
+
// Environment setup & latest features
|
|
7
|
+
"lib": ["ESNext"],
|
|
8
|
+
"module": "Preserve",
|
|
9
|
+
"moduleDetection": "force",
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
// Bundler mode
|
|
12
|
+
"moduleResolution": "bundler",
|
|
13
|
+
"noEmit": true,
|
|
14
|
+
"noFallthroughCasesInSwitch": true,
|
|
15
|
+
"noImplicitOverride": true,
|
|
16
|
+
"noPropertyAccessFromIndexSignature": false,
|
|
17
|
+
"noUncheckedIndexedAccess": true,
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
"noUncheckedIndexedAccess": true,
|
|
22
|
-
"noImplicitOverride": true,
|
|
19
|
+
// Some stricter flags (disabled by default)
|
|
20
|
+
"noUnusedLocals": false,
|
|
21
|
+
"noUnusedParameters": false,
|
|
22
|
+
"skipLibCheck": true,
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
// Best practices
|
|
25
|
+
"strict": true,
|
|
26
|
+
"target": "ESNext",
|
|
27
|
+
"verbatimModuleSyntax": true
|
|
28
|
+
}
|
|
29
29
|
}
|