sidekiq-ts 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.
Files changed (86) hide show
  1. package/README.md +686 -0
  2. package/dist/api.d.ts +172 -0
  3. package/dist/api.d.ts.map +1 -0
  4. package/dist/api.js +679 -0
  5. package/dist/backtrace.d.ts +3 -0
  6. package/dist/backtrace.d.ts.map +1 -0
  7. package/dist/backtrace.js +16 -0
  8. package/dist/cli-helpers.d.ts +22 -0
  9. package/dist/cli-helpers.d.ts.map +1 -0
  10. package/dist/cli-helpers.js +152 -0
  11. package/dist/cli.d.ts +3 -0
  12. package/dist/cli.d.ts.map +1 -0
  13. package/dist/cli.js +143 -0
  14. package/dist/client.d.ts +25 -0
  15. package/dist/client.d.ts.map +1 -0
  16. package/dist/client.js +212 -0
  17. package/dist/config-loader.d.ts +16 -0
  18. package/dist/config-loader.d.ts.map +1 -0
  19. package/dist/config-loader.js +37 -0
  20. package/dist/config.d.ts +59 -0
  21. package/dist/config.d.ts.map +1 -0
  22. package/dist/config.js +155 -0
  23. package/dist/context.d.ts +10 -0
  24. package/dist/context.d.ts.map +1 -0
  25. package/dist/context.js +29 -0
  26. package/dist/cron.d.ts +44 -0
  27. package/dist/cron.d.ts.map +1 -0
  28. package/dist/cron.js +173 -0
  29. package/dist/index.d.ts +16 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +14 -0
  32. package/dist/interrupt-handler.d.ts +8 -0
  33. package/dist/interrupt-handler.d.ts.map +1 -0
  34. package/dist/interrupt-handler.js +24 -0
  35. package/dist/iterable-constants.d.ts +3 -0
  36. package/dist/iterable-constants.d.ts.map +1 -0
  37. package/dist/iterable-constants.js +2 -0
  38. package/dist/iterable-errors.d.ts +10 -0
  39. package/dist/iterable-errors.d.ts.map +1 -0
  40. package/dist/iterable-errors.js +18 -0
  41. package/dist/iterable.d.ts +44 -0
  42. package/dist/iterable.d.ts.map +1 -0
  43. package/dist/iterable.js +298 -0
  44. package/dist/job-logger.d.ts +12 -0
  45. package/dist/job-logger.d.ts.map +1 -0
  46. package/dist/job-logger.js +64 -0
  47. package/dist/job-util.d.ts +8 -0
  48. package/dist/job-util.d.ts.map +1 -0
  49. package/dist/job-util.js +158 -0
  50. package/dist/job.d.ts +73 -0
  51. package/dist/job.d.ts.map +1 -0
  52. package/dist/job.js +200 -0
  53. package/dist/json.d.ts +3 -0
  54. package/dist/json.d.ts.map +1 -0
  55. package/dist/json.js +2 -0
  56. package/dist/leader.d.ts +63 -0
  57. package/dist/leader.d.ts.map +1 -0
  58. package/dist/leader.js +193 -0
  59. package/dist/logger.d.ts +53 -0
  60. package/dist/logger.d.ts.map +1 -0
  61. package/dist/logger.js +143 -0
  62. package/dist/middleware.d.ts +23 -0
  63. package/dist/middleware.d.ts.map +1 -0
  64. package/dist/middleware.js +92 -0
  65. package/dist/periodic.d.ts +80 -0
  66. package/dist/periodic.d.ts.map +1 -0
  67. package/dist/periodic.js +205 -0
  68. package/dist/redis.d.ts +3 -0
  69. package/dist/redis.d.ts.map +1 -0
  70. package/dist/redis.js +1 -0
  71. package/dist/registry.d.ts +11 -0
  72. package/dist/registry.d.ts.map +1 -0
  73. package/dist/registry.js +8 -0
  74. package/dist/runner.d.ts +81 -0
  75. package/dist/runner.d.ts.map +1 -0
  76. package/dist/runner.js +791 -0
  77. package/dist/sidekiq.d.ts +43 -0
  78. package/dist/sidekiq.d.ts.map +1 -0
  79. package/dist/sidekiq.js +189 -0
  80. package/dist/testing.d.ts +32 -0
  81. package/dist/testing.d.ts.map +1 -0
  82. package/dist/testing.js +112 -0
  83. package/dist/types.d.ts +116 -0
  84. package/dist/types.d.ts.map +1 -0
  85. package/dist/types.js +1 -0
  86. package/package.json +42 -0
package/README.md ADDED
@@ -0,0 +1,686 @@
1
+ # sidekiq-ts
2
+
3
+ A TypeScript implementation of [Sidekiq](https://sidekiq.org/) for Node.js. Process background jobs with Redis-backed queues, featuring type-safe job definitions, automatic retries, scheduled jobs, leader election, and cron scheduling.
4
+
5
+ ## Features
6
+
7
+ - **Type-safe jobs** - Generic argument types with full TypeScript support
8
+ - **Multiple queues** - Priority-based queue processing with weighted selection
9
+ - **Job scheduling** - Execute jobs immediately, after a delay, or at specific times
10
+ - **Automatic retries** - Exponential backoff with configurable retry limits
11
+ - **Dead letter queue** - Failed jobs preserved for debugging
12
+ - **Leader election** - Coordinate across distributed workers
13
+ - **Cron jobs** - Periodic job scheduling with standard cron expressions
14
+ - **Middleware** - Customize job enqueueing and execution
15
+ - **CLI** - Run workers from the command line
16
+ - **Testing utilities** - Fake and inline modes for testing
17
+
18
+ ## Requirements
19
+
20
+ - Node.js >= 24.12.0
21
+ - Redis server
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ npm install sidekiq-ts
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ```typescript
32
+ import { Job, Sidekiq } from "sidekiq-ts";
33
+
34
+ // Configure Redis connection
35
+ Sidekiq.defaultConfiguration.redis = { url: "redis://localhost:6379" };
36
+
37
+ // Define a job
38
+ class WelcomeEmailJob extends Job<[string, string]> {
39
+ perform(email: string, name: string) {
40
+ console.log(`Sending welcome email to ${name} <${email}>`);
41
+ }
42
+ }
43
+
44
+ // Register the job
45
+ Sidekiq.registerJob(WelcomeEmailJob);
46
+
47
+ // Enqueue jobs
48
+ await WelcomeEmailJob.performAsync("alice@example.com", "Alice");
49
+
50
+ // Start the worker
51
+ const runner = await Sidekiq.run();
52
+
53
+ // Later, stop gracefully
54
+ await runner.stop();
55
+ ```
56
+
57
+ ## Defining Jobs
58
+
59
+ Extend the `Job` class with a type parameter specifying your argument types:
60
+
61
+ ```typescript
62
+ import { Job } from "sidekiq-ts";
63
+
64
+ class ProcessOrderJob extends Job<[string, number, boolean]> {
65
+ perform(orderId: string, amount: number, isPriority: boolean) {
66
+ // Your job logic here
67
+ }
68
+ }
69
+ ```
70
+
71
+ The `perform` method can be synchronous or return a Promise:
72
+
73
+ ```typescript
74
+ class FetchDataJob extends Job<[string]> {
75
+ async perform(url: string) {
76
+ const response = await fetch(url);
77
+ // Process response...
78
+ }
79
+ }
80
+ ```
81
+
82
+ ### Accessing Job Context
83
+
84
+ Inside `perform`, you can access:
85
+
86
+ ```typescript
87
+ class MyJob extends Job<[string]> {
88
+ perform(data: string) {
89
+ // Unique job ID
90
+ console.log(this.jid);
91
+
92
+ // Check if worker is stopping (for graceful shutdown)
93
+ if (this._context?.stopping()) {
94
+ return; // Exit early
95
+ }
96
+ }
97
+ }
98
+ ```
99
+
100
+ ## Job Options
101
+
102
+ Configure jobs using the static `sidekiqOptions` property:
103
+
104
+ ```typescript
105
+ class PaymentJob extends Job<[string, number]> {
106
+ static sidekiqOptions = {
107
+ queue: "critical", // Queue name (default: "default")
108
+ retry: 5, // Number of retries (default: 25, false to disable)
109
+ backtrace: 10, // Lines of backtrace to keep (default: false)
110
+ dead: true, // Add to dead queue on failure (default: true)
111
+ tags: ["payments"], // Tags for categorization
112
+ };
113
+
114
+ perform(orderId: string, amount: number) {
115
+ // ...
116
+ }
117
+ }
118
+ ```
119
+
120
+ ### Custom Retry Delay
121
+
122
+ Override the default exponential backoff:
123
+
124
+ ```typescript
125
+ PaymentJob.retryIn((retryCount, error, payload) => {
126
+ // Return delay in seconds
127
+ return 10 * Math.pow(2, retryCount - 1); // 10s, 20s, 40s, 80s...
128
+ });
129
+ ```
130
+
131
+ ### Retries Exhausted Handler
132
+
133
+ Handle permanent failures:
134
+
135
+ ```typescript
136
+ PaymentJob.retriesExhausted((payload, error) => {
137
+ console.log(`Job ${payload.jid} failed permanently: ${error.message}`);
138
+ // Notify external service, send alerts, etc.
139
+
140
+ return undefined; // Send to dead queue
141
+ // return "discard"; // Skip dead queue entirely
142
+ });
143
+ ```
144
+
145
+ ### Per-Job Option Override
146
+
147
+ Override options for a specific enqueue:
148
+
149
+ ```typescript
150
+ await NotificationJob.set({ queue: "critical" }).performAsync(userId, message);
151
+ ```
152
+
153
+ ## Enqueueing Jobs
154
+
155
+ ### Immediate Execution
156
+
157
+ ```typescript
158
+ const jid = await MyJob.performAsync(arg1, arg2);
159
+ ```
160
+
161
+ ### Delayed Execution
162
+
163
+ Execute after a delay (in seconds):
164
+
165
+ ```typescript
166
+ const jid = await ReportJob.performIn(300, "daily-report"); // 5 minutes
167
+ ```
168
+
169
+ ### Scheduled Execution
170
+
171
+ Execute at a specific Unix timestamp:
172
+
173
+ ```typescript
174
+ const tomorrow = Math.floor(Date.now() / 1000) + 86400;
175
+ const jid = await ReportJob.performAt(tomorrow, "weekly-report");
176
+ ```
177
+
178
+ ### Bulk Enqueueing
179
+
180
+ Enqueue multiple jobs efficiently:
181
+
182
+ ```typescript
183
+ const jids = await ReminderJob.performBulk([
184
+ [1, "Meeting at 3pm"],
185
+ [2, "Review PR #123"],
186
+ [3, "Deploy to staging"],
187
+ ]);
188
+ ```
189
+
190
+ ## Configuration
191
+
192
+ ### Redis Connection
193
+
194
+ ```typescript
195
+ Sidekiq.defaultConfiguration.redis = {
196
+ url: "redis://localhost:6379",
197
+ // Or use environment variable
198
+ // url: process.env.REDIS_URL
199
+ };
200
+ ```
201
+
202
+ ### Server Configuration
203
+
204
+ Configure worker-specific options:
205
+
206
+ ```typescript
207
+ Sidekiq.configureServer({
208
+ concurrency: 10, // Worker threads (default: 5)
209
+ timeout: 30, // Shutdown timeout in seconds (default: 25)
210
+ maxRetries: 25, // Default retry limit (default: 25)
211
+ tag: "worker-1", // Process tag for identification
212
+ labels: ["api", "prod"], // Process labels
213
+ });
214
+ ```
215
+
216
+ ### Client Configuration
217
+
218
+ Configure client-only options (for processes that only enqueue jobs):
219
+
220
+ ```typescript
221
+ Sidekiq.configureClient({
222
+ strictArgs: "raise", // "raise" | "warn" | "none"
223
+ });
224
+ ```
225
+
226
+ ## Queue Configuration
227
+
228
+ ### Simple Queues
229
+
230
+ Process queues in random order with equal priority:
231
+
232
+ ```typescript
233
+ Sidekiq.configureServer({
234
+ queues: ["default", "emails", "reports"],
235
+ });
236
+ ```
237
+
238
+ ### Weighted Queues
239
+
240
+ Higher weights get proportionally more processing time:
241
+
242
+ ```typescript
243
+ Sidekiq.configureServer({
244
+ queues: [
245
+ ["critical", 5], // 5x weight
246
+ ["default", 2], // 2x weight
247
+ ["background", 1], // 1x weight
248
+ ],
249
+ });
250
+ ```
251
+
252
+ ## Running Workers
253
+
254
+ ### Programmatic
255
+
256
+ ```typescript
257
+ import { Sidekiq } from "sidekiq-ts";
258
+
259
+ // Import your job files to register them
260
+ import "./jobs/email-job.js";
261
+ import "./jobs/report-job.js";
262
+
263
+ const runner = await Sidekiq.run();
264
+
265
+ // Handle shutdown signals
266
+ process.on("SIGTERM", async () => {
267
+ await runner.stop();
268
+ process.exit(0);
269
+ });
270
+ ```
271
+
272
+ ### CLI
273
+
274
+ ```bash
275
+ sidekiq-ts [options]
276
+ ```
277
+
278
+ **Options:**
279
+
280
+ | Option | Description |
281
+ |--------|-------------|
282
+ | `-C, --config PATH` | Path to JSON config file (default: sidekiq.json if present) |
283
+ | `-c, --concurrency NUM` | Number of worker threads |
284
+ | `-e, --environment ENV` | Application environment |
285
+ | `-g, --tag TAG` | Process tag for identification |
286
+ | `-q, --queue QUEUE[,WT]` | Queue with optional weight (repeatable) |
287
+ | `-r, --require PATH` | File to import before startup (repeatable) |
288
+ | `-t, --timeout NUM` | Shutdown timeout in seconds |
289
+ | `-v, --verbose` | Enable debug logging |
290
+ | `-V, --version` | Print version and exit |
291
+ | `-h, --help` | Show help message |
292
+
293
+ **Examples:**
294
+
295
+ ```bash
296
+ # Basic usage
297
+ sidekiq-ts -r ./dist/jobs.js
298
+
299
+ # Multiple queues with weights
300
+ sidekiq-ts -q critical,5 -q default,2 -q background -c 10
301
+
302
+ # With config file
303
+ sidekiq-ts -C config/sidekiq.json -e production
304
+ ```
305
+
306
+ ## Configuration File
307
+
308
+ Create `sidekiq.json` in your project root:
309
+
310
+ ```json
311
+ {
312
+ "concurrency": 10,
313
+ "queues": [
314
+ ["critical", 5],
315
+ ["default", 1]
316
+ ],
317
+ "timeout": 30,
318
+ "require": [
319
+ "./dist/jobs/index.js"
320
+ ],
321
+ "redis": {
322
+ "url": "redis://localhost:6379"
323
+ },
324
+ "development": {
325
+ "concurrency": 2
326
+ },
327
+ "production": {
328
+ "concurrency": 20,
329
+ "redis": {
330
+ "url": "redis://prod-redis:6379"
331
+ }
332
+ }
333
+ }
334
+ ```
335
+
336
+ Environment-specific settings override the defaults when `NODE_ENV` matches.
337
+
338
+ ## Middleware
339
+
340
+ ### Client Middleware
341
+
342
+ Intercept jobs during enqueueing:
343
+
344
+ ```typescript
345
+ import { Sidekiq } from "sidekiq-ts";
346
+
347
+ class LoggingMiddleware {
348
+ call(
349
+ jobClass: string | unknown,
350
+ payload: JobPayload,
351
+ queue: string,
352
+ redis: RedisClient,
353
+ next: () => Promise<JobPayload | null>
354
+ ) {
355
+ console.log(`Enqueueing ${payload.class} to ${queue}`);
356
+ return next();
357
+ }
358
+ }
359
+
360
+ Sidekiq.useClientMiddleware(LoggingMiddleware);
361
+ ```
362
+
363
+ ### Server Middleware
364
+
365
+ Intercept jobs during execution:
366
+
367
+ ```typescript
368
+ class TimingMiddleware {
369
+ async call(
370
+ job: Job,
371
+ payload: JobPayload,
372
+ queue: string,
373
+ next: () => Promise<void>
374
+ ) {
375
+ const start = Date.now();
376
+ try {
377
+ await next();
378
+ } finally {
379
+ console.log(`${payload.class} took ${Date.now() - start}ms`);
380
+ }
381
+ }
382
+ }
383
+
384
+ Sidekiq.useServerMiddleware(TimingMiddleware);
385
+ ```
386
+
387
+ ## Leader Election
388
+
389
+ For tasks that should only run on one worker (like cron jobs), use leader election:
390
+
391
+ ```typescript
392
+ const runner = await Sidekiq.run();
393
+
394
+ // Check if this process is the leader
395
+ if (runner.leader()) {
396
+ console.log("I am the leader!");
397
+ }
398
+ ```
399
+
400
+ ### Leader Lifecycle Events
401
+
402
+ ```typescript
403
+ Sidekiq.on("leader", () => {
404
+ console.log("Became leader");
405
+ });
406
+
407
+ Sidekiq.on("follower", () => {
408
+ console.log("Lost leadership");
409
+ });
410
+ ```
411
+
412
+ ## Periodic Jobs (Cron)
413
+
414
+ Schedule jobs to run on a cron schedule. Only the leader process enqueues periodic jobs.
415
+
416
+ ```typescript
417
+ import { Job, Sidekiq } from "sidekiq-ts";
418
+
419
+ class DailyReportJob extends Job<[]> {
420
+ perform() {
421
+ console.log("Generating daily report...");
422
+ }
423
+ }
424
+
425
+ Sidekiq.registerJob(DailyReportJob);
426
+
427
+ const runner = await Sidekiq.run();
428
+
429
+ // Register a cron job (runs at midnight daily)
430
+ runner.periodicScheduler.register("0 0 * * *", DailyReportJob, {
431
+ queue: "reports",
432
+ args: [],
433
+ });
434
+ ```
435
+
436
+ ### Cron Expression Format
437
+
438
+ Standard 5-field cron expressions:
439
+
440
+ ```
441
+ ┌───────────── minute (0-59)
442
+ │ ┌───────────── hour (0-23)
443
+ │ │ ┌───────────── day of month (1-31)
444
+ │ │ │ ┌───────────── month (1-12 or jan-dec)
445
+ │ │ │ │ ┌───────────── day of week (0-6 or sun-sat)
446
+ │ │ │ │ │
447
+ * * * * *
448
+ ```
449
+
450
+ **Examples:**
451
+
452
+ | Expression | Description |
453
+ |------------|-------------|
454
+ | `* * * * *` | Every minute |
455
+ | `*/5 * * * *` | Every 5 minutes |
456
+ | `0 * * * *` | Every hour |
457
+ | `0 0 * * *` | Daily at midnight |
458
+ | `0 9 * * 1-5` | Weekdays at 9am |
459
+ | `0 0 1 * *` | First day of month |
460
+
461
+ ## Monitoring & API
462
+
463
+ ### Statistics
464
+
465
+ ```typescript
466
+ import { Stats } from "sidekiq-ts";
467
+
468
+ const stats = new Stats(config);
469
+ await stats.fetch();
470
+
471
+ console.log(stats.processed); // Total processed jobs
472
+ console.log(stats.failed); // Total failed jobs
473
+ console.log(stats.enqueued); // Jobs waiting in queues
474
+ console.log(stats.scheduled); // Scheduled job count
475
+ console.log(stats.retry); // Jobs awaiting retry
476
+ console.log(stats.dead); // Jobs in dead queue
477
+ console.log(stats.processes); // Active worker count
478
+ ```
479
+
480
+ ### Queue Information
481
+
482
+ ```typescript
483
+ import { Queue } from "sidekiq-ts";
484
+
485
+ const queue = new Queue("default", config);
486
+ console.log(await queue.size()); // Jobs in queue
487
+ console.log(await queue.latency()); // Oldest job age in seconds
488
+ await queue.clear(); // Remove all jobs
489
+ ```
490
+
491
+ ### Process Information
492
+
493
+ ```typescript
494
+ import { ProcessSet } from "sidekiq-ts";
495
+
496
+ const processes = new ProcessSet(config);
497
+ for await (const process of processes) {
498
+ console.log(process.identity); // hostname:pid
499
+ console.log(process.concurrency); // Worker threads
500
+ console.log(process.busy); // Currently processing
501
+ console.log(process.queues); // Assigned queues
502
+ }
503
+ ```
504
+
505
+ ### Dead Jobs
506
+
507
+ ```typescript
508
+ import { DeadSet } from "sidekiq-ts";
509
+
510
+ const dead = new DeadSet(config);
511
+ console.log(await dead.size());
512
+
513
+ for await (const entry of dead) {
514
+ console.log(entry.item.class);
515
+ console.log(entry.item.error_message);
516
+
517
+ await entry.retry(); // Re-enqueue the job
518
+ // or
519
+ await entry.delete(); // Remove permanently
520
+ }
521
+ ```
522
+
523
+ ## Testing
524
+
525
+ ### Fake Mode
526
+
527
+ Collect jobs without executing them:
528
+
529
+ ```typescript
530
+ import { Testing, Queues } from "sidekiq-ts";
531
+
532
+ Testing.fake();
533
+
534
+ await MyJob.performAsync("test");
535
+
536
+ // Check enqueued jobs
537
+ const jobs = Queues.jobs();
538
+ expect(jobs).toHaveLength(1);
539
+ expect(jobs[0].class).toBe("MyJob");
540
+
541
+ // Clear for next test
542
+ Queues.clearAll();
543
+
544
+ // Disable fake mode
545
+ Testing.disable();
546
+ ```
547
+
548
+ ### Inline Mode
549
+
550
+ Execute jobs synchronously:
551
+
552
+ ```typescript
553
+ Testing.inline();
554
+
555
+ await MyJob.performAsync("test"); // Executes immediately
556
+
557
+ Testing.disable();
558
+ ```
559
+
560
+ ### Scoped Testing
561
+
562
+ ```typescript
563
+ await Testing.fake(async () => {
564
+ await MyJob.performAsync("test");
565
+ expect(Queues.jobs()).toHaveLength(1);
566
+ }); // Automatically restores previous mode
567
+ ```
568
+
569
+ ## Error Handling
570
+
571
+ ### Error Handlers
572
+
573
+ Called for every job failure (including retries):
574
+
575
+ ```typescript
576
+ Sidekiq.on("error", (error, context) => {
577
+ console.error(`Job failed: ${error.message}`);
578
+ // Send to error tracking service
579
+ });
580
+ ```
581
+
582
+ ### Death Handlers
583
+
584
+ Called when a job exhausts all retries:
585
+
586
+ ```typescript
587
+ Sidekiq.on("death", (payload, error) => {
588
+ console.error(`Job ${payload.jid} died: ${error.message}`);
589
+ // Alert on-call, create incident ticket, etc.
590
+ });
591
+ ```
592
+
593
+ ## Lifecycle Events
594
+
595
+ ```typescript
596
+ Sidekiq.on("startup", () => {
597
+ console.log("Worker starting");
598
+ });
599
+
600
+ Sidekiq.on("quiet", () => {
601
+ console.log("Worker quieting (no new jobs)");
602
+ });
603
+
604
+ Sidekiq.on("shutdown", () => {
605
+ console.log("Worker shutting down");
606
+ });
607
+
608
+ Sidekiq.on("heartbeat", () => {
609
+ // Called every 10 seconds
610
+ });
611
+ ```
612
+
613
+ ## Graceful Shutdown
614
+
615
+ The worker handles these signals:
616
+
617
+ | Signal | Action |
618
+ |--------|--------|
619
+ | `SIGINT` | Graceful shutdown |
620
+ | `SIGTERM` | Graceful shutdown |
621
+ | `SIGTSTP` | Quiet mode (stop accepting new jobs) |
622
+ | `SIGTTIN` | Dump current job state to logs |
623
+
624
+ The shutdown timeout (default: 25 seconds) allows in-flight jobs to complete before forced termination.
625
+
626
+ ## Production Deployment
627
+
628
+ ### Process Manager
629
+
630
+ Use a process manager like systemd, PM2, or Docker:
631
+
632
+ **systemd example:**
633
+
634
+ ```ini
635
+ [Unit]
636
+ Description=Sidekiq Worker
637
+ After=network.target redis.service
638
+
639
+ [Service]
640
+ Type=simple
641
+ User=app
642
+ WorkingDirectory=/app
643
+ ExecStart=/usr/bin/node /app/node_modules/.bin/sidekiq-ts -C /app/sidekiq.json
644
+ Restart=always
645
+ RestartSec=5
646
+
647
+ [Install]
648
+ WantedBy=multi-user.target
649
+ ```
650
+
651
+ **PM2 example:**
652
+
653
+ ```javascript
654
+ // ecosystem.config.js
655
+ module.exports = {
656
+ apps: [{
657
+ name: "sidekiq-worker",
658
+ script: "node_modules/.bin/sidekiq-ts",
659
+ args: "-C sidekiq.json",
660
+ instances: 2,
661
+ exec_mode: "cluster",
662
+ }]
663
+ };
664
+ ```
665
+
666
+ ### Redis Configuration
667
+
668
+ For production Redis:
669
+
670
+ ```typescript
671
+ Sidekiq.defaultConfiguration.redis = {
672
+ url: process.env.REDIS_URL,
673
+ // Connection pool settings are handled by the redis package
674
+ };
675
+ ```
676
+
677
+ ### Monitoring
678
+
679
+ - Use the Stats API to build dashboards
680
+ - Set up alerts on the dead queue size
681
+ - Monitor process count and job latency
682
+ - Track processed/failed rates over time
683
+
684
+ ## License
685
+
686
+ See LICENSE for details.