@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.
@@ -0,0 +1,251 @@
1
+ import type { JobStatusInfo, Transport, TransportEvent, TransportMode } from '../transport';
2
+ import { InteractsWithConsole } from '@elysiumjs/core';
3
+ /**
4
+ * Redis transport configuration options
5
+ * @author Axel Nana <axel.nana@workbud.com>
6
+ */
7
+ export type RedisTransportOptions = {
8
+ /**
9
+ * Redis connection name.
10
+ * If not provided, uses the default connection.
11
+ */
12
+ connection?: string;
13
+ /**
14
+ * Prefix for all Redis keys used by this transport.
15
+ * @default 'elysium:heracles'
16
+ */
17
+ keyPrefix?: string;
18
+ /**
19
+ * Group name for Redis consumer groups.
20
+ * @default 'workers'
21
+ */
22
+ consumerGroup?: string;
23
+ /**
24
+ * Consumer name for this instance in the consumer group.
25
+ * If not provided, a random name will be generated.
26
+ */
27
+ consumerName?: string;
28
+ /**
29
+ * How often to poll Redis for new messages (in milliseconds).
30
+ * @default 1000
31
+ */
32
+ pollInterval?: number;
33
+ /**
34
+ * Maximum number of messages to read in a single poll.
35
+ * @default 10
36
+ */
37
+ batchSize?: number;
38
+ /**
39
+ * Time to live for job status information (in seconds).
40
+ * @default 86400 (24 hours)
41
+ */
42
+ statusTTL?: number;
43
+ /**
44
+ * Whether to automatically clean up completed and failed jobs from streams.
45
+ * @default true
46
+ */
47
+ cleanupCompletedJobs?: boolean;
48
+ /**
49
+ * How long to retain completed jobs in the stream before cleanup (in seconds).
50
+ * @default 3600 (1 hour)
51
+ */
52
+ completedJobRetention?: number;
53
+ /**
54
+ * Maximum number of entries to keep in each stream.
55
+ * @default 1000
56
+ */
57
+ maxStreamSize?: number;
58
+ };
59
+ /**
60
+ * Transport implementation using Redis streams for distributed job processing.
61
+ * @author Axel Nana <axel.nana@workbud.com>
62
+ */
63
+ export declare class RedisTransport extends InteractsWithConsole implements Transport {
64
+ /**
65
+ * Redis client instance.
66
+ */
67
+ private client;
68
+ /**
69
+ * Options for this transport.
70
+ */
71
+ private options;
72
+ /**
73
+ * Mode this transport is operating in.
74
+ */
75
+ private mode;
76
+ /**
77
+ * Registered message handlers.
78
+ */
79
+ private messageHandlers;
80
+ /**
81
+ * Timer for polling Redis in consumer mode.
82
+ */
83
+ private pollTimer?;
84
+ /**
85
+ * Timer for stream cleanup.
86
+ */
87
+ private cleanupTimer?;
88
+ /**
89
+ * Last processed message IDs by stream.
90
+ */
91
+ private lastIds;
92
+ /**
93
+ * Whether to check the logs instead of new messages.
94
+ */
95
+ private checkLogs;
96
+ /**
97
+ * Consumer ID for this instance.
98
+ */
99
+ private consumerId;
100
+ /**
101
+ * Whether the consumer group has been created for each stream.
102
+ */
103
+ private consumerGroupCreated;
104
+ /**
105
+ * Map of message IDs to completed status for tracking completed jobs.
106
+ */
107
+ private completedMessageIds;
108
+ /**
109
+ * Map to track locks on job IDs for NO_OVERLAP jobs.
110
+ */
111
+ private jobLocks;
112
+ /**
113
+ * Create a new Redis transport.
114
+ */
115
+ constructor(mode: TransportMode, options?: RedisTransportOptions);
116
+ /**
117
+ * Helper method to execute Redis commands with proper type conversion
118
+ * @param command Redis command
119
+ * @param args Command arguments
120
+ * @returns Redis response
121
+ */
122
+ private redisCommand;
123
+ /**
124
+ * Finds job message ID in the stream by job ID
125
+ * @param jobId The job ID to find
126
+ * @param queueName The queue to search in
127
+ * @returns The message ID if found, null otherwise
128
+ */
129
+ private findJobMessageId;
130
+ /**
131
+ * Start the transport
132
+ */
133
+ start(): Promise<void>;
134
+ /**
135
+ * Stop the transport
136
+ */
137
+ stop(): Promise<void>;
138
+ /**
139
+ * Send a message through the transport
140
+ */
141
+ send(message: TransportEvent): Promise<void>;
142
+ /**
143
+ * Register a callback to handle messages
144
+ */
145
+ onMessage(handler: (message: TransportEvent) => void | Promise<void>): void;
146
+ /**
147
+ * Gets the current status of a job.
148
+ */
149
+ getJobStatus(jobId: string, dispatchId: string, queueName: string): Promise<JobStatusInfo>;
150
+ /**
151
+ * Updates a job's status directly in Redis without creating a new message.
152
+ *
153
+ * @param jobId The ID of the job to update
154
+ * @param queueName The queue the job belongs to
155
+ * @param updates The status updates to apply
156
+ */
157
+ updateJobStatus(jobId: string, dispatchId: string, queueName: string, updates: Partial<JobStatusInfo>): Promise<void>;
158
+ /**
159
+ * Register a worker with this transport
160
+ */
161
+ registerWorker(workerId: string, queues: string[]): Promise<void>;
162
+ /**
163
+ * Unregister a worker from this transport
164
+ */
165
+ unregisterWorker(workerId: string): Promise<void>;
166
+ /**
167
+ * Start polling Redis for messages
168
+ */
169
+ private startPolling;
170
+ /**
171
+ * Poll for new messages
172
+ */
173
+ private pollMessages;
174
+ /**
175
+ * Process messages from Redis stream
176
+ */
177
+ private processMessages;
178
+ /**
179
+ * Get all registered queues
180
+ */
181
+ private getRegisteredQueues;
182
+ /**
183
+ * Ensure a stream exists
184
+ */
185
+ private ensureStreamExists;
186
+ /**
187
+ * Ensure a consumer group exists for a stream
188
+ */
189
+ private ensureConsumerGroupExists;
190
+ /**
191
+ * Store job status in Redis
192
+ */
193
+ private storeJobStatus;
194
+ /**
195
+ * Serialize a message for Redis
196
+ */
197
+ private serializeMessage;
198
+ /**
199
+ * Deserialize a message from Redis
200
+ */
201
+ private deserializeMessage;
202
+ /**
203
+ * Get Redis key for a stream
204
+ */
205
+ private getStreamKey;
206
+ /**
207
+ * Get Redis key for job status
208
+ */
209
+ private getJobStatusKey;
210
+ /**
211
+ * Get Redis key for worker status
212
+ */
213
+ private getWorkerKey;
214
+ /**
215
+ * Track completed message for later cleanup
216
+ */
217
+ private trackCompletedMessage;
218
+ /**
219
+ * Schedule periodic stream cleanup
220
+ */
221
+ private scheduleStreamCleanup;
222
+ /**
223
+ * Find expired message IDs for cleanup
224
+ */
225
+ private cleanupStreams;
226
+ /**
227
+ * Trim all streams to the maximum configured size
228
+ */
229
+ private trimStreamsToMaxSize;
230
+ /**
231
+ * Check if a job with the same ID is currently locked
232
+ * @param jobId The job ID to check
233
+ * @param queue The queue name
234
+ * @returns True if the job is locked, false otherwise
235
+ */
236
+ isJobLocked(jobId: string, queue: string): Promise<boolean>;
237
+ /**
238
+ * Acquire a lock for a job ID
239
+ * @param jobId The job ID to lock
240
+ * @param queue The queue name
241
+ * @param duration Duration in milliseconds to lock the job
242
+ * @returns True if lock was acquired, false otherwise
243
+ */
244
+ acquireJobLock(jobId: string, queue: string, duration?: number): Promise<boolean>;
245
+ /**
246
+ * Release a lock for a job ID
247
+ * @param jobId The job ID to unlock
248
+ * @param queue The queue name
249
+ */
250
+ releaseJobLock(jobId: string, queue: string): Promise<void>;
251
+ }
@@ -0,0 +1,34 @@
1
+ import type { Job, JobClass, JobMetadata } from './job';
2
+ export declare namespace Symbols {
3
+ /**
4
+ * Symbol for job metadata
5
+ */
6
+ const job: unique symbol;
7
+ /**
8
+ * Symbol for queue metadata
9
+ */
10
+ const queue: unique symbol;
11
+ /**
12
+ * Symbol for worker metadata
13
+ */
14
+ const worker: unique symbol;
15
+ /**
16
+ * Symbol for transport metadata
17
+ */
18
+ const transport: unique symbol;
19
+ /**
20
+ * Symbol for job transaction data
21
+ */
22
+ const jobTransaction: unique symbol;
23
+ /**
24
+ * Symbol for worker pool metadata
25
+ */
26
+ const workerPool: unique symbol;
27
+ }
28
+ /**
29
+ * Get job metadata from a job class
30
+ * @author Axel Nana <axel.nana@workbud.com>
31
+ * @param jobClass The job class
32
+ * @returns The job metadata or null if not found
33
+ */
34
+ export declare const getJobMetadata: <T extends Job>(jobClass: JobClass<T>) => JobMetadata | null;
@@ -0,0 +1,250 @@
1
+ import type { Job, JobOverlapBehavior } from './job';
2
+ import type { QueueOptions } from './queue';
3
+ /**
4
+ * Represents a job in the queue with its metadata.
5
+ * @author Axel Nana <axel.nana@workbud.com>
6
+ */
7
+ export type QueuedJob = {
8
+ /**
9
+ * The job instance.
10
+ */
11
+ job: Job;
12
+ /**
13
+ * The number of times this job has been retried.
14
+ */
15
+ retries: number;
16
+ /**
17
+ * The queue this job belongs to.
18
+ */
19
+ queueName: string;
20
+ /**
21
+ * When the job was added to the queue.
22
+ */
23
+ enqueuedAt: Date;
24
+ /**
25
+ * When the job is scheduled to be processed.
26
+ * This is used for delayed jobs or retry scheduling.
27
+ */
28
+ scheduledFor?: Date;
29
+ /**
30
+ * Priority of this job (lower number = higher priority).
31
+ * @see JobDispatchOptions.priority
32
+ */
33
+ priority?: number;
34
+ /**
35
+ * Maximum number of retries for this specific job.
36
+ * Overrides queue-level setting if specified.
37
+ * @see JobDispatchOptions.maxRetries
38
+ */
39
+ maxRetries?: number;
40
+ /**
41
+ * Delay between retries in milliseconds for this specific job.
42
+ * Overrides queue-level setting if specified.
43
+ * @see JobDispatchOptions.retryDelay
44
+ */
45
+ retryDelay?: number;
46
+ /**
47
+ * Defines how the job handles overlap with other instances of the same job ID.
48
+ * @see JobOverlapBehavior
49
+ */
50
+ overlapBehavior?: JobOverlapBehavior;
51
+ /**
52
+ * When using NO_OVERLAP behavior, specifies the delay in milliseconds
53
+ * between sequential executions of jobs with the same ID.
54
+ */
55
+ overlapDelay?: number;
56
+ };
57
+ /**
58
+ * Options for worker queues.
59
+ * @author Axel Nana <axel.nana@workbud.com>
60
+ */
61
+ export type WorkerQueueOptions = Omit<QueueOptions, 'transport'>;
62
+ /**
63
+ * State associated with a job queue managed by a worker.
64
+ * @author Axel Nana <axel.nana@workbud.com>
65
+ */
66
+ export type QueueState = {
67
+ /**
68
+ * The queue configuration options.
69
+ */
70
+ options: WorkerQueueOptions;
71
+ /**
72
+ * The jobs waiting to be processed.
73
+ */
74
+ jobs: QueuedJob[];
75
+ /**
76
+ * The jobs currently being processed.
77
+ */
78
+ activeJobs: Map<string, QueuedJob>;
79
+ /**
80
+ * The number of jobs currently being processed.
81
+ */
82
+ processing: number;
83
+ /**
84
+ * Whether the queue is currently paused.
85
+ */
86
+ paused: boolean;
87
+ /**
88
+ * Whether the queue is currently being drained (completing existing jobs but not accepting new ones).
89
+ */
90
+ draining: boolean;
91
+ };
92
+ /**
93
+ * Worker status information.
94
+ * @author Axel Nana <axel.nana@workbud.com>
95
+ */
96
+ export declare enum WorkerStatus {
97
+ /**
98
+ * Worker is idle - no jobs are being processed.
99
+ */
100
+ IDLE = "idle",
101
+ /**
102
+ * Worker is active - processing jobs.
103
+ */
104
+ ACTIVE = "active",
105
+ /**
106
+ * Worker is paused - not accepting new jobs.
107
+ */
108
+ PAUSED = "paused",
109
+ /**
110
+ * Worker is draining - completing current jobs but not accepting new ones.
111
+ */
112
+ DRAINING = "draining",
113
+ /**
114
+ * Worker is stopping - gracefully shutting down.
115
+ */
116
+ STOPPING = "stopping",
117
+ /**
118
+ * Worker is stopped - no longer processing jobs.
119
+ */
120
+ STOPPED = "stopped",
121
+ /**
122
+ * Worker is disconnected - lost connection.
123
+ */
124
+ DISCONNECTED = "disconnected"
125
+ }
126
+ /**
127
+ * Worker information used by the worker pool.
128
+ * @author Axel Nana <axel.nana@workbud.com>
129
+ */
130
+ export type WorkerInfo = {
131
+ /**
132
+ * The worker ID.
133
+ */
134
+ id: string;
135
+ /**
136
+ * The worker status.
137
+ */
138
+ status: WorkerStatus;
139
+ /**
140
+ * The queues handled by this worker.
141
+ */
142
+ queues: string[];
143
+ /**
144
+ * The jobs being processed by this worker.
145
+ */
146
+ activeJobs: number;
147
+ /**
148
+ * The transport used by this worker.
149
+ */
150
+ transport: string;
151
+ /**
152
+ * Start time of this worker.
153
+ */
154
+ startedAt: Date;
155
+ /**
156
+ * Add a job to this worker.
157
+ */
158
+ addJob(job: Job, queueName?: string): Promise<boolean>;
159
+ /**
160
+ * Cancel a job on this worker.
161
+ */
162
+ cancelJob(jobId: string, queueName?: string): Promise<boolean>;
163
+ /**
164
+ * Cancel all jobs on this worker.
165
+ */
166
+ cancelAllJobs(queueName?: string): Promise<number>;
167
+ };
168
+ /**
169
+ * Standardized interface for a worker managing job queues.
170
+ * @author Axel Nana <axel.nana@workbud.com>
171
+ */
172
+ export interface Worker {
173
+ /**
174
+ * The worker ID.
175
+ */
176
+ id: string;
177
+ /**
178
+ * The worker status.
179
+ */
180
+ status: WorkerStatus;
181
+ /**
182
+ * Create a new queue with the specified options.
183
+ */
184
+ createQueue(options: QueueOptions): Promise<Worker>;
185
+ /**
186
+ * Add a job to a queue.
187
+ */
188
+ addJob(job: Job, queueName?: string): Promise<Worker>;
189
+ /**
190
+ * Pause a queue to stop processing new jobs.
191
+ */
192
+ pause(queueName?: string): Promise<Worker>;
193
+ /**
194
+ * Resume a paused queue.
195
+ */
196
+ resume(queueName?: string): Promise<Worker>;
197
+ /**
198
+ * Drain a queue - complete current jobs but don't accept new ones.
199
+ */
200
+ drain(queueName?: string): Promise<Worker>;
201
+ /**
202
+ * Clear all jobs from a queue.
203
+ */
204
+ clear(queueName?: string): Promise<Worker>;
205
+ /**
206
+ * Get the number of jobs in a queue.
207
+ */
208
+ size(queueName?: string): Promise<number>;
209
+ /**
210
+ * Get the total number of jobs across all queues.
211
+ */
212
+ totalSize(): Promise<number>;
213
+ /**
214
+ * Get statistics about all queues.
215
+ */
216
+ getStats(): Promise<Record<string, {
217
+ waiting: number;
218
+ processing: number;
219
+ paused: boolean;
220
+ draining: boolean;
221
+ }>>;
222
+ /**
223
+ * Get information about this worker for the worker pool.
224
+ */
225
+ getInfo(): WorkerInfo;
226
+ /**
227
+ * Cancel a job.
228
+ */
229
+ cancelJob(jobId: string, queueName?: string): Promise<boolean>;
230
+ /**
231
+ * Cancel all jobs.
232
+ */
233
+ cancelAllJobs(queueName?: string): Promise<number>;
234
+ /**
235
+ * Get the job with the given ID.
236
+ */
237
+ getJob(jobId: string, queueName?: string): Promise<Job | null>;
238
+ /**
239
+ * Start the worker.
240
+ */
241
+ start(): Promise<void>;
242
+ /**
243
+ * Stop the worker.
244
+ */
245
+ stop(force?: boolean): Promise<void>;
246
+ /**
247
+ * Set the concurrency for a queue.
248
+ */
249
+ setConcurrency(queueName: string, concurrency: number): Promise<void>;
250
+ }