@elysiumjs/heracles 0.4.4 → 0.5.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 +83 -75
- package/dist/heracles.d.ts +13 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +2 -0
- package/dist/job.d.ts +249 -0
- package/dist/queue.d.ts +247 -0
- package/dist/transport.d.ts +427 -0
- package/dist/transports/redis.transport.d.ts +251 -0
- package/dist/utils.d.ts +34 -0
- package/dist/worker.d.ts +250 -0
- package/dist/workers/base.worker.d.ts +216 -0
- package/dist/workers/redis.worker.d.ts +57 -0
- package/package.json +12 -10
- package/tsconfig.json +34 -33
- package/USAGE.md +0 -527
- package/index.ts +0 -16
- package/src/heracles.ts +0 -68
- package/src/index.ts +0 -28
- package/src/job.ts +0 -413
- package/src/queue.ts +0 -567
- package/src/transport.ts +0 -518
- package/src/transports/redis.transport.ts +0 -1558
- package/src/transports/thread.transport.ts +0 -289
- package/src/utils.ts +0 -61
- package/src/worker.ts +0 -312
- package/src/worker_pool.ts +0 -393
- package/src/workers/base.worker.ts +0 -804
- package/src/workers/redis.worker.ts +0 -523
- package/src/workers/thread.worker.entry.ts +0 -171
- package/src/workers/thread.worker.ts +0 -525
- package/types.d.ts +0 -21
package/USAGE.md
DELETED
|
@@ -1,527 +0,0 @@
|
|
|
1
|
-
# Heracles Usage Guide
|
|
2
|
-
|
|
3
|
-
This document provides a practical guide to using the Heracles job processing framework in your Elysium.js application.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
bun add @elysiumjs/heracles
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Basic Concepts
|
|
12
|
-
|
|
13
|
-
- **Jobs**: Background tasks defined as classes
|
|
14
|
-
- **Queues**: Lists of jobs waiting to be processed
|
|
15
|
-
- **Workers**: Processes that execute jobs from queues
|
|
16
|
-
- **Transports**: Communication channels between queues and workers
|
|
17
|
-
- **Worker Pool**: Efficiently distributes jobs to workers
|
|
18
|
-
|
|
19
|
-
## Creating Jobs
|
|
20
|
-
|
|
21
|
-
Jobs are defined as classes that extend the `Job` base class:
|
|
22
|
-
|
|
23
|
-
```typescript
|
|
24
|
-
import { Job } from '@elysiumjs/heracles';
|
|
25
|
-
|
|
26
|
-
@Job.register({
|
|
27
|
-
queue: 'emails', // Queue to process this job
|
|
28
|
-
maxRetries: 3, // Max retry attempts
|
|
29
|
-
retryDelay: 5000 // Delay between retries (ms)
|
|
30
|
-
})
|
|
31
|
-
export class SendEmailJob extends Job {
|
|
32
|
-
constructor(
|
|
33
|
-
private readonly to: string,
|
|
34
|
-
private readonly subject: string,
|
|
35
|
-
private readonly body: string
|
|
36
|
-
) {
|
|
37
|
-
super(); // Required
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
protected async execute(): Promise<void> {
|
|
41
|
-
// Your job implementation here
|
|
42
|
-
console.log(`Sending email to ${this.to}: ${this.subject}`);
|
|
43
|
-
|
|
44
|
-
// Use this.debug(), this.info(), this.warning(), this.error() for logging
|
|
45
|
-
this.info(`Email sent successfully to ${this.to}`);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
## Dispatching Jobs
|
|
51
|
-
|
|
52
|
-
```typescript
|
|
53
|
-
import { Queue, RedisTransport } from '@elysiumjs/heracles';
|
|
54
|
-
|
|
55
|
-
import { SendEmailJob } from './jobs/send-email.job';
|
|
56
|
-
|
|
57
|
-
// Get the queue with Redis transport
|
|
58
|
-
const emailQueue = Queue.get('emails', {
|
|
59
|
-
transport: RedisTransport,
|
|
60
|
-
transportOptions: {
|
|
61
|
-
connection: 'default' // Redis connection name
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// Dispatch a job
|
|
66
|
-
await emailQueue.dispatch(SendEmailJob, [
|
|
67
|
-
'user@example.com',
|
|
68
|
-
'Welcome!',
|
|
69
|
-
'Welcome to our service!'
|
|
70
|
-
]);
|
|
71
|
-
|
|
72
|
-
// With additional options
|
|
73
|
-
await emailQueue.dispatch(
|
|
74
|
-
SendEmailJob,
|
|
75
|
-
['user@example.com', 'Welcome!', 'Welcome to our service!'],
|
|
76
|
-
{
|
|
77
|
-
priority: 5, // Higher priority (lower number)
|
|
78
|
-
scheduledFor: new Date(Date.now() + 3600000), // Run in 1 hour
|
|
79
|
-
maxRetries: 5, // Override max retries
|
|
80
|
-
retryDelay: 10000 // Override retry delay
|
|
81
|
-
}
|
|
82
|
-
);
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
## Queue Management
|
|
86
|
-
|
|
87
|
-
```typescript
|
|
88
|
-
// Create a custom queue with Redis transport
|
|
89
|
-
const queue = Queue.get('emails', {
|
|
90
|
-
concurrency: 5, // Process 5 jobs simultaneously
|
|
91
|
-
maxRetries: 3, // Default retries for failed jobs
|
|
92
|
-
retryDelay: 5000, // Default delay between retries
|
|
93
|
-
pauseOnError: false, // Don't pause on error
|
|
94
|
-
transport: RedisTransport, // Redis transport
|
|
95
|
-
transportOptions: {
|
|
96
|
-
// Transport-specific options
|
|
97
|
-
connection: 'default',
|
|
98
|
-
keyPrefix: 'myapp:jobs'
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
// Queue operations
|
|
103
|
-
await queue.pause(); // Pause processing
|
|
104
|
-
await queue.resume(); // Resume processing
|
|
105
|
-
await queue.drain(); // Complete current jobs, don't accept new ones
|
|
106
|
-
await queue.clear(); // Clear all jobs from queue
|
|
107
|
-
await queue.cancelJob('job_id'); // Cancel specific job
|
|
108
|
-
await queue.cancelAllJobs(); // Cancel all jobs
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
## Starting Workers
|
|
112
|
-
|
|
113
|
-
### Using the CLI
|
|
114
|
-
|
|
115
|
-
```bash
|
|
116
|
-
# Start a Redis worker for one queue
|
|
117
|
-
bun heracles work --id worker-1 --queues=emails --concurrency=3 --redis=default
|
|
118
|
-
|
|
119
|
-
# Start a Redis worker for multiple queues
|
|
120
|
-
bun heracles work --id worker-2 --queues=emails,notifications --redis=default --concurrency=4
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
CLI options:
|
|
124
|
-
|
|
125
|
-
- `--id`: Unique identifier for this worker (required)
|
|
126
|
-
- `--queues`: Comma-separated list of queues (default: "default")
|
|
127
|
-
- `--concurrency`: Number of concurrent jobs per worker (default: 1)
|
|
128
|
-
- `--redis`: Redis connection name (default: "default")
|
|
129
|
-
- `--max-retries`: Maximum retries for failed jobs (default: 3)
|
|
130
|
-
- `--retry-delay`: Delay between retries in milliseconds (default: 5000)
|
|
131
|
-
- `--pause-on-error`: Pause queue when error occurs (default: false)
|
|
132
|
-
- `--config`: Path to configuration file for more complex setups
|
|
133
|
-
|
|
134
|
-
### Programmatically
|
|
135
|
-
|
|
136
|
-
```typescript
|
|
137
|
-
import { RedisWorker } from '@elysiumjs/heracles';
|
|
138
|
-
|
|
139
|
-
// Create a Redis worker with a unique ID
|
|
140
|
-
const worker = new RedisWorker('default', { id: 'worker-1' });
|
|
141
|
-
|
|
142
|
-
// Add queues to the worker
|
|
143
|
-
await worker.createQueue({
|
|
144
|
-
name: 'emails',
|
|
145
|
-
concurrency: 3,
|
|
146
|
-
maxRetries: 5,
|
|
147
|
-
retryDelay: 5000
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
// Start the worker
|
|
151
|
-
await worker.start();
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
## Redis Setup
|
|
155
|
-
|
|
156
|
-
Heracles uses Redis Streams as its transport layer, enabling distributed job processing across multiple servers, applications, or processes. This section explains how to set up and configure Redis for Heracles.
|
|
157
|
-
|
|
158
|
-
### 1. Configure Redis Connection
|
|
159
|
-
|
|
160
|
-
First, register a Redis connection in your application:
|
|
161
|
-
|
|
162
|
-
```typescript
|
|
163
|
-
import { Redis } from '@elysiumjs/core';
|
|
164
|
-
|
|
165
|
-
// Register a Redis connection
|
|
166
|
-
Redis.registerConnection('default', {
|
|
167
|
-
url: 'redis://localhost:6379'
|
|
168
|
-
// Other Redis options like password, tls, etc.
|
|
169
|
-
});
|
|
170
|
-
```
|
|
171
|
-
|
|
172
|
-
### 2. Configure Queue with Redis
|
|
173
|
-
|
|
174
|
-
Configure your queues to use Redis:
|
|
175
|
-
|
|
176
|
-
```typescript
|
|
177
|
-
import { Queue, RedisTransport } from '@elysiumjs/heracles';
|
|
178
|
-
|
|
179
|
-
const queue = Queue.get('emails', {
|
|
180
|
-
transport: RedisTransport,
|
|
181
|
-
transportOptions: {
|
|
182
|
-
connection: 'default', // Redis connection name
|
|
183
|
-
keyPrefix: 'myapp:jobs', // Prefix for Redis keys (default: 'elysium:heracles')
|
|
184
|
-
consumerGroup: 'workers', // Redis consumer group name (default: 'workers')
|
|
185
|
-
|
|
186
|
-
// Stream cleanup options
|
|
187
|
-
cleanupCompletedJobs: true, // Auto-cleanup completed jobs (default: true)
|
|
188
|
-
completedJobRetention: 3600, // Seconds to keep completed jobs (default: 3600 - 1 hour)
|
|
189
|
-
maxStreamSize: 1000, // Maximum stream size to maintain (default: 1000)
|
|
190
|
-
|
|
191
|
-
// Advanced options
|
|
192
|
-
pollInterval: 1000, // Polling interval in milliseconds (default: 1000)
|
|
193
|
-
batchSize: 10, // Batch size for reading messages (default: 10)
|
|
194
|
-
statusTTL: 86400, // Time to live for job status in seconds (default: 86400 - 24 hours)
|
|
195
|
-
consumerName: 'consumer-xyz' // Unique consumer name (optional, auto-generated if not provided)
|
|
196
|
-
}
|
|
197
|
-
});
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
### 3. Understanding Heracles Architecture
|
|
201
|
-
|
|
202
|
-
Heracles uses Redis Streams as the message broker for job coordination:
|
|
203
|
-
|
|
204
|
-
- **Producer Mode**: The application that dispatches jobs operates as a producer
|
|
205
|
-
- **Consumer Mode**: Worker processes operate as consumers that process jobs
|
|
206
|
-
- **Job Status Storage**: Each job's status is stored in a Redis hash for quick updates
|
|
207
|
-
- **Streams**: One stream per queue for reliable, ordered message delivery
|
|
208
|
-
- **Consumer Groups**: Ensures each job is processed by only one worker
|
|
209
|
-
|
|
210
|
-
### 4. Efficient Storage Architecture
|
|
211
|
-
|
|
212
|
-
Heracles implements an efficient Redis storage strategy:
|
|
213
|
-
|
|
214
|
-
- **Status Hashes**: Job status is tracked in dedicated Redis hashes for quick updates
|
|
215
|
-
- **Minimal Storage**: Single entry per job in the Redis stream to minimize storage usage
|
|
216
|
-
- **In-place Updates**: Status updates modify the existing job status hash instead of creating new entries
|
|
217
|
-
- **Automatic Cleanup**: Background process runs every 5 minutes, removing completed job entries older than the retention period
|
|
218
|
-
- **Stream Trimming**: Streams are automatically trimmed to the maximum size to prevent unbounded growth
|
|
219
|
-
- **Job Locking**: Support for job locking to prevent concurrent execution of jobs with the same ID
|
|
220
|
-
|
|
221
|
-
## Job Status Lifecycle
|
|
222
|
-
|
|
223
|
-
1. **PENDING**: Job is queued but not yet started
|
|
224
|
-
2. **RUNNING**: Job is currently executing
|
|
225
|
-
3. **COMPLETED**: Job completed successfully
|
|
226
|
-
4. **FAILED**: Job failed (may be retried if retries available)
|
|
227
|
-
5. **CANCELLED**: Job was cancelled
|
|
228
|
-
6. **SCHEDULED_FOR_RETRY**: Job failed and scheduled for retry
|
|
229
|
-
|
|
230
|
-
## Deployment Strategies
|
|
231
|
-
|
|
232
|
-
### Distributed Deployment
|
|
233
|
-
|
|
234
|
-
Heracles is designed for distributed deployment across multiple servers, processes, or applications:
|
|
235
|
-
|
|
236
|
-
#### 1. Configure Redis in Applications
|
|
237
|
-
|
|
238
|
-
In all applications that dispatch or process jobs:
|
|
239
|
-
|
|
240
|
-
```typescript
|
|
241
|
-
import { Redis } from '@elysiumjs/core';
|
|
242
|
-
|
|
243
|
-
// Register the same Redis connection across all applications
|
|
244
|
-
Redis.registerConnection('default', {
|
|
245
|
-
url: 'redis://your-redis-server:6379'
|
|
246
|
-
});
|
|
247
|
-
```
|
|
248
|
-
|
|
249
|
-
#### 2. Configure Job Dispatch in Producer Applications
|
|
250
|
-
|
|
251
|
-
```typescript
|
|
252
|
-
import { Queue, RedisTransport } from '@elysiumjs/heracles';
|
|
253
|
-
|
|
254
|
-
// Create queues with Redis transport
|
|
255
|
-
const emailQueue = Queue.get('emails', {
|
|
256
|
-
transport: RedisTransport,
|
|
257
|
-
transportOptions: {
|
|
258
|
-
connection: 'default',
|
|
259
|
-
keyPrefix: 'myapp:jobs' // Use consistent prefix across all applications
|
|
260
|
-
}
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
// Dispatch jobs as usual
|
|
264
|
-
await emailQueue.dispatch(SendEmailJob, [
|
|
265
|
-
'user@example.com',
|
|
266
|
-
'Welcome!',
|
|
267
|
-
'Welcome to our service!'
|
|
268
|
-
]);
|
|
269
|
-
```
|
|
270
|
-
|
|
271
|
-
#### 3. Start Workers on Multiple Servers
|
|
272
|
-
|
|
273
|
-
```bash
|
|
274
|
-
# Server 1 (handles email jobs)
|
|
275
|
-
bun heracles work --id worker-emails-1 --queues=emails --redis=default --concurrency=4
|
|
276
|
-
|
|
277
|
-
# Server 2 (handles notification jobs)
|
|
278
|
-
bun heracles work --id worker-notifications-1 --queues=notifications --redis=default --concurrency=4
|
|
279
|
-
|
|
280
|
-
# Server 3 (handles heavy processing jobs)
|
|
281
|
-
bun heracles work --id worker-processing-1 --queues=processing --redis=default --concurrency=1
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
#### 4. Start Workers Programmatically
|
|
285
|
-
|
|
286
|
-
For more control, you can start workers programmatically:
|
|
287
|
-
|
|
288
|
-
```typescript
|
|
289
|
-
import { RedisWorker } from '@elysiumjs/heracles';
|
|
290
|
-
|
|
291
|
-
// Create worker for specific queues
|
|
292
|
-
const worker = new RedisWorker('default', {
|
|
293
|
-
id: 'worker-emails-1', // Unique worker ID
|
|
294
|
-
keyPrefix: 'myapp:jobs' // Match queue prefix
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
// Configure queues with specific settings
|
|
298
|
-
await worker.createQueue({
|
|
299
|
-
name: 'emails',
|
|
300
|
-
concurrency: 4,
|
|
301
|
-
maxRetries: 5,
|
|
302
|
-
retryDelay: 10000
|
|
303
|
-
});
|
|
304
|
-
|
|
305
|
-
// Start the worker
|
|
306
|
-
await worker.start();
|
|
307
|
-
|
|
308
|
-
// Graceful shutdown
|
|
309
|
-
process.on('SIGTERM', async () => {
|
|
310
|
-
await worker.stop(false); // false = finish processing current jobs
|
|
311
|
-
});
|
|
312
|
-
```
|
|
313
|
-
|
|
314
|
-
## Monitoring and Maintenance
|
|
315
|
-
|
|
316
|
-
When running Heracles in production, proper monitoring and maintenance is essential.
|
|
317
|
-
|
|
318
|
-
### Automatic Maintenance
|
|
319
|
-
|
|
320
|
-
Heracles automatically manages Redis streams:
|
|
321
|
-
|
|
322
|
-
1. **Auto Cleanup**: Every 5 minutes, completed jobs older than the retention period are removed
|
|
323
|
-
2. **Stream Trimming**: Streams are trimmed to the maximum size to prevent unbounded growth
|
|
324
|
-
3. **Status TTL**: Job status records have a configurable TTL (default: 24 hours)
|
|
325
|
-
|
|
326
|
-
### Manual Cleanup with CLI
|
|
327
|
-
|
|
328
|
-
For manual maintenance or scheduled cleanup, use the CLI:
|
|
329
|
-
|
|
330
|
-
```bash
|
|
331
|
-
# Basic cleanup with default settings
|
|
332
|
-
bun heracles cleanup --redis=default
|
|
333
|
-
|
|
334
|
-
# Targeted cleanup for specific queues
|
|
335
|
-
bun heracles cleanup --redis=default --queues=emails,notifications \
|
|
336
|
-
--keyprefix=myapp:jobs --retention=3600 --max=1000
|
|
337
|
-
|
|
338
|
-
# Only clean up job status keys, preserve stream entries
|
|
339
|
-
bun heracles cleanup --status-only
|
|
340
|
-
|
|
341
|
-
# Preview what would be removed without making changes
|
|
342
|
-
bun heracles cleanup --dry-run
|
|
343
|
-
|
|
344
|
-
# Remove all entries from streams (use with caution!)
|
|
345
|
-
bun heracles cleanup --all
|
|
346
|
-
```
|
|
347
|
-
|
|
348
|
-
#### Cleanup Command Options
|
|
349
|
-
|
|
350
|
-
| Option | Description | Default |
|
|
351
|
-
| --------------- | ---------------------------------------------------- | ------------------ |
|
|
352
|
-
| `--redis` | Redis connection name | "default" |
|
|
353
|
-
| `--queues` | Comma-separated list of queues to clean up | All queues |
|
|
354
|
-
| `--keyprefix` | Redis key prefix | "elysium:heracles" |
|
|
355
|
-
| `--retention` | Seconds to retain completed jobs before removal | 3600 (1 hour) |
|
|
356
|
-
| `--max` | Maximum stream size to trim to | 1000 |
|
|
357
|
-
| `--status-only` | Only clean up job status keys, not stream entries | false |
|
|
358
|
-
| `--dry-run` | Show what would be removed without actually removing | false |
|
|
359
|
-
| `--all` | Remove all entries, not just completed ones | false |
|
|
360
|
-
|
|
361
|
-
### Monitoring Redis Keys
|
|
362
|
-
|
|
363
|
-
Heracles uses the following Redis key patterns:
|
|
364
|
-
|
|
365
|
-
- `{keyPrefix}:stream:{queueName}` - Redis streams for each queue
|
|
366
|
-
- `{keyPrefix}:status:{queueName}:{jobId}:{dispatchId}` - Job status hashes
|
|
367
|
-
- `{keyPrefix}:lock:{queueName}:{jobId}` - Locks for NO_OVERLAP jobs
|
|
368
|
-
- `{keyPrefix}:worker:{workerId}` - Worker registration information
|
|
369
|
-
|
|
370
|
-
You can monitor these using Redis CLI or monitoring tools:
|
|
371
|
-
|
|
372
|
-
```bash
|
|
373
|
-
# Count stream entries
|
|
374
|
-
redis-cli XLEN elysium:heracles:stream:emails
|
|
375
|
-
|
|
376
|
-
# View active workers
|
|
377
|
-
redis-cli KEYS elysium:heracles:worker:*
|
|
378
|
-
|
|
379
|
-
# Count job status records
|
|
380
|
-
redis-cli KEYS elysium:heracles:status:emails:* | wc -l
|
|
381
|
-
```
|
|
382
|
-
|
|
383
|
-
## Advanced Features
|
|
384
|
-
|
|
385
|
-
### Job Overlap Behavior Control
|
|
386
|
-
|
|
387
|
-
You can control how multiple instances of the same job are handled using the `JobOverlapBehavior` setting:
|
|
388
|
-
|
|
389
|
-
```typescript
|
|
390
|
-
import { Job, JobOverlapBehavior } from '@elysiumjs/heracles';
|
|
391
|
-
|
|
392
|
-
@Job.register({
|
|
393
|
-
queue: 'emails',
|
|
394
|
-
overlapBehavior: JobOverlapBehavior.NO_OVERLAP, // Prevent parallel execution of same job ID
|
|
395
|
-
overlapDelay: 5000 // Wait 5 seconds after job completion before starting next one
|
|
396
|
-
})
|
|
397
|
-
export class SendEmailJob extends Job {
|
|
398
|
-
// Job implementation...
|
|
399
|
-
}
|
|
400
|
-
```
|
|
401
|
-
|
|
402
|
-
This ensures that even across multiple workers on different servers, only one instance of a job with the same ID runs at a time.
|
|
403
|
-
|
|
404
|
-
### Advanced Redis Configuration
|
|
405
|
-
|
|
406
|
-
For advanced Redis setups like Redis Cluster or Sentinel, configure the Redis connection appropriately:
|
|
407
|
-
|
|
408
|
-
```typescript
|
|
409
|
-
import { Redis } from '@elysiumjs/core';
|
|
410
|
-
|
|
411
|
-
// Example with Redis Sentinel
|
|
412
|
-
Redis.registerConnection('sentinel', {
|
|
413
|
-
sentinel: {
|
|
414
|
-
sentinels: [
|
|
415
|
-
{ host: '192.168.1.1', port: 26379 },
|
|
416
|
-
{ host: '192.168.1.2', port: 26379 }
|
|
417
|
-
],
|
|
418
|
-
name: 'mymaster'
|
|
419
|
-
},
|
|
420
|
-
password: 'your-password'
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
// Use in queue configuration
|
|
424
|
-
const queue = Queue.get('emails', {
|
|
425
|
-
transport: RedisTransport,
|
|
426
|
-
transportOptions: {
|
|
427
|
-
connection: 'sentinel'
|
|
428
|
-
// Other options...
|
|
429
|
-
}
|
|
430
|
-
});
|
|
431
|
-
```
|
|
432
|
-
|
|
433
|
-
### Worker Pool Management
|
|
434
|
-
|
|
435
|
-
When running multiple workers, they efficiently distribute workload through Redis:
|
|
436
|
-
|
|
437
|
-
```typescript
|
|
438
|
-
import { RedisWorker } from '@elysiumjs/heracles';
|
|
439
|
-
|
|
440
|
-
// Create multiple workers for the same queues
|
|
441
|
-
const worker1 = new RedisWorker('default', { id: 'worker-1' });
|
|
442
|
-
const worker2 = new RedisWorker('default', { id: 'worker-2' });
|
|
443
|
-
|
|
444
|
-
// Register both for the same queues
|
|
445
|
-
await worker1.createQueue({ name: 'emails', concurrency: 3 });
|
|
446
|
-
await worker2.createQueue({ name: 'emails', concurrency: 3 });
|
|
447
|
-
|
|
448
|
-
// Start both workers
|
|
449
|
-
await worker1.start();
|
|
450
|
-
await worker2.start();
|
|
451
|
-
|
|
452
|
-
// Jobs dispatched to the 'emails' queue will be automatically
|
|
453
|
-
// distributed between worker1 and worker2 by Redis consumer groups
|
|
454
|
-
```
|
|
455
|
-
|
|
456
|
-
## Best Practices
|
|
457
|
-
|
|
458
|
-
1. **Job Design**: Keep jobs small and focused on a single task
|
|
459
|
-
2. **Queue Organization**: Group similar jobs in the same queue
|
|
460
|
-
3. **Error Handling**: Implement proper error handling in your execute method
|
|
461
|
-
4. **Idempotent Jobs**: Design jobs to be safely retried
|
|
462
|
-
5. **Monitoring**: Regularly check worker status and queue sizes
|
|
463
|
-
6. **Resource Management**: Adjust concurrency based on the job's resource requirements
|
|
464
|
-
|
|
465
|
-
## Error Handling and Retries
|
|
466
|
-
|
|
467
|
-
Heracles automatically handles retries for failed jobs:
|
|
468
|
-
|
|
469
|
-
```typescript
|
|
470
|
-
@Job.register({
|
|
471
|
-
queue: 'emails',
|
|
472
|
-
maxRetries: 5, // Max retry attempts
|
|
473
|
-
retryDelay: 10000 // 10 seconds between retries
|
|
474
|
-
})
|
|
475
|
-
export class SendEmailJob extends Job {
|
|
476
|
-
protected async execute(): Promise<void> {
|
|
477
|
-
try {
|
|
478
|
-
// Your job logic
|
|
479
|
-
} catch (error) {
|
|
480
|
-
// Log error details
|
|
481
|
-
this.error(`Failed to send email: ${error.message}`);
|
|
482
|
-
|
|
483
|
-
// Throwing the error will mark the job as failed
|
|
484
|
-
// and trigger retry if retries are available
|
|
485
|
-
throw error;
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
```
|
|
490
|
-
|
|
491
|
-
## Configuration File for Workers
|
|
492
|
-
|
|
493
|
-
For consistent worker configuration in production environments, create a JSON configuration file:
|
|
494
|
-
|
|
495
|
-
```json
|
|
496
|
-
{
|
|
497
|
-
"id": "worker-email-prod-1",
|
|
498
|
-
"queues": "emails,notifications,processing",
|
|
499
|
-
"redis": "default",
|
|
500
|
-
"concurrency": 3,
|
|
501
|
-
"maxRetries": 5,
|
|
502
|
-
"retryDelay": 10000,
|
|
503
|
-
"pauseOnError": false,
|
|
504
|
-
"keyPrefix": "myapp:jobs"
|
|
505
|
-
}
|
|
506
|
-
```
|
|
507
|
-
|
|
508
|
-
Then use it with the CLI:
|
|
509
|
-
|
|
510
|
-
```bash
|
|
511
|
-
bun heracles work --config=heracles.json
|
|
512
|
-
```
|
|
513
|
-
|
|
514
|
-
This approach ensures consistent configuration across all worker instances and simplifies deployment scripts.
|
|
515
|
-
|
|
516
|
-
### Performance Considerations
|
|
517
|
-
|
|
518
|
-
When running Heracles in production:
|
|
519
|
-
|
|
520
|
-
1. **Redis Connection Pool**: Configure appropriate connection pool size in your Redis client
|
|
521
|
-
2. **Key Expiration**: Set appropriate retention periods to avoid memory growth
|
|
522
|
-
3. **Stream Size**: Monitor stream sizes and adjust `maxStreamSize` accordingly
|
|
523
|
-
4. **Cleanup Schedule**: Run regular cleanup jobs during off-peak hours
|
|
524
|
-
5. **Concurrency Settings**: Balance worker concurrency based on job complexity and server resources
|
|
525
|
-
6. **Worker Distribution**: Distribute workers across multiple servers for high availability
|
|
526
|
-
|
|
527
|
-
By following these practices, you can build a robust, scalable job processing system with Heracles.
|
package/index.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025-present Workbud Technologies Inc. All rights reserved.
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
14
|
-
|
|
15
|
-
// Re-export all components from src/index.ts
|
|
16
|
-
export * from './src/index';
|
package/src/heracles.ts
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025-present Workbud Technologies Inc. All rights reserved.
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
14
|
-
|
|
15
|
-
import type { Job, JobClass } from './job';
|
|
16
|
-
import type { JobDispatchId, JobDispatchOptions } from './queue';
|
|
17
|
-
|
|
18
|
-
import { uid } from 'radash';
|
|
19
|
-
|
|
20
|
-
import { Queue } from './queue';
|
|
21
|
-
import { getJobMetadata } from './utils';
|
|
22
|
-
|
|
23
|
-
export namespace Heracles {
|
|
24
|
-
/**
|
|
25
|
-
* Dispatch a job to be executed asynchronously.
|
|
26
|
-
* @author Axel Nana <axel.nana@workbud.com>
|
|
27
|
-
* @param jobClass The job class to dispatch. Should extend the base {@link Job} class and have the {@link Job.register} decorator set.
|
|
28
|
-
* @param args The arguments to pass to the job constructor.
|
|
29
|
-
* @param options Additional options for the job dispatch.
|
|
30
|
-
* @returns The ID of the dispatched job.
|
|
31
|
-
*/
|
|
32
|
-
export const dispatch = async <T extends Job, TClass extends JobClass<T> = JobClass<T>>(
|
|
33
|
-
jobClass: TClass,
|
|
34
|
-
args: ConstructorParameters<TClass> = [] as any,
|
|
35
|
-
options: JobDispatchOptions = {}
|
|
36
|
-
): Promise<JobDispatchId> => {
|
|
37
|
-
// Check if job class is registered properly
|
|
38
|
-
const metadata = getJobMetadata(jobClass);
|
|
39
|
-
if (!metadata) {
|
|
40
|
-
throw new Error(`Job ${jobClass.name} is not marked with @Job.register() decorator`);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (!metadata.queue) {
|
|
44
|
-
throw new Error(`Job ${jobClass.name} is not registered with a queue`);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (!Queue.exists(metadata.queue)) {
|
|
48
|
-
throw new Error(
|
|
49
|
-
`Queue ${metadata.queue} does not exist. Please create the queue before dispatching the job.`
|
|
50
|
-
);
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const queue = Queue.get(metadata.queue);
|
|
54
|
-
|
|
55
|
-
// Generate a unique dispatch ID for this job dispatch
|
|
56
|
-
const dispatchId = `dispatch_${Date.now()}_${uid(8)}`;
|
|
57
|
-
|
|
58
|
-
// Include overlap behavior information in the options
|
|
59
|
-
const dispatchOptions = {
|
|
60
|
-
dispatchId,
|
|
61
|
-
overlapBehavior: metadata.overlapBehavior,
|
|
62
|
-
overlapDelay: metadata.overlapDelay,
|
|
63
|
-
...options
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
return queue.dispatch(jobClass, metadata.name, args, dispatchOptions);
|
|
67
|
-
};
|
|
68
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
// Copyright (c) 2025-present Workbud Technologies Inc. All rights reserved.
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
14
|
-
|
|
15
|
-
export type { JobProps } from './job';
|
|
16
|
-
export type { JobDispatchOptions, QueueOptions } from './queue';
|
|
17
|
-
export type { JobStatusInfo, Transport, TransportClass, TransportEvent } from './transport';
|
|
18
|
-
export type { QueuedJob, QueueState, Worker, WorkerInfo } from './worker';
|
|
19
|
-
export type { RedisTransportOptions } from './transports/redis.transport';
|
|
20
|
-
|
|
21
|
-
export { Heracles } from './heracles';
|
|
22
|
-
export { Job, JobStatus } from './job';
|
|
23
|
-
export { Queue } from './queue';
|
|
24
|
-
export { TransportMode } from './transport';
|
|
25
|
-
export { RedisTransport } from './transports/redis.transport';
|
|
26
|
-
export { WorkerStatus } from './worker';
|
|
27
|
-
export { BaseWorker } from './workers/base.worker';
|
|
28
|
-
export { RedisWorker } from './workers/redis.worker';
|