@fgiova/sqs-consumer 3.2.1 → 3.4.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 +45 -7
- package/dist/cjs/hooks.d.ts +5 -1
- package/dist/cjs/hooks.js +6 -0
- package/dist/cjs/index.d.ts +3 -0
- package/dist/cjs/index.js +59 -10
- package/dist/esm/hooks.d.mts +5 -1
- package/dist/esm/hooks.mjs +6 -0
- package/dist/esm/index.d.mts +3 -0
- package/dist/esm/index.mjs +59 -10
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -78,13 +78,50 @@ const consumer = new SQSConsumer({
|
|
|
78
78
|
| destroySigner | boolean | true | Whether to destroy the signer when the consumer is destroyed. |
|
|
79
79
|
|
|
80
80
|
### ConsumerOptions
|
|
81
|
-
| Option | Type
|
|
82
|
-
|
|
83
|
-
| visibilityTimeout | number
|
|
84
|
-
| waitTimeSeconds | number
|
|
85
|
-
| itemsPerRequest | number
|
|
86
|
-
| messageAttributeNames | string[]
|
|
87
|
-
| attributeNames | string[]
|
|
81
|
+
| Option | Type | Default | Description |
|
|
82
|
+
|-----------------------|-----------------------------|---------|----------------------------------------------------------------------------------------------------------------------------------------------|
|
|
83
|
+
| visibilityTimeout | number | 30 | The visibility timeout for the messages in seconds |
|
|
84
|
+
| waitTimeSeconds | number | 20 | The wait time for the receiveMessage call in seconds |
|
|
85
|
+
| itemsPerRequest | number | 10 | The maximum number of messages to be received at once |
|
|
86
|
+
| messageAttributeNames | string[] | [] | The message attribute names to be included in the response |
|
|
87
|
+
| attributeNames | string[] | [] | The attribute names to be included in the response |
|
|
88
|
+
| errorDelaySeconds | number | 0 | Seconds a failed message stays invisible before it can be received again |
|
|
89
|
+
| errorDelayBackoff | (attempt: number) => number | | Returns the delay in seconds for a failed message, given its `ApproximateReceiveCount`. Takes precedence over `errorDelaySeconds` |
|
|
90
|
+
| maxReceiveCount | number / (attempt: number) => number | | Receive count after which a failed message triggers `onMaxReceiveCountExceeded`. A function receives the `ApproximateReceiveCount` and returns the threshold |
|
|
91
|
+
|
|
92
|
+
Delays are truncated to an integer and clamped to the SQS range 0–43200 seconds. When `errorDelayBackoff` is set, `ApproximateReceiveCount` is added to `attributeNames` automatically. If `errorDelayBackoff` throws, the error is reported to `onSQSError` and `errorDelaySeconds` is used instead.
|
|
93
|
+
|
|
94
|
+
> **Note:** SQS counts the 12-hour visibility limit from when the message was received, not from when the delay is set. A delay that exceeds the remaining time (roughly `43200 - executionTimeout - visibility extensions`) is silently rejected by SQS and the message is retried after its current visibility timeout. Keep delays well below 12 hours, and keep their sum below the queue's `MessageRetentionPeriod`, or the message expires without reaching the DLQ.
|
|
95
|
+
>
|
|
96
|
+
> Every retry increments `ApproximateReceiveCount`, so with a redrive policy the message moves to the DLQ after `maxReceiveCount` attempts: size the backoff accordingly. On FIFO queues a delayed message blocks its whole message group until the delay expires.
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
const consumer = new SQSConsumer({
|
|
100
|
+
queueARN: "arn:aws:sqs:eu-central-1:000000000000:test",
|
|
101
|
+
handler: async (message) => { /* ... */ },
|
|
102
|
+
consumerOptions: {
|
|
103
|
+
// 1st failure: 2s, 2nd: 4s, 3rd: 8s ... capped at 15 minutes
|
|
104
|
+
errorDelayBackoff: (attempt) => Math.min(2 ** attempt, 900),
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
When a message fails and its `ApproximateReceiveCount` reaches `maxReceiveCount`, the `onMaxReceiveCountExceeded` hook is called with the message and its receive count. **The message is deleted unless a hook returns `false`**, including when no hook is registered: return `false` to release it with the usual error delay instead. When `maxReceiveCount` is set, `ApproximateReceiveCount` is added to `attributeNames` automatically. If the `maxReceiveCount` function throws, the error is reported to `onSQSError` and the message is released.
|
|
110
|
+
|
|
111
|
+
> **Note:** if the queue has a redrive policy, set `maxReceiveCount` below the queue's own `maxReceiveCount`, or SQS moves the message to the DLQ before the hook is called.
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
const consumer = new SQSConsumer({
|
|
115
|
+
queueARN: "arn:aws:sqs:eu-central-1:000000000000:test",
|
|
116
|
+
handler: async (message) => { /* ... */ },
|
|
117
|
+
consumerOptions: {
|
|
118
|
+
maxReceiveCount: 5,
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
consumer.addHook("onMaxReceiveCountExceeded", async (message, count) => {
|
|
122
|
+
await archive(message); // keep a copy, then let the consumer delete it
|
|
123
|
+
});
|
|
124
|
+
```
|
|
88
125
|
|
|
89
126
|
### Hooks
|
|
90
127
|
| Option | Type | Description |
|
|
@@ -94,6 +131,7 @@ const consumer = new SQSConsumer({
|
|
|
94
131
|
| onHandlerSuccess | (message: Message) => Promise<Message> | Called when the consumer handles a message successfully |
|
|
95
132
|
| onHandlerTimeout | (message: Message) => Promise<boolean> | Called when the consumer handler execution exceed executionTimeout |
|
|
96
133
|
| onHandlerError | (message: Message, error: Error) => Promise<boolean> | Called when the consumer handler execution throws an error |
|
|
134
|
+
| onMaxReceiveCountExceeded | (message: Message, count: number) => Promise<boolean> | Called when a failed message reaches `maxReceiveCount`. Return `false` to keep the message, otherwise it is deleted |
|
|
97
135
|
| onSuccess | (message: Message) => Promise<boolean> | Called when the consumer handler execution finishes successfully |
|
|
98
136
|
| onError | (hook: HookName, message: Message, error: Error) => Promise<boolean> | Called when the consumer handler execution throws an uncaught error |
|
|
99
137
|
| onSQSError | (error: Error, message?: Message) => Promise<void> | Called when the consumer receives an error from the SQS service |
|
package/dist/cjs/hooks.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** biome-ignore-all lint/complexity/noBannedTypes: Function required as generic */
|
|
2
2
|
import type { Message } from "@fgiova/mini-sqs-client";
|
|
3
3
|
import type { Logger } from "./logger";
|
|
4
|
-
export type HookName = "onStart" | "onStop" | "onPoll" | "onMessage" | "onHandlerSuccess" | "onHandlerTimeout" | "onHandlerError" | "onSuccess" | "onError" | "onSQSError";
|
|
4
|
+
export type HookName = "onStart" | "onStop" | "onPoll" | "onMessage" | "onHandlerSuccess" | "onHandlerTimeout" | "onHandlerError" | "onMaxReceiveCountExceeded" | "onSuccess" | "onError" | "onSQSError";
|
|
5
5
|
export type HookSignatureMap = {
|
|
6
6
|
onStart: {
|
|
7
7
|
args: [sqsConsumer: unknown];
|
|
@@ -31,6 +31,10 @@ export type HookSignatureMap = {
|
|
|
31
31
|
args: [message: Message, error: Error];
|
|
32
32
|
return: boolean;
|
|
33
33
|
};
|
|
34
|
+
onMaxReceiveCountExceeded: {
|
|
35
|
+
args: [message: Message, count: number];
|
|
36
|
+
return: boolean;
|
|
37
|
+
};
|
|
34
38
|
onSuccess: {
|
|
35
39
|
args: [message: Message];
|
|
36
40
|
return: boolean;
|
package/dist/cjs/hooks.js
CHANGED
|
@@ -39,6 +39,11 @@ class Hooks {
|
|
|
39
39
|
throwable: false,
|
|
40
40
|
type: "boolean-return",
|
|
41
41
|
},
|
|
42
|
+
onMaxReceiveCountExceeded: {
|
|
43
|
+
S: Symbol("maxReceiveCountExceeded"),
|
|
44
|
+
throwable: false,
|
|
45
|
+
type: "boolean-return",
|
|
46
|
+
},
|
|
42
47
|
onSuccess: {
|
|
43
48
|
S: Symbol("success"),
|
|
44
49
|
throwable: false,
|
|
@@ -64,6 +69,7 @@ class Hooks {
|
|
|
64
69
|
[this.hookSymbols.onHandlerSuccess.S]: [],
|
|
65
70
|
[this.hookSymbols.onHandlerTimeout.S]: [],
|
|
66
71
|
[this.hookSymbols.onHandlerError.S]: [],
|
|
72
|
+
[this.hookSymbols.onMaxReceiveCountExceeded.S]: [],
|
|
67
73
|
[this.hookSymbols.onSuccess.S]: [],
|
|
68
74
|
[this.hookSymbols.onError.S]: [],
|
|
69
75
|
[this.hookSymbols.onSQSError.S]: [],
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -23,6 +23,9 @@ type ConsumerOptions = {
|
|
|
23
23
|
itemsPerRequest?: number;
|
|
24
24
|
messageAttributeNames?: string[];
|
|
25
25
|
attributeNames?: string[];
|
|
26
|
+
errorDelaySeconds?: number;
|
|
27
|
+
errorDelayBackoff?: (attempt: number) => number;
|
|
28
|
+
maxReceiveCount?: number | ((attempt: number) => number);
|
|
26
29
|
};
|
|
27
30
|
type ConsumerHookName = Exclude<HookName, "onStart" | "onStop">;
|
|
28
31
|
export type HooksOptions = {
|
package/dist/cjs/index.js
CHANGED
|
@@ -42,7 +42,18 @@ class SQSConsumer {
|
|
|
42
42
|
waitTimeSeconds: options.consumerOptions?.waitTimeSeconds ?? 20,
|
|
43
43
|
itemsPerRequest: options.consumerOptions?.itemsPerRequest ?? 10,
|
|
44
44
|
messageAttributeNames: options.consumerOptions?.messageAttributeNames ?? [],
|
|
45
|
-
attributeNames:
|
|
45
|
+
attributeNames: [
|
|
46
|
+
...new Set([
|
|
47
|
+
...(options.consumerOptions?.attributeNames ?? []),
|
|
48
|
+
...(options.consumerOptions?.errorDelayBackoff ||
|
|
49
|
+
options.consumerOptions?.maxReceiveCount !== undefined
|
|
50
|
+
? ["ApproximateReceiveCount"]
|
|
51
|
+
: []),
|
|
52
|
+
]),
|
|
53
|
+
],
|
|
54
|
+
errorDelaySeconds: options.consumerOptions?.errorDelaySeconds ?? 0,
|
|
55
|
+
errorDelayBackoff: options.consumerOptions?.errorDelayBackoff,
|
|
56
|
+
maxReceiveCount: options.consumerOptions?.maxReceiveCount,
|
|
46
57
|
};
|
|
47
58
|
this.sqsClient =
|
|
48
59
|
options.clientOptions?.sqsClient ??
|
|
@@ -120,9 +131,11 @@ class SQSConsumer {
|
|
|
120
131
|
}
|
|
121
132
|
}
|
|
122
133
|
haExtendVisibilityTimeout(pendingReceipts, visibilityTimeout) {
|
|
123
|
-
if (this.handlerOptions.extendVisibilityTimeout
|
|
124
|
-
visibilityTimeout > 0) {
|
|
134
|
+
if (this.handlerOptions.extendVisibilityTimeout && visibilityTimeout > 0) {
|
|
125
135
|
return setInterval(async () => {
|
|
136
|
+
/* c8 ignore next 1 */
|
|
137
|
+
if (!pendingReceipts.size)
|
|
138
|
+
return;
|
|
126
139
|
try {
|
|
127
140
|
await this.sqsClient.changeMessageVisibilityBatch(this.queueARN, [...pendingReceipts], visibilityTimeout);
|
|
128
141
|
} /* c8 ignore next 3 */
|
|
@@ -138,10 +151,46 @@ class SQSConsumer {
|
|
|
138
151
|
const candidateToRelease = [];
|
|
139
152
|
for (const result of messagesResults) {
|
|
140
153
|
if (result.error) {
|
|
141
|
-
|
|
142
|
-
|
|
154
|
+
let delaySeconds = this.consumerOptions.errorDelaySeconds;
|
|
155
|
+
const maxReceiveCount = this.consumerOptions.maxReceiveCount;
|
|
156
|
+
if (maxReceiveCount !== undefined) {
|
|
157
|
+
try {
|
|
158
|
+
const receiveCount = Number(
|
|
159
|
+
/* c8 ignore next 1 */
|
|
160
|
+
result.message.Attributes?.ApproximateReceiveCount ?? 0);
|
|
161
|
+
if (receiveCount >=
|
|
162
|
+
(typeof maxReceiveCount === "function"
|
|
163
|
+
? maxReceiveCount(receiveCount)
|
|
164
|
+
: maxReceiveCount)) {
|
|
165
|
+
const toDelete = await this.hooks.runHook("onMaxReceiveCountExceeded", result.message, receiveCount);
|
|
166
|
+
if (toDelete) {
|
|
167
|
+
// biome-ignore lint/style/noNonNullAssertion: ReceiptHandle must be present here
|
|
168
|
+
candidatesToDelete.push(result.message.ReceiptHandle);
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
catch (e) {
|
|
174
|
+
await this.hooks.runHook("onSQSError", e);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (this.consumerOptions.errorDelayBackoff) {
|
|
178
|
+
try {
|
|
179
|
+
delaySeconds = this.consumerOptions.errorDelayBackoff(
|
|
180
|
+
/* c8 ignore next 1 */
|
|
181
|
+
Number(result.message.Attributes?.ApproximateReceiveCount ?? 0));
|
|
182
|
+
}
|
|
183
|
+
catch (e) {
|
|
184
|
+
await this.hooks.runHook("onSQSError", e);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
candidateToRelease.push({
|
|
188
|
+
// biome-ignore lint/style/noNonNullAssertion: ReceiptHandle must be present here
|
|
189
|
+
receiptHandle: result.message.ReceiptHandle,
|
|
190
|
+
visibilityTimeout: Math.min(43200, Math.max(0, Math.trunc(delaySeconds) || 0)),
|
|
191
|
+
});
|
|
143
192
|
}
|
|
144
|
-
else if (this.handlerOptions.deleteMessage
|
|
193
|
+
else if (this.handlerOptions.deleteMessage) {
|
|
145
194
|
// biome-ignore lint/style/noNonNullAssertion: ReceiptHandle must be present here
|
|
146
195
|
candidatesToDelete.push(result.message.ReceiptHandle);
|
|
147
196
|
}
|
|
@@ -150,15 +199,14 @@ class SQSConsumer {
|
|
|
150
199
|
await this.sqsClient.deleteMessageBatch(this.queueARN, candidatesToDelete);
|
|
151
200
|
}
|
|
152
201
|
if (candidateToRelease.length) {
|
|
153
|
-
await this.sqsClient.changeMessageVisibilityBatch(this.queueARN, candidateToRelease
|
|
202
|
+
await this.sqsClient.changeMessageVisibilityBatch(this.queueARN, candidateToRelease);
|
|
154
203
|
}
|
|
155
204
|
}
|
|
156
205
|
async pollMessages() {
|
|
157
206
|
let consecutiveErrors = 0;
|
|
158
207
|
while (this.running) {
|
|
159
208
|
try {
|
|
160
|
-
|
|
161
|
-
const visibilityTimeout = this.consumerOptions.visibilityTimeout ?? 30;
|
|
209
|
+
const visibilityTimeout = this.consumerOptions.visibilityTimeout;
|
|
162
210
|
const messagesResult = await this.sqsClient.receiveMessage(this.queueARN, {
|
|
163
211
|
WaitTimeSeconds: this.consumerOptions.waitTimeSeconds,
|
|
164
212
|
MaxNumberOfMessages: this.consumerOptions.itemsPerRequest,
|
|
@@ -175,13 +223,14 @@ class SQSConsumer {
|
|
|
175
223
|
messages.map((message) => message.ReceiptHandle));
|
|
176
224
|
const haTimeout = this.haExtendVisibilityTimeout(pendingReceipts, visibilityTimeout);
|
|
177
225
|
try {
|
|
178
|
-
if (this.handlerOptions.parallelExecution
|
|
226
|
+
if (this.handlerOptions.parallelExecution) {
|
|
179
227
|
const results = await (0, p_map_1.default)(messages, async (message) => {
|
|
180
228
|
return await this.runOnMessageHook(message);
|
|
181
229
|
}, {
|
|
182
230
|
concurrency: 10,
|
|
183
231
|
stopOnError: false,
|
|
184
232
|
});
|
|
233
|
+
pendingReceipts.clear();
|
|
185
234
|
await this.deleteMessages(results);
|
|
186
235
|
}
|
|
187
236
|
else {
|
package/dist/esm/hooks.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** biome-ignore-all lint/complexity/noBannedTypes: Function required as generic */
|
|
2
2
|
import type { Message } from "@fgiova/mini-sqs-client";
|
|
3
3
|
import type { Logger } from "./logger.mjs";
|
|
4
|
-
export type HookName = "onStart" | "onStop" | "onPoll" | "onMessage" | "onHandlerSuccess" | "onHandlerTimeout" | "onHandlerError" | "onSuccess" | "onError" | "onSQSError";
|
|
4
|
+
export type HookName = "onStart" | "onStop" | "onPoll" | "onMessage" | "onHandlerSuccess" | "onHandlerTimeout" | "onHandlerError" | "onMaxReceiveCountExceeded" | "onSuccess" | "onError" | "onSQSError";
|
|
5
5
|
export type HookSignatureMap = {
|
|
6
6
|
onStart: {
|
|
7
7
|
args: [sqsConsumer: unknown];
|
|
@@ -31,6 +31,10 @@ export type HookSignatureMap = {
|
|
|
31
31
|
args: [message: Message, error: Error];
|
|
32
32
|
return: boolean;
|
|
33
33
|
};
|
|
34
|
+
onMaxReceiveCountExceeded: {
|
|
35
|
+
args: [message: Message, count: number];
|
|
36
|
+
return: boolean;
|
|
37
|
+
};
|
|
34
38
|
onSuccess: {
|
|
35
39
|
args: [message: Message];
|
|
36
40
|
return: boolean;
|
package/dist/esm/hooks.mjs
CHANGED
|
@@ -36,6 +36,11 @@ export class Hooks {
|
|
|
36
36
|
throwable: false,
|
|
37
37
|
type: "boolean-return",
|
|
38
38
|
},
|
|
39
|
+
onMaxReceiveCountExceeded: {
|
|
40
|
+
S: Symbol("maxReceiveCountExceeded"),
|
|
41
|
+
throwable: false,
|
|
42
|
+
type: "boolean-return",
|
|
43
|
+
},
|
|
39
44
|
onSuccess: {
|
|
40
45
|
S: Symbol("success"),
|
|
41
46
|
throwable: false,
|
|
@@ -61,6 +66,7 @@ export class Hooks {
|
|
|
61
66
|
[this.hookSymbols.onHandlerSuccess.S]: [],
|
|
62
67
|
[this.hookSymbols.onHandlerTimeout.S]: [],
|
|
63
68
|
[this.hookSymbols.onHandlerError.S]: [],
|
|
69
|
+
[this.hookSymbols.onMaxReceiveCountExceeded.S]: [],
|
|
64
70
|
[this.hookSymbols.onSuccess.S]: [],
|
|
65
71
|
[this.hookSymbols.onError.S]: [],
|
|
66
72
|
[this.hookSymbols.onSQSError.S]: [],
|
package/dist/esm/index.d.mts
CHANGED
|
@@ -23,6 +23,9 @@ type ConsumerOptions = {
|
|
|
23
23
|
itemsPerRequest?: number;
|
|
24
24
|
messageAttributeNames?: string[];
|
|
25
25
|
attributeNames?: string[];
|
|
26
|
+
errorDelaySeconds?: number;
|
|
27
|
+
errorDelayBackoff?: (attempt: number) => number;
|
|
28
|
+
maxReceiveCount?: number | ((attempt: number) => number);
|
|
26
29
|
};
|
|
27
30
|
type ConsumerHookName = Exclude<HookName, "onStart" | "onStop">;
|
|
28
31
|
export type HooksOptions = {
|
package/dist/esm/index.mjs
CHANGED
|
@@ -35,7 +35,18 @@ export class SQSConsumer {
|
|
|
35
35
|
waitTimeSeconds: options.consumerOptions?.waitTimeSeconds ?? 20,
|
|
36
36
|
itemsPerRequest: options.consumerOptions?.itemsPerRequest ?? 10,
|
|
37
37
|
messageAttributeNames: options.consumerOptions?.messageAttributeNames ?? [],
|
|
38
|
-
attributeNames:
|
|
38
|
+
attributeNames: [
|
|
39
|
+
...new Set([
|
|
40
|
+
...(options.consumerOptions?.attributeNames ?? []),
|
|
41
|
+
...(options.consumerOptions?.errorDelayBackoff ||
|
|
42
|
+
options.consumerOptions?.maxReceiveCount !== undefined
|
|
43
|
+
? ["ApproximateReceiveCount"]
|
|
44
|
+
: []),
|
|
45
|
+
]),
|
|
46
|
+
],
|
|
47
|
+
errorDelaySeconds: options.consumerOptions?.errorDelaySeconds ?? 0,
|
|
48
|
+
errorDelayBackoff: options.consumerOptions?.errorDelayBackoff,
|
|
49
|
+
maxReceiveCount: options.consumerOptions?.maxReceiveCount,
|
|
39
50
|
};
|
|
40
51
|
this.sqsClient =
|
|
41
52
|
options.clientOptions?.sqsClient ??
|
|
@@ -113,9 +124,11 @@ export class SQSConsumer {
|
|
|
113
124
|
}
|
|
114
125
|
}
|
|
115
126
|
haExtendVisibilityTimeout(pendingReceipts, visibilityTimeout) {
|
|
116
|
-
if (this.handlerOptions.extendVisibilityTimeout
|
|
117
|
-
visibilityTimeout > 0) {
|
|
127
|
+
if (this.handlerOptions.extendVisibilityTimeout && visibilityTimeout > 0) {
|
|
118
128
|
return setInterval(async () => {
|
|
129
|
+
/* c8 ignore next 1 */
|
|
130
|
+
if (!pendingReceipts.size)
|
|
131
|
+
return;
|
|
119
132
|
try {
|
|
120
133
|
await this.sqsClient.changeMessageVisibilityBatch(this.queueARN, [...pendingReceipts], visibilityTimeout);
|
|
121
134
|
} /* c8 ignore next 3 */
|
|
@@ -131,10 +144,46 @@ export class SQSConsumer {
|
|
|
131
144
|
const candidateToRelease = [];
|
|
132
145
|
for (const result of messagesResults) {
|
|
133
146
|
if (result.error) {
|
|
134
|
-
|
|
135
|
-
|
|
147
|
+
let delaySeconds = this.consumerOptions.errorDelaySeconds;
|
|
148
|
+
const maxReceiveCount = this.consumerOptions.maxReceiveCount;
|
|
149
|
+
if (maxReceiveCount !== undefined) {
|
|
150
|
+
try {
|
|
151
|
+
const receiveCount = Number(
|
|
152
|
+
/* c8 ignore next 1 */
|
|
153
|
+
result.message.Attributes?.ApproximateReceiveCount ?? 0);
|
|
154
|
+
if (receiveCount >=
|
|
155
|
+
(typeof maxReceiveCount === "function"
|
|
156
|
+
? maxReceiveCount(receiveCount)
|
|
157
|
+
: maxReceiveCount)) {
|
|
158
|
+
const toDelete = await this.hooks.runHook("onMaxReceiveCountExceeded", result.message, receiveCount);
|
|
159
|
+
if (toDelete) {
|
|
160
|
+
// biome-ignore lint/style/noNonNullAssertion: ReceiptHandle must be present here
|
|
161
|
+
candidatesToDelete.push(result.message.ReceiptHandle);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
catch (e) {
|
|
167
|
+
await this.hooks.runHook("onSQSError", e);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
if (this.consumerOptions.errorDelayBackoff) {
|
|
171
|
+
try {
|
|
172
|
+
delaySeconds = this.consumerOptions.errorDelayBackoff(
|
|
173
|
+
/* c8 ignore next 1 */
|
|
174
|
+
Number(result.message.Attributes?.ApproximateReceiveCount ?? 0));
|
|
175
|
+
}
|
|
176
|
+
catch (e) {
|
|
177
|
+
await this.hooks.runHook("onSQSError", e);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
candidateToRelease.push({
|
|
181
|
+
// biome-ignore lint/style/noNonNullAssertion: ReceiptHandle must be present here
|
|
182
|
+
receiptHandle: result.message.ReceiptHandle,
|
|
183
|
+
visibilityTimeout: Math.min(43200, Math.max(0, Math.trunc(delaySeconds) || 0)),
|
|
184
|
+
});
|
|
136
185
|
}
|
|
137
|
-
else if (this.handlerOptions.deleteMessage
|
|
186
|
+
else if (this.handlerOptions.deleteMessage) {
|
|
138
187
|
// biome-ignore lint/style/noNonNullAssertion: ReceiptHandle must be present here
|
|
139
188
|
candidatesToDelete.push(result.message.ReceiptHandle);
|
|
140
189
|
}
|
|
@@ -143,15 +192,14 @@ export class SQSConsumer {
|
|
|
143
192
|
await this.sqsClient.deleteMessageBatch(this.queueARN, candidatesToDelete);
|
|
144
193
|
}
|
|
145
194
|
if (candidateToRelease.length) {
|
|
146
|
-
await this.sqsClient.changeMessageVisibilityBatch(this.queueARN, candidateToRelease
|
|
195
|
+
await this.sqsClient.changeMessageVisibilityBatch(this.queueARN, candidateToRelease);
|
|
147
196
|
}
|
|
148
197
|
}
|
|
149
198
|
async pollMessages() {
|
|
150
199
|
let consecutiveErrors = 0;
|
|
151
200
|
while (this.running) {
|
|
152
201
|
try {
|
|
153
|
-
|
|
154
|
-
const visibilityTimeout = this.consumerOptions.visibilityTimeout ?? 30;
|
|
202
|
+
const visibilityTimeout = this.consumerOptions.visibilityTimeout;
|
|
155
203
|
const messagesResult = await this.sqsClient.receiveMessage(this.queueARN, {
|
|
156
204
|
WaitTimeSeconds: this.consumerOptions.waitTimeSeconds,
|
|
157
205
|
MaxNumberOfMessages: this.consumerOptions.itemsPerRequest,
|
|
@@ -168,13 +216,14 @@ export class SQSConsumer {
|
|
|
168
216
|
messages.map((message) => message.ReceiptHandle));
|
|
169
217
|
const haTimeout = this.haExtendVisibilityTimeout(pendingReceipts, visibilityTimeout);
|
|
170
218
|
try {
|
|
171
|
-
if (this.handlerOptions.parallelExecution
|
|
219
|
+
if (this.handlerOptions.parallelExecution) {
|
|
172
220
|
const results = await pMap(messages, async (message) => {
|
|
173
221
|
return await this.runOnMessageHook(message);
|
|
174
222
|
}, {
|
|
175
223
|
concurrency: 10,
|
|
176
224
|
stopOnError: false,
|
|
177
225
|
});
|
|
226
|
+
pendingReceipts.clear();
|
|
178
227
|
await this.deleteMessages(results);
|
|
179
228
|
}
|
|
180
229
|
else {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fgiova/sqs-consumer",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.4.0",
|
|
4
4
|
"main": "./dist/cjs/index.js",
|
|
5
5
|
"types": "./dist/cjs/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
]
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
|
-
"@fgiova/mini-sqs-client": "^5.
|
|
59
|
+
"@fgiova/mini-sqs-client": "^5.2.0",
|
|
60
60
|
"@watchable/unpromise": "^1.0.2",
|
|
61
61
|
"p-map": "^7.0.3"
|
|
62
62
|
},
|