@pingpolls/redisq 0.2.0 → 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 +15 -151
- package/app.ts +13 -10
- 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
|
}, {
|
package/app.ts
CHANGED
|
@@ -550,7 +550,7 @@ export class RedisQueue {
|
|
|
550
550
|
|
|
551
551
|
private async processBatches(
|
|
552
552
|
qname: string,
|
|
553
|
-
handler: (
|
|
553
|
+
handler: (batch: BatchMessage) => Promise<{ success: boolean }>,
|
|
554
554
|
silent: boolean,
|
|
555
555
|
): Promise<void> {
|
|
556
556
|
if (this.isClosing) return;
|
|
@@ -600,7 +600,7 @@ export class RedisQueue {
|
|
|
600
600
|
async startWorker<QueueName extends `${string}:batch` | (string & {})>(
|
|
601
601
|
qname: QueueName,
|
|
602
602
|
handler: (
|
|
603
|
-
|
|
603
|
+
received: QueueName extends `${string}:batch`
|
|
604
604
|
? BatchMessage
|
|
605
605
|
: Message,
|
|
606
606
|
) => Promise<{ success: boolean }>,
|
|
@@ -630,7 +630,7 @@ export class RedisQueue {
|
|
|
630
630
|
await this.processBatches(
|
|
631
631
|
qname,
|
|
632
632
|
handler as (
|
|
633
|
-
|
|
633
|
+
received: BatchMessage,
|
|
634
634
|
) => Promise<{ success: boolean }>,
|
|
635
635
|
silent,
|
|
636
636
|
);
|
|
@@ -653,7 +653,7 @@ export class RedisQueue {
|
|
|
653
653
|
this.runWorker(
|
|
654
654
|
qname,
|
|
655
655
|
handler as (
|
|
656
|
-
|
|
656
|
+
received: Message,
|
|
657
657
|
) => Promise<{ success: boolean }>,
|
|
658
658
|
controller.signal,
|
|
659
659
|
i,
|
|
@@ -667,7 +667,7 @@ export class RedisQueue {
|
|
|
667
667
|
|
|
668
668
|
private async runWorker(
|
|
669
669
|
qname: string,
|
|
670
|
-
handler: (
|
|
670
|
+
handler: (received: Message) => Promise<{ success: boolean }>,
|
|
671
671
|
signal: AbortSignal,
|
|
672
672
|
workerId: number,
|
|
673
673
|
silent: boolean,
|
|
@@ -687,13 +687,16 @@ export class RedisQueue {
|
|
|
687
687
|
ids as string[],
|
|
688
688
|
);
|
|
689
689
|
|
|
690
|
-
const processingPromises = messages.map((
|
|
691
|
-
handler(
|
|
690
|
+
const processingPromises = messages.map((received) =>
|
|
691
|
+
handler(received)
|
|
692
692
|
.then(async ({ success }) => {
|
|
693
693
|
if (success) {
|
|
694
|
-
await this.deleteMessage(
|
|
694
|
+
await this.deleteMessage(
|
|
695
|
+
qname,
|
|
696
|
+
received.id,
|
|
697
|
+
);
|
|
695
698
|
} else {
|
|
696
|
-
await this.retryMessage(qname,
|
|
699
|
+
await this.retryMessage(qname, received.id);
|
|
697
700
|
}
|
|
698
701
|
})
|
|
699
702
|
.catch(async (error) => {
|
|
@@ -703,7 +706,7 @@ export class RedisQueue {
|
|
|
703
706
|
(error as Error).message,
|
|
704
707
|
);
|
|
705
708
|
}
|
|
706
|
-
await this.retryMessage(qname,
|
|
709
|
+
await this.retryMessage(qname, received.id);
|
|
707
710
|
}),
|
|
708
711
|
);
|
|
709
712
|
await Promise.all(processingPromises);
|
package/package.json
CHANGED