@gravito/stream 2.0.1 → 2.1.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 +127 -285
- package/README.zh-TW.md +146 -13
- package/dist/BatchConsumer.d.ts +81 -0
- package/dist/Consumer.d.ts +215 -0
- package/dist/DashboardProvider.d.ts +20 -0
- package/dist/Job.d.ts +183 -0
- package/dist/OrbitStream.d.ts +151 -0
- package/dist/QueueManager.d.ts +319 -0
- package/dist/Queueable.d.ts +91 -0
- package/dist/Scheduler.d.ts +214 -0
- package/dist/StreamEventBackend.d.ts +114 -0
- package/dist/SystemEventJob.d.ts +33 -0
- package/dist/Worker.d.ts +139 -0
- package/dist/benchmarks/PerformanceReporter.d.ts +99 -0
- package/dist/consumer/ConcurrencyGate.d.ts +55 -0
- package/dist/consumer/ConsumerStrategy.d.ts +41 -0
- package/dist/consumer/GroupSequencer.d.ts +57 -0
- package/dist/consumer/HeartbeatManager.d.ts +65 -0
- package/dist/consumer/JobExecutor.d.ts +61 -0
- package/dist/consumer/JobSourceGenerator.d.ts +31 -0
- package/dist/consumer/PollingStrategy.d.ts +42 -0
- package/dist/consumer/ReactiveStrategy.d.ts +41 -0
- package/dist/consumer/StreamingConsumer.d.ts +88 -0
- package/dist/consumer/index.d.ts +13 -0
- package/dist/consumer/types.d.ts +102 -0
- package/dist/drivers/BinaryJobFrame.d.ts +78 -0
- package/dist/drivers/BullMQDriver.d.ts +186 -0
- package/dist/drivers/DatabaseDriver.d.ts +131 -0
- package/dist/drivers/GrpcDriver.d.ts +16 -0
- package/dist/drivers/KafkaDriver.d.ts +148 -0
- package/dist/drivers/MemoryDriver.d.ts +108 -0
- package/dist/drivers/QueueDriver.d.ts +250 -0
- package/dist/drivers/RabbitMQDriver.d.ts +102 -0
- package/dist/drivers/RedisDriver.d.ts +294 -0
- package/dist/drivers/SQSDriver.d.ts +111 -0
- package/dist/drivers/kafka/BackpressureController.d.ts +60 -0
- package/dist/drivers/kafka/BatchProcessor.d.ts +50 -0
- package/dist/drivers/kafka/ConsumerLifecycleManager.d.ts +80 -0
- package/dist/drivers/kafka/ErrorCategorizer.d.ts +39 -0
- package/dist/drivers/kafka/ErrorRecoveryManager.d.ts +100 -0
- package/dist/drivers/kafka/HeartbeatManager.d.ts +57 -0
- package/dist/drivers/kafka/KafkaDriver.d.ts +138 -0
- package/dist/drivers/kafka/KafkaMetrics.d.ts +88 -0
- package/dist/drivers/kafka/KafkaNotifier.d.ts +54 -0
- package/dist/drivers/kafka/MessageBuffer.d.ts +71 -0
- package/dist/drivers/kafka/OffsetTracker.d.ts +63 -0
- package/dist/drivers/kafka/PerformanceMonitor.d.ts +88 -0
- package/dist/drivers/kafka/RateLimiter.d.ts +52 -0
- package/dist/drivers/kafka/RebalanceHandler.d.ts +104 -0
- package/dist/drivers/kafka/RingBuffer.d.ts +63 -0
- package/dist/drivers/kafka/index.d.ts +22 -0
- package/dist/drivers/kafka/types.d.ts +553 -0
- package/dist/drivers/prepareJobForTransport.d.ts +10 -0
- package/dist/index.cjs +6274 -3777
- package/dist/index.cjs.map +71 -0
- package/dist/index.d.ts +60 -2233
- package/dist/index.js +6955 -4446
- package/dist/index.js.map +71 -0
- package/dist/locks/DistributedLock.d.ts +175 -0
- package/dist/persistence/BufferedPersistence.d.ts +130 -0
- package/dist/persistence/BunBufferedPersistence.d.ts +173 -0
- package/dist/persistence/MySQLPersistence.d.ts +134 -0
- package/dist/persistence/SQLitePersistence.d.ts +133 -0
- package/dist/serializers/BinarySerializer.d.ts +42 -0
- package/dist/serializers/CachedSerializer.d.ts +38 -0
- package/dist/serializers/CborNativeSerializer.d.ts +56 -0
- package/dist/serializers/ClassNameSerializer.d.ts +58 -0
- package/dist/serializers/JobSerializer.d.ts +33 -0
- package/dist/serializers/JsonSerializer.d.ts +28 -0
- package/dist/serializers/JsonlSerializer.d.ts +90 -0
- package/dist/serializers/MessagePackSerializer.d.ts +29 -0
- package/dist/types.d.ts +653 -0
- package/dist/workers/BinaryWorkerProtocol.d.ts +77 -0
- package/dist/workers/BunWorker.d.ts +179 -0
- package/dist/workers/SandboxedWorker.d.ts +132 -0
- package/dist/workers/WorkerFactory.d.ts +128 -0
- package/dist/workers/WorkerPool.d.ts +186 -0
- package/dist/workers/bun-job-executor.d.ts +14 -0
- package/dist/workers/index.d.ts +13 -0
- package/dist/workers/job-executor.d.ts +9 -0
- package/package.json +13 -6
- package/proto/queue.proto +101 -0
- package/dist/index.d.cts +0 -2242
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import type { JobPushOptions, QueueStats, SerializedJob } from '../types';
|
|
2
|
+
import type { QueueDriver } from './QueueDriver';
|
|
3
|
+
/**
|
|
4
|
+
* Interface for Redis clients (compatible with ioredis and node-redis).
|
|
5
|
+
*/
|
|
6
|
+
export interface RedisClient {
|
|
7
|
+
lpush(key: string, ...values: string[]): Promise<number>;
|
|
8
|
+
rpop(key: string, count?: number): Promise<string | string[] | null>;
|
|
9
|
+
llen(key: string): Promise<number>;
|
|
10
|
+
del(key: string, ...keys: string[]): Promise<number>;
|
|
11
|
+
lpushx?(key: string, ...values: string[]): Promise<number>;
|
|
12
|
+
rpoplpush?(src: string, dst: string): Promise<string | null>;
|
|
13
|
+
zadd?(key: string, score: number, member: string): Promise<number>;
|
|
14
|
+
zrange?(key: string, start: number, end: number, ...args: string[]): Promise<string[]>;
|
|
15
|
+
zrem?(key: string, ...members: string[]): Promise<number>;
|
|
16
|
+
get?(key: string): Promise<string | null>;
|
|
17
|
+
set?(key: string, value: string, ...args: any[]): Promise<'OK' | null>;
|
|
18
|
+
ltrim?(key: string, start: number, stop: number): Promise<'OK'>;
|
|
19
|
+
lrange?(key: string, start: number, stop: number): Promise<string[]>;
|
|
20
|
+
publish?(channel: string, message: string): Promise<number>;
|
|
21
|
+
pipeline?(): any;
|
|
22
|
+
defineCommand?(name: string, options: {
|
|
23
|
+
numberOfKeys: number;
|
|
24
|
+
lua: string;
|
|
25
|
+
}): void;
|
|
26
|
+
incr?(key: string): Promise<number>;
|
|
27
|
+
expire?(key: string, seconds: number): Promise<number>;
|
|
28
|
+
eval(script: string, numKeys: number, ...args: (string | number)[]): Promise<any>;
|
|
29
|
+
/**
|
|
30
|
+
* Binary-capable RPOP(ioredis Buffer mode)
|
|
31
|
+
* 回傳 Buffer 而非 string,支援 binary frame 的零拷貝讀取
|
|
32
|
+
*/
|
|
33
|
+
rpopBuffer?(key: string, count?: number): Promise<Buffer | Buffer[] | null>;
|
|
34
|
+
/**
|
|
35
|
+
* Binary-capable BRPOP(ioredis Buffer mode)
|
|
36
|
+
* 支援 blocking pop 的 binary frame 讀取
|
|
37
|
+
*/
|
|
38
|
+
brpopBuffer?(...args: any[]): Promise<[Buffer, Buffer] | null>;
|
|
39
|
+
/**
|
|
40
|
+
* Binary-capable LRANGE(ioredis Buffer mode)
|
|
41
|
+
* 用於從 DLQ 讀取 binary frame
|
|
42
|
+
*/
|
|
43
|
+
lrangeBuffer?(key: string, start: number, stop: number): Promise<Buffer[]>;
|
|
44
|
+
/**
|
|
45
|
+
* Binary-capable LPUSH(接受 Buffer)
|
|
46
|
+
* 用於將 binary frame 寫入 Redis list
|
|
47
|
+
*/
|
|
48
|
+
lpushBuffer?(key: string, ...values: Buffer[]): Promise<number>;
|
|
49
|
+
[key: string]: any;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Extended Redis client with custom commands.
|
|
53
|
+
*/
|
|
54
|
+
export interface CustomRedisClient extends RedisClient {
|
|
55
|
+
pushGroupJob(waitList: string, activeSet: string, pendingList: string, groupId: string, payload: string): Promise<number>;
|
|
56
|
+
completeGroupJob(waitList: string, activeSet: string, pendingList: string, groupId: string): Promise<number>;
|
|
57
|
+
popMany(queue: string, prefix: string, count: number, now: string): Promise<string[]>;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Extended Redis client with custom group commands (Legacy name).
|
|
61
|
+
*/
|
|
62
|
+
export type GroupRedisClient = CustomRedisClient;
|
|
63
|
+
/**
|
|
64
|
+
* Redis driver configuration.
|
|
65
|
+
*/
|
|
66
|
+
export interface RedisDriverConfig {
|
|
67
|
+
/**
|
|
68
|
+
* Redis client instance (ioredis or node-redis).
|
|
69
|
+
*/
|
|
70
|
+
client: RedisClient;
|
|
71
|
+
/**
|
|
72
|
+
* Key prefix (default: `queue:`).
|
|
73
|
+
*/
|
|
74
|
+
prefix?: string;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* High-performance Redis queue driver.
|
|
78
|
+
*
|
|
79
|
+
* Implements FIFO queues using Redis Lists, reliable priority support, delayed jobs via Sorted Sets,
|
|
80
|
+
* and rate limiting. Uses Lua scripts for atomic operations and advanced features like
|
|
81
|
+
* group-based sequential processing.
|
|
82
|
+
*
|
|
83
|
+
* 支援 binary frame 格式:
|
|
84
|
+
* - push/pushMany:type === 'binary' 且 data instanceof Uint8Array 時,使用 BinaryJobFrame 格式
|
|
85
|
+
* - pop/popMany:透過 magic byte 嗅探自動偵測格式(binary frame 或 JSON)
|
|
86
|
+
*
|
|
87
|
+
* @public
|
|
88
|
+
* @example
|
|
89
|
+
* ```typescript
|
|
90
|
+
* import Redis from 'ioredis';
|
|
91
|
+
* const redis = new Redis();
|
|
92
|
+
* const driver = new RedisDriver({ client: redis });
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export declare class RedisDriver implements QueueDriver {
|
|
96
|
+
private prefix;
|
|
97
|
+
private client;
|
|
98
|
+
private static PUSH_SCRIPT;
|
|
99
|
+
private static COMPLETE_SCRIPT;
|
|
100
|
+
private static POP_MANY_SCRIPT;
|
|
101
|
+
constructor(config: RedisDriverConfig);
|
|
102
|
+
/**
|
|
103
|
+
* Get full Redis key for a queue.
|
|
104
|
+
*/
|
|
105
|
+
private getKey;
|
|
106
|
+
/**
|
|
107
|
+
* 判斷 job 是否應使用 binary frame 格式傳輸
|
|
108
|
+
*
|
|
109
|
+
* 當 type === 'binary' 且 data 為 Uint8Array 時啟用 binary path
|
|
110
|
+
*/
|
|
111
|
+
private isBinaryJob;
|
|
112
|
+
/**
|
|
113
|
+
* 序列化 Job 為適合 Redis 儲存的格式
|
|
114
|
+
*
|
|
115
|
+
* Binary path:使用 BinaryJobFrame 格式(Uint8Array → Buffer)
|
|
116
|
+
* Legacy path:使用 JSON.stringify(string)
|
|
117
|
+
*
|
|
118
|
+
* @returns Buffer(binary path)或 string(legacy path)
|
|
119
|
+
*/
|
|
120
|
+
private serializeJobForTransport;
|
|
121
|
+
/**
|
|
122
|
+
* 自動偵測並解析 Redis payload 格式
|
|
123
|
+
*
|
|
124
|
+
* 1. 若輸入為 Buffer/Uint8Array 且以 Magic bytes 開頭 → 使用 BinaryJobFrame 解碼
|
|
125
|
+
* 2. 否則 → 使用 JSON.parse(legacy path)
|
|
126
|
+
*
|
|
127
|
+
* @param payload - Redis 回傳的資料(string 或 Buffer)
|
|
128
|
+
* @returns 解析後的 SerializedJob
|
|
129
|
+
*/
|
|
130
|
+
private parsePayloadAuto;
|
|
131
|
+
/**
|
|
132
|
+
* 解析 JSON 格式的 payload(legacy path)
|
|
133
|
+
*/
|
|
134
|
+
private parseJsonPayload;
|
|
135
|
+
/**
|
|
136
|
+
* Pushes a job to Redis.
|
|
137
|
+
*
|
|
138
|
+
* 支援 binary path 和 legacy path:
|
|
139
|
+
* - Binary path:job.type === 'binary' 且 data instanceof Uint8Array → 使用 BinaryJobFrame
|
|
140
|
+
* - Legacy path:其他格式 → 使用 JSON.stringify
|
|
141
|
+
*
|
|
142
|
+
* @param queue - The queue name.
|
|
143
|
+
* @param job - The serialized job.
|
|
144
|
+
* @param options - Push options.
|
|
145
|
+
*/
|
|
146
|
+
push(queue: string, job: SerializedJob, options?: JobPushOptions): Promise<void>;
|
|
147
|
+
/**
|
|
148
|
+
* Completes a job.
|
|
149
|
+
*
|
|
150
|
+
* Crucial for Group FIFO logic to unlock the next job in the group.
|
|
151
|
+
*
|
|
152
|
+
* @param queue - The queue name.
|
|
153
|
+
* @param job - The job to complete.
|
|
154
|
+
*/
|
|
155
|
+
complete(queue: string, job: SerializedJob): Promise<void>;
|
|
156
|
+
/**
|
|
157
|
+
* Pops a job from the queue.
|
|
158
|
+
*
|
|
159
|
+
* 支援 binary frame 自動偵測:
|
|
160
|
+
* - 若 client 支援 rpopBuffer → 嘗試讀取 Buffer 並用 magic byte 判斷格式
|
|
161
|
+
* - 否則降級為 string rpop,使用 parsePayloadAuto 解析
|
|
162
|
+
*
|
|
163
|
+
* @param queue - The queue name.
|
|
164
|
+
* @returns The job or `null`.
|
|
165
|
+
*/
|
|
166
|
+
pop(queue: string): Promise<SerializedJob | null>;
|
|
167
|
+
/**
|
|
168
|
+
* Manual fallback for pop if Lua fails.
|
|
169
|
+
*
|
|
170
|
+
* 優先使用 rpopBuffer 取得 binary frame,否則降級為 string
|
|
171
|
+
*/
|
|
172
|
+
private popManualFallback;
|
|
173
|
+
/**
|
|
174
|
+
* Pops a job using blocking Redis commands (BRPOP).
|
|
175
|
+
*
|
|
176
|
+
* 支援 brpopBuffer 取得 binary frame,否則降級為 string
|
|
177
|
+
*
|
|
178
|
+
* @param queues - The queues to listen to.
|
|
179
|
+
* @param timeout - Timeout in seconds.
|
|
180
|
+
*/
|
|
181
|
+
popBlocking(queues: string | string[], timeout: number): Promise<SerializedJob | null>;
|
|
182
|
+
/**
|
|
183
|
+
* Returns the length of the queue (Redis List length).
|
|
184
|
+
*
|
|
185
|
+
* @param queue - The queue name.
|
|
186
|
+
*/
|
|
187
|
+
size(queue: string): Promise<number>;
|
|
188
|
+
/**
|
|
189
|
+
* Marks a job as permanently failed by moving it to a DLQ list.
|
|
190
|
+
*
|
|
191
|
+
* 支援 binary path:binary job 使用 BinaryJobFrame 格式存入 DLQ
|
|
192
|
+
*
|
|
193
|
+
* @param queue - The queue name.
|
|
194
|
+
* @param job - The failed job.
|
|
195
|
+
*/
|
|
196
|
+
fail(queue: string, job: SerializedJob): Promise<void>;
|
|
197
|
+
/**
|
|
198
|
+
* Clears the queue and its associated delayed/active sets.
|
|
199
|
+
*
|
|
200
|
+
* @param queue - The queue name.
|
|
201
|
+
*/
|
|
202
|
+
clear(queue: string): Promise<void>;
|
|
203
|
+
/**
|
|
204
|
+
* Retrieves full stats for the queue using Redis Pipelining.
|
|
205
|
+
*
|
|
206
|
+
* @param queue - The queue name.
|
|
207
|
+
*/
|
|
208
|
+
stats(queue: string): Promise<QueueStats>;
|
|
209
|
+
/**
|
|
210
|
+
* Pushes multiple jobs to the queue.
|
|
211
|
+
*
|
|
212
|
+
* 批次 push 支援 binary path 和 legacy path 混合使用。
|
|
213
|
+
* 對於含有 binary job 的批次,透過 pipeline 分批處理。
|
|
214
|
+
*
|
|
215
|
+
* @param queue - The queue name.
|
|
216
|
+
* @param jobs - Array of jobs.
|
|
217
|
+
*/
|
|
218
|
+
pushMany(queue: string, jobs: SerializedJob[]): Promise<void>;
|
|
219
|
+
/**
|
|
220
|
+
* Pops multiple jobs from the queue.
|
|
221
|
+
*
|
|
222
|
+
* 支援 binary frame 自動偵測格式。
|
|
223
|
+
*
|
|
224
|
+
* @param queue - The queue name.
|
|
225
|
+
* @param count - Max jobs to pop.
|
|
226
|
+
*/
|
|
227
|
+
popMany(queue: string, count: number): Promise<SerializedJob[]>;
|
|
228
|
+
/**
|
|
229
|
+
* Reports a worker heartbeat.
|
|
230
|
+
*/
|
|
231
|
+
reportHeartbeat(workerInfo: any, prefix?: string): Promise<void>;
|
|
232
|
+
/**
|
|
233
|
+
* Publishes monitoring logs.
|
|
234
|
+
*/
|
|
235
|
+
publishLog(logPayload: any, prefix?: string): Promise<void>;
|
|
236
|
+
/**
|
|
237
|
+
* Checks the rate limit for a queue.
|
|
238
|
+
*/
|
|
239
|
+
checkRateLimit(queue: string, config: {
|
|
240
|
+
max: number;
|
|
241
|
+
duration: number;
|
|
242
|
+
}): Promise<boolean>;
|
|
243
|
+
/**
|
|
244
|
+
* Retrieves failed jobs from the DLQ.
|
|
245
|
+
*
|
|
246
|
+
* 支援 lrangeBuffer 讀取 binary frame,否則降級為 string lrange
|
|
247
|
+
*
|
|
248
|
+
* @param queue - The queue name.
|
|
249
|
+
* @param start - Start index.
|
|
250
|
+
* @param end - End index.
|
|
251
|
+
*/
|
|
252
|
+
getFailed(queue: string, start?: number, end?: number): Promise<SerializedJob[]>;
|
|
253
|
+
/**
|
|
254
|
+
* Retries failed jobs.
|
|
255
|
+
*
|
|
256
|
+
* @param queue - The queue name.
|
|
257
|
+
* @param count - Jobs to retry.
|
|
258
|
+
*/
|
|
259
|
+
retryFailed(queue: string, count?: number): Promise<number>;
|
|
260
|
+
/**
|
|
261
|
+
* Clears the Dead Letter Queue.
|
|
262
|
+
*
|
|
263
|
+
* @param queue - The queue name.
|
|
264
|
+
*/
|
|
265
|
+
clearFailed(queue: string): Promise<void>;
|
|
266
|
+
/**
|
|
267
|
+
* Retrieves all discovered queue names from Redis.
|
|
268
|
+
*/
|
|
269
|
+
getQueues(): Promise<string[]>;
|
|
270
|
+
/**
|
|
271
|
+
* Enables real-time notifications for job arrivals via Redis Pub/Sub.
|
|
272
|
+
*
|
|
273
|
+
* Sets up the pub/sub connection and prepares for notification callbacks.
|
|
274
|
+
*
|
|
275
|
+
* @throws {Error} If the Redis client doesn't support pub/sub operations.
|
|
276
|
+
*/
|
|
277
|
+
enableNotifications(): Promise<void>;
|
|
278
|
+
/**
|
|
279
|
+
* Disables real-time notifications for job arrivals.
|
|
280
|
+
*
|
|
281
|
+
* Stops listening to notification channels.
|
|
282
|
+
*/
|
|
283
|
+
disableNotifications(): Promise<void>;
|
|
284
|
+
/**
|
|
285
|
+
* Registers a notification listener for one or more queues.
|
|
286
|
+
*
|
|
287
|
+
* Uses Redis Pub/Sub to listen for job arrivals. When a job is pushed,
|
|
288
|
+
* the driver publishes a notification that triggers the callback.
|
|
289
|
+
*
|
|
290
|
+
* @param queues - Queue name(s) to listen for.
|
|
291
|
+
* @param callback - Function called with queue name when a job arrives.
|
|
292
|
+
*/
|
|
293
|
+
onNotify(queues: string | string[], callback: (queue: string) => Promise<void>): Promise<void>;
|
|
294
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { SerializedJob } from '../types';
|
|
2
|
+
import type { QueueDriver } from './QueueDriver';
|
|
3
|
+
/**
|
|
4
|
+
* SQS driver configuration.
|
|
5
|
+
*/
|
|
6
|
+
export interface SQSDriverConfig {
|
|
7
|
+
/**
|
|
8
|
+
* SQS client instance (`@aws-sdk/client-sqs`).
|
|
9
|
+
*/
|
|
10
|
+
client: {
|
|
11
|
+
send: (command: unknown) => Promise<{
|
|
12
|
+
MessageId?: string;
|
|
13
|
+
Messages?: Array<{
|
|
14
|
+
MessageId?: string;
|
|
15
|
+
ReceiptHandle?: string;
|
|
16
|
+
Body?: string;
|
|
17
|
+
}>;
|
|
18
|
+
}>;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Queue URL prefix (used to build full queue URLs).
|
|
22
|
+
*/
|
|
23
|
+
queueUrlPrefix?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Visibility timeout (seconds, default: 30).
|
|
26
|
+
*/
|
|
27
|
+
visibilityTimeout?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Long-polling duration (seconds, default: 20).
|
|
30
|
+
*/
|
|
31
|
+
waitTimeSeconds?: number;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Amazon SQS queue driver.
|
|
35
|
+
*
|
|
36
|
+
* Wraps the AWS SDK for SQS. Supports standard and FIFO queues, long polling,
|
|
37
|
+
* and visibility timeouts.
|
|
38
|
+
*
|
|
39
|
+
* @public
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* import { SQSClient } from '@aws-sdk/client-sqs';
|
|
43
|
+
* const sqs = new SQSClient({ region: 'us-east-1' });
|
|
44
|
+
* const driver = new SQSDriver({ client: sqs });
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare class SQSDriver implements QueueDriver {
|
|
48
|
+
private client;
|
|
49
|
+
private queueUrlPrefix;
|
|
50
|
+
private visibilityTimeout;
|
|
51
|
+
private waitTimeSeconds;
|
|
52
|
+
private queueUrls;
|
|
53
|
+
constructor(config: SQSDriverConfig);
|
|
54
|
+
/**
|
|
55
|
+
* Resolve the full queue URL.
|
|
56
|
+
*/
|
|
57
|
+
private getQueueUrl;
|
|
58
|
+
/**
|
|
59
|
+
* Pushes a job to SQS.
|
|
60
|
+
*
|
|
61
|
+
* @param queue - The queue name (or URL).
|
|
62
|
+
* @param job - The serialized job.
|
|
63
|
+
*/
|
|
64
|
+
push(queue: string, job: SerializedJob): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Pops a job from SQS (using long polling).
|
|
67
|
+
*
|
|
68
|
+
* @param queue - The queue name (or URL).
|
|
69
|
+
*/
|
|
70
|
+
pop(queue: string): Promise<SerializedJob | null>;
|
|
71
|
+
/**
|
|
72
|
+
* Pops multiple jobs (up to 10).
|
|
73
|
+
*
|
|
74
|
+
* @param queue - The queue name.
|
|
75
|
+
* @param count - Max jobs (capped at 10 by SQS).
|
|
76
|
+
*/
|
|
77
|
+
popMany(queue: string, count: number): Promise<SerializedJob[]>;
|
|
78
|
+
/**
|
|
79
|
+
* Returns the approximate number of messages in the queue.
|
|
80
|
+
*
|
|
81
|
+
* @param queue - The queue name.
|
|
82
|
+
*/
|
|
83
|
+
size(queue: string): Promise<number>;
|
|
84
|
+
/**
|
|
85
|
+
* Clears the queue by continuously receiving and deleting messages.
|
|
86
|
+
*
|
|
87
|
+
* SQS does not have a "purge" command in the client data plane easily accessible here,
|
|
88
|
+
* so we drain the queue.
|
|
89
|
+
*
|
|
90
|
+
* @param queue - The queue name.
|
|
91
|
+
*/
|
|
92
|
+
clear(queue: string): Promise<void>;
|
|
93
|
+
/**
|
|
94
|
+
* Pushes multiple jobs using SQS batch API.
|
|
95
|
+
*
|
|
96
|
+
* @param queue - The queue name.
|
|
97
|
+
* @param jobs - Array of jobs.
|
|
98
|
+
*/
|
|
99
|
+
pushMany(queue: string, jobs: SerializedJob[]): Promise<void>;
|
|
100
|
+
/**
|
|
101
|
+
* Throws error as SQS requires ReceiptHandle, not just MessageId.
|
|
102
|
+
*/
|
|
103
|
+
acknowledge(_messageId: string): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Deletes a message using its ReceiptHandle (ACK).
|
|
106
|
+
*
|
|
107
|
+
* @param queue - The queue name.
|
|
108
|
+
* @param receiptHandle - The SQS receipt handle.
|
|
109
|
+
*/
|
|
110
|
+
deleteMessage(queue: string, receiptHandle: string): Promise<void>;
|
|
111
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { BackpressureConfig } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Monitors buffer watermarks and signals pause/resume to the Kafka consumer.
|
|
4
|
+
*
|
|
5
|
+
* Decoupled from KafkaDriver to enable independent testing and reuse.
|
|
6
|
+
* Uses callback pattern for minimal overhead.
|
|
7
|
+
*
|
|
8
|
+
* @public
|
|
9
|
+
*/
|
|
10
|
+
export declare class BackpressureController {
|
|
11
|
+
private readonly bufferSize;
|
|
12
|
+
private readonly highWatermark;
|
|
13
|
+
private readonly lowWatermark;
|
|
14
|
+
private readonly maxInFlight;
|
|
15
|
+
private paused;
|
|
16
|
+
private inFlightCount;
|
|
17
|
+
private onPause;
|
|
18
|
+
private onResume;
|
|
19
|
+
constructor(bufferSize: number, config?: BackpressureConfig);
|
|
20
|
+
/**
|
|
21
|
+
* Register pause/resume callbacks.
|
|
22
|
+
*/
|
|
23
|
+
onBackpressure(callbacks: {
|
|
24
|
+
pause: (topics: string[]) => void;
|
|
25
|
+
resume: (topics: string[]) => void;
|
|
26
|
+
}): void;
|
|
27
|
+
/**
|
|
28
|
+
* Check if a topic should be paused based on buffer level.
|
|
29
|
+
*/
|
|
30
|
+
shouldPause(bufferLength: number): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Check if a topic should be resumed based on buffer level.
|
|
33
|
+
*/
|
|
34
|
+
shouldResume(bufferLength: number): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Acquire a slot for in-flight callback processing.
|
|
37
|
+
* Returns false if max in-flight limit reached.
|
|
38
|
+
*/
|
|
39
|
+
acquireSlot(): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Release a slot after callback processing completes.
|
|
42
|
+
*/
|
|
43
|
+
releaseSlot(): void;
|
|
44
|
+
/**
|
|
45
|
+
* Evaluate backpressure for a topic and trigger callbacks if needed.
|
|
46
|
+
*/
|
|
47
|
+
evaluate(bufferLength: number, topics: string[]): void;
|
|
48
|
+
/**
|
|
49
|
+
* Check if consumer is currently paused.
|
|
50
|
+
*/
|
|
51
|
+
isPaused(): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Get current in-flight callback count.
|
|
54
|
+
*/
|
|
55
|
+
getInFlightCount(): number;
|
|
56
|
+
/**
|
|
57
|
+
* Reset state (for cleanup/testing).
|
|
58
|
+
*/
|
|
59
|
+
reset(): void;
|
|
60
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { SerializedJob } from '../../types';
|
|
2
|
+
import type { MessageBuffer } from './MessageBuffer';
|
|
3
|
+
import type { BatchConfig, BatchResult, BufferedMessage, KafkaProducerClient } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* 批次處理引擎,管理 Kafka 的批次發送和消費。
|
|
6
|
+
*
|
|
7
|
+
* 功能:
|
|
8
|
+
* - 並行管線推送(pushBatches):最多 concurrency 個並行批次發送
|
|
9
|
+
* - 批次收集(collectBatch):等待 linger 時間收集滿批次
|
|
10
|
+
* - 並行回調處理(processBatch):控制並行度 + 逾時 + 部分失敗追蹤
|
|
11
|
+
*
|
|
12
|
+
* @public
|
|
13
|
+
*/
|
|
14
|
+
export declare class BatchProcessor {
|
|
15
|
+
private readonly maxBatchSize;
|
|
16
|
+
private readonly batchLingerMs;
|
|
17
|
+
private readonly concurrency;
|
|
18
|
+
private readonly enablePipeline;
|
|
19
|
+
constructor(config?: BatchConfig);
|
|
20
|
+
/**
|
|
21
|
+
* 並行管線推送多個批次到 Kafka producer。
|
|
22
|
+
*
|
|
23
|
+
* 將 jobs 分割成 maxBatchSize 的批次,
|
|
24
|
+
* 最多 concurrency 個並行發送。
|
|
25
|
+
*/
|
|
26
|
+
pushBatches(producer: KafkaProducerClient, topic: string, jobs: SerializedJob[], serialize: (job: SerializedJob) => string | Buffer): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* 從 buffer 收集一個批次的訊息。
|
|
29
|
+
*
|
|
30
|
+
* 立即取出現有訊息,如果未滿則等待 linger 時間收集更多。
|
|
31
|
+
*/
|
|
32
|
+
collectBatch(buffer: MessageBuffer, topic: string, maxWaitMs: number): Promise<BufferedMessage[]>;
|
|
33
|
+
/**
|
|
34
|
+
* 並行執行批次回調並追蹤結果。
|
|
35
|
+
*
|
|
36
|
+
* 使用信號量控制並行度,支援逾時和部分失敗。
|
|
37
|
+
*/
|
|
38
|
+
processBatch(messages: BufferedMessage[], callback: (job: SerializedJob) => Promise<void>, options: {
|
|
39
|
+
concurrency: number;
|
|
40
|
+
timeoutMs: number;
|
|
41
|
+
}): Promise<BatchResult>;
|
|
42
|
+
/**
|
|
43
|
+
* 取得當前配置快照。
|
|
44
|
+
*/
|
|
45
|
+
getConfig(): Required<BatchConfig>;
|
|
46
|
+
/**
|
|
47
|
+
* 內部:發送單一批次到 Kafka。
|
|
48
|
+
*/
|
|
49
|
+
private sendBatch;
|
|
50
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import type { ConsumerLifecycleState, LifecycleEvent } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Manages consumer lifecycle state transitions and events.
|
|
5
|
+
*
|
|
6
|
+
* State transitions:
|
|
7
|
+
* - idle → starting → running → stopping → stopped
|
|
8
|
+
* - running → restarting → running
|
|
9
|
+
* - any state → error
|
|
10
|
+
*
|
|
11
|
+
* Emits lifecycle events on state changes for coordination with other components.
|
|
12
|
+
*
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
export declare class ConsumerLifecycleManager extends EventEmitter {
|
|
16
|
+
private currentState;
|
|
17
|
+
private previousState;
|
|
18
|
+
private lastTransitionTime;
|
|
19
|
+
private stateHistory;
|
|
20
|
+
/**
|
|
21
|
+
* Register a listener for lifecycle events.
|
|
22
|
+
* Event data: { state, previousState, timestamp, error? }
|
|
23
|
+
*/
|
|
24
|
+
onStateChange(listener: (event: LifecycleEvent) => void): void;
|
|
25
|
+
/**
|
|
26
|
+
* Start the consumer (transition idle → starting → running).
|
|
27
|
+
*/
|
|
28
|
+
start(): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Restart the consumer (running → restarting → running).
|
|
31
|
+
*/
|
|
32
|
+
restart(): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Stop the consumer (running/starting → stopping → stopped).
|
|
35
|
+
*/
|
|
36
|
+
stop(): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Reset to idle state (for testing/reuse).
|
|
39
|
+
*/
|
|
40
|
+
reset(): void;
|
|
41
|
+
/**
|
|
42
|
+
* Get current lifecycle state.
|
|
43
|
+
*/
|
|
44
|
+
getState(): ConsumerLifecycleState;
|
|
45
|
+
/**
|
|
46
|
+
* Get previous lifecycle state.
|
|
47
|
+
*/
|
|
48
|
+
getPreviousState(): ConsumerLifecycleState;
|
|
49
|
+
/**
|
|
50
|
+
* Get last state transition timestamp.
|
|
51
|
+
*/
|
|
52
|
+
getLastTransitionTime(): number;
|
|
53
|
+
/**
|
|
54
|
+
* Get state transition history.
|
|
55
|
+
*/
|
|
56
|
+
getStateHistory(): Array<{
|
|
57
|
+
state: ConsumerLifecycleState;
|
|
58
|
+
timestamp: number;
|
|
59
|
+
}>;
|
|
60
|
+
/**
|
|
61
|
+
* Check if consumer is in running state.
|
|
62
|
+
*/
|
|
63
|
+
isRunning(): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Check if consumer is in error state.
|
|
66
|
+
*/
|
|
67
|
+
isError(): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Check if consumer is stopped.
|
|
70
|
+
*/
|
|
71
|
+
isStopped(): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Internal: transition to new state and emit event.
|
|
74
|
+
*/
|
|
75
|
+
private transition;
|
|
76
|
+
/**
|
|
77
|
+
* Internal: transition to error state with error details.
|
|
78
|
+
*/
|
|
79
|
+
private transitionError;
|
|
80
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ErrorCategory } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* 根據錯誤訊息與型別分類 Kafka 操作錯誤。
|
|
4
|
+
*
|
|
5
|
+
* 錯誤分類:
|
|
6
|
+
* - `transient`(影響電路斷路器):連線失敗、逾時、網路錯誤
|
|
7
|
+
* - `serialization`(不影響電路):JSON parse 失敗、序列化錯誤
|
|
8
|
+
* - `business_logic`(不影響電路):回調拋出的一般錯誤
|
|
9
|
+
* - `permanent`(不影響電路):其他永久性失敗
|
|
10
|
+
*
|
|
11
|
+
* 使用者應根據錯誤分類決定是否影響電路斷路器狀態。
|
|
12
|
+
* 只有 `transient` 錯誤才應遞增電路的失敗計數。
|
|
13
|
+
*
|
|
14
|
+
* @public
|
|
15
|
+
*/
|
|
16
|
+
export declare class ErrorCategorizer {
|
|
17
|
+
/**
|
|
18
|
+
* 將錯誤分類為標準分類之一。
|
|
19
|
+
*
|
|
20
|
+
* 分類規則(優先順序):
|
|
21
|
+
* 1. `transient` - ECONNREFUSED, ETIMEDOUT, ECONNRESET, 網路相關
|
|
22
|
+
* 2. `serialization` - SyntaxError, JSON.parse 相關
|
|
23
|
+
* 3. `business_logic` - 回調拋出的一般 Error(isBusinessLogic 為 true)
|
|
24
|
+
* 4. `permanent` - 其他所有錯誤
|
|
25
|
+
*/
|
|
26
|
+
categorize(error: Error, isBusinessLogic?: boolean): ErrorCategory;
|
|
27
|
+
/**
|
|
28
|
+
* 檢查某個分類的錯誤是否應影響電路斷路器。
|
|
29
|
+
*
|
|
30
|
+
* 只有 `transient` 分類應遞增電路的失敗計數。
|
|
31
|
+
* 其他分類(`serialization`, `business_logic`, `permanent`)
|
|
32
|
+
* 應直接跳過電路斷路器邏輯。
|
|
33
|
+
*/
|
|
34
|
+
shouldAffectCircuit(category: ErrorCategory): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* 根據錯誤直接判斷是否應影響電路(便利方法)。
|
|
37
|
+
*/
|
|
38
|
+
shouldRecordInCircuit(error: Error, isBusinessLogic?: boolean): boolean;
|
|
39
|
+
}
|