@fgiova/fastify-sqs-consumer 3.1.0 → 3.2.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 +31 -2
- package/dist/cjs/index.d.ts +16 -12
- package/dist/cjs/index.js +68 -59
- package/dist/esm/index.d.mts +16 -12
- package/dist/esm/index.mjs +68 -59
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -107,9 +107,38 @@ app.register(sqsConsumer, [
|
|
|
107
107
|
]);
|
|
108
108
|
```
|
|
109
109
|
|
|
110
|
-
##
|
|
110
|
+
## Decorators
|
|
111
111
|
|
|
112
|
-
|
|
112
|
+
### sqsConsumers
|
|
113
|
+
|
|
114
|
+
`sqsConsumers` is a `Record<string, { consumer: SQSConsumer, meta: { pendingMessages: number }, ownedClient?: MiniSQSClient }>` that provides access to the registered consumers and their metadata, keyed by consumer name.
|
|
115
|
+
It is a null-prototype object (`Object.create(null)`): use `Object.hasOwn(app.sqsConsumers, name)` or `name in app.sqsConsumers` instead of `app.sqsConsumers.hasOwnProperty(name)`.
|
|
116
|
+
|
|
117
|
+
The consumer lifecycle (start/stop) is managed by the plugin: do not call `start()` or `stop()` on these instances directly.
|
|
118
|
+
|
|
119
|
+
### addSQSConsumer
|
|
120
|
+
|
|
121
|
+
`addSQSConsumer(options: ConsumerOptions): string | null` adds a consumer at runtime. `options` accepts the same object used for each entry of the plugin options array (see [Options](#options)); the `ConsumerOptions` type is exported by the package.
|
|
122
|
+
|
|
123
|
+
- Returns the consumer name (the generated uuid when `name` is not provided), or `null` if the consumer was not created.
|
|
124
|
+
- Before the server is ready, the consumer is started together with the others in the `onReady` hook; after that, it is started immediately.
|
|
125
|
+
- Returns `null` (and logs a warning) when a consumer with the same name already exists or when the server is closing.
|
|
126
|
+
|
|
127
|
+
```js
|
|
128
|
+
import fastify from "fastify";
|
|
129
|
+
import sqsConsumer from "@fgiova/fastify-sqs-consumer";
|
|
130
|
+
|
|
131
|
+
const app = fastify();
|
|
132
|
+
await app.register(sqsConsumer, []);
|
|
133
|
+
await app.listen({ port: 3000 });
|
|
134
|
+
|
|
135
|
+
const name = app.addSQSConsumer({
|
|
136
|
+
arn: "arn:aws:sqs:eu-central-1:000000000000:MyOtherQueue",
|
|
137
|
+
handlerFunction: async (message, fastify) => {
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
```
|
|
113
142
|
|
|
114
143
|
## handlerFunction
|
|
115
144
|
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -2,17 +2,14 @@ import { type Message, MiniSQSClient } from "@fgiova/mini-sqs-client";
|
|
|
2
2
|
import { type HooksOptions, SQSConsumer } from "@fgiova/sqs-consumer";
|
|
3
3
|
import type { FastifyInstance } from "fastify";
|
|
4
4
|
import type { Pool } from "undici";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
declare function sqsConsumerPlugin(fastify: FastifyInstance, options: {
|
|
5
|
+
type ConsumerEntry = {
|
|
6
|
+
consumer: SQSConsumer;
|
|
7
|
+
meta: {
|
|
8
|
+
pendingMessages: number;
|
|
9
|
+
};
|
|
10
|
+
ownedClient?: MiniSQSClient;
|
|
11
|
+
};
|
|
12
|
+
export type ConsumerOptions = {
|
|
16
13
|
arn: string;
|
|
17
14
|
credentials?: {
|
|
18
15
|
accessKeyId: string;
|
|
@@ -35,6 +32,13 @@ declare function sqsConsumerPlugin(fastify: FastifyInstance, options: {
|
|
|
35
32
|
secretAccessKey: string;
|
|
36
33
|
};
|
|
37
34
|
};
|
|
38
|
-
}
|
|
35
|
+
};
|
|
36
|
+
declare module "fastify" {
|
|
37
|
+
interface FastifyInstance {
|
|
38
|
+
sqsConsumers: Record<string, ConsumerEntry>;
|
|
39
|
+
addSQSConsumer: (consumerOptions: ConsumerOptions) => string | null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
declare function sqsConsumerPlugin(fastify: FastifyInstance, options: ConsumerOptions[], done: (err?: Error) => void): void;
|
|
39
43
|
export declare const fastifyPlugin: typeof sqsConsumerPlugin;
|
|
40
44
|
export default fastifyPlugin;
|
package/dist/cjs/index.js
CHANGED
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.fastifyPlugin = void 0;
|
|
7
7
|
const node_crypto_1 = require("node:crypto");
|
|
8
|
+
const promises_1 = require("node:timers/promises");
|
|
8
9
|
const mini_sqs_client_1 = require("@fgiova/mini-sqs-client");
|
|
9
10
|
const sqs_consumer_1 = require("@fgiova/sqs-consumer");
|
|
10
11
|
// @ts-expect-error
|
|
@@ -12,11 +13,13 @@ const unpromise_1 = require("@watchable/unpromise");
|
|
|
12
13
|
const fastify_plugin_1 = __importDefault(require("fastify-plugin"));
|
|
13
14
|
function createConsumer(fastify, queueArn, handlerFunction, timeout = 90000, waitTimeSeconds = 20, batchSize = 1, attributeNames = [], messageAttributeNames = [], parallelExecution, hooks, sqs, credentials) {
|
|
14
15
|
const meta = { pendingMessages: 0 };
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
16
|
+
let ownedClient;
|
|
17
|
+
if (!(sqs instanceof mini_sqs_client_1.MiniSQSClient)) {
|
|
18
|
+
credentials = credentials ?? sqs?.credentials;
|
|
19
|
+
ownedClient = new mini_sqs_client_1.MiniSQSClient(queueArn.split(":")[3], sqs?.endpoint, sqs?.undiciOptions,
|
|
20
|
+
/* c8 ignore next 1 */
|
|
21
|
+
credentials ? { credentials } : undefined);
|
|
22
|
+
}
|
|
20
23
|
return {
|
|
21
24
|
consumer: new sqs_consumer_1.SQSConsumer({
|
|
22
25
|
queueARN: queueArn,
|
|
@@ -34,15 +37,7 @@ function createConsumer(fastify, queueArn, handlerFunction, timeout = 90000, wai
|
|
|
34
37
|
},
|
|
35
38
|
hooks,
|
|
36
39
|
clientOptions: {
|
|
37
|
-
sqsClient:
|
|
38
|
-
endpoint: typeof sqs === "object" && !(sqs instanceof mini_sqs_client_1.MiniSQSClient)
|
|
39
|
-
? sqs.endpoint
|
|
40
|
-
: undefined,
|
|
41
|
-
undiciOptions: typeof sqs === "object" && !(sqs instanceof mini_sqs_client_1.MiniSQSClient)
|
|
42
|
-
? sqs.undiciOptions
|
|
43
|
-
: undefined,
|
|
44
|
-
/* c8 ignore next 1 */
|
|
45
|
-
signer: credentials ? { credentials } : undefined,
|
|
40
|
+
sqsClient: ownedClient ?? sqs,
|
|
46
41
|
},
|
|
47
42
|
handler: async function handleMessageFunction(message) {
|
|
48
43
|
let timeoutId;
|
|
@@ -51,7 +46,7 @@ function createConsumer(fastify, queueArn, handlerFunction, timeout = 90000, wai
|
|
|
51
46
|
return await unpromise_1.Unpromise.race([
|
|
52
47
|
handlerFunction(message, fastify),
|
|
53
48
|
new Promise((_, reject) => {
|
|
54
|
-
timeoutId = setTimeout(() => reject(new Error("Handler execution timed out")), timeout + 1000);
|
|
49
|
+
timeoutId = setTimeout(() => reject(new Error("Handler execution timed out")), timeout + 1000).unref();
|
|
55
50
|
}),
|
|
56
51
|
]);
|
|
57
52
|
}
|
|
@@ -68,68 +63,82 @@ function createConsumer(fastify, queueArn, handlerFunction, timeout = 90000, wai
|
|
|
68
63
|
},
|
|
69
64
|
}),
|
|
70
65
|
meta,
|
|
66
|
+
ownedClient,
|
|
71
67
|
};
|
|
72
68
|
}
|
|
73
69
|
function sqsConsumerPlugin(fastify, options, done) {
|
|
74
|
-
const consumers =
|
|
70
|
+
const consumers = Object.create(null);
|
|
75
71
|
let maxExecutionTimeout = 90000;
|
|
72
|
+
let isClosing = false;
|
|
73
|
+
let isReady = false;
|
|
76
74
|
fastify.decorate("sqsConsumers", consumers);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
const
|
|
75
|
+
const startConsumer = (consumerName) => {
|
|
76
|
+
const { consumer, ownedClient } = consumers[consumerName];
|
|
77
|
+
const consumerStart = consumer.start();
|
|
78
|
+
consumerStart.catch(async (e) => {
|
|
79
|
+
fastify.log.error(e);
|
|
80
|
+
delete consumers[consumerName];
|
|
81
|
+
await consumer.stop().catch((stopErr) => fastify.log.error(stopErr));
|
|
82
|
+
await ownedClient?.destroy().catch((err) => fastify.log.error(err));
|
|
83
|
+
});
|
|
84
|
+
return consumerStart;
|
|
85
|
+
};
|
|
86
|
+
const addSQSConsumer = (consumerOptions) => {
|
|
87
|
+
const { name, arn: queueArn, handlerFunction, timeout, waitTimeSeconds, batchSize, attributeNames, messageAttributeNames, events, sqs, parallelExecution, credentials, } = consumerOptions;
|
|
88
|
+
const consumerName = name || (0, node_crypto_1.randomUUID)();
|
|
89
|
+
if (consumers[consumerName]) {
|
|
90
|
+
fastify.log.warn(`[fastify-sqs-consumer] Consumer with name ${consumerName} already exists. Skipping creation.`);
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
if (isClosing) {
|
|
94
|
+
fastify.log.warn(`[fastify-sqs-consumer] Cannot add consumer ${consumerName} while closing. Skipping creation.`);
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
maxExecutionTimeout = Math.max(maxExecutionTimeout, consumerOptions.timeout ?? 0);
|
|
80
98
|
try {
|
|
81
|
-
consumers[
|
|
82
|
-
|
|
99
|
+
consumers[consumerName] = createConsumer(fastify, queueArn, handlerFunction, timeout, waitTimeSeconds, batchSize, attributeNames, messageAttributeNames, parallelExecution, events, sqs, credentials);
|
|
100
|
+
if (isReady) {
|
|
101
|
+
void startConsumer(consumerName);
|
|
102
|
+
}
|
|
103
|
+
return consumerName;
|
|
104
|
+
} /* c8 ignore next 4 */
|
|
83
105
|
catch (e) {
|
|
84
106
|
fastify.log.error(e);
|
|
107
|
+
return null;
|
|
85
108
|
}
|
|
109
|
+
};
|
|
110
|
+
fastify.decorate("addSQSConsumer", addSQSConsumer);
|
|
111
|
+
for (const handler of options) {
|
|
112
|
+
addSQSConsumer(handler);
|
|
86
113
|
}
|
|
87
114
|
fastify.addHook("onReady", (done) => {
|
|
115
|
+
isReady = true;
|
|
88
116
|
for (const consumerName of Object.keys(consumers)) {
|
|
89
|
-
|
|
90
|
-
fastify.log.error(e);
|
|
91
|
-
await consumers[consumerName].consumer
|
|
92
|
-
.stop()
|
|
93
|
-
.catch((stopErr) => fastify.log.error(stopErr));
|
|
94
|
-
delete consumers[consumerName];
|
|
95
|
-
});
|
|
117
|
+
void startConsumer(consumerName);
|
|
96
118
|
}
|
|
97
119
|
done();
|
|
98
120
|
});
|
|
99
|
-
fastify.addHook("
|
|
100
|
-
|
|
121
|
+
fastify.addHook("preClose", async () => {
|
|
122
|
+
isClosing = true;
|
|
123
|
+
});
|
|
124
|
+
fastify.addHook("onClose", async () => {
|
|
101
125
|
const arrayConsumers = Object.values(consumers);
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if (Date.now()
|
|
126
|
+
const deadline = Date.now() + maxExecutionTimeout + 2000;
|
|
127
|
+
let stopped = false;
|
|
128
|
+
Promise.allSettled(arrayConsumers.map(({ consumer }) => consumer
|
|
129
|
+
.stop()
|
|
130
|
+
.catch((e) => fastify.log.error(`[fastify-sqs-consumer] Error stopping consumer: ${e}`)))).then(() => {
|
|
131
|
+
stopped = true;
|
|
132
|
+
});
|
|
133
|
+
while (!stopped ||
|
|
134
|
+
arrayConsumers.some(({ meta }) => meta.pendingMessages > 0)) {
|
|
135
|
+
if (Date.now() > deadline) {
|
|
112
136
|
fastify.log.warn("[fastify-sqs-consumer] Consumers are taking too long to stop... forcing shutdown");
|
|
113
|
-
|
|
114
|
-
return done();
|
|
115
|
-
}
|
|
116
|
-
/* c8 ignore next 5 */
|
|
117
|
-
if (isRunning) {
|
|
118
|
-
fastify.log.debug("[fastify-sqs-consumer] Some consumers still running... please wait");
|
|
119
|
-
return null;
|
|
137
|
+
break;
|
|
120
138
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
}
|
|
125
|
-
if (pendingMessages <= 0) {
|
|
126
|
-
clearInterval(interval);
|
|
127
|
-
return done();
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
fastify.log.debug(`[fastify-sqs-consumer] ${pendingMessages} pending messages`);
|
|
131
|
-
}
|
|
132
|
-
}, 500);
|
|
139
|
+
await (0, promises_1.setTimeout)(500);
|
|
140
|
+
}
|
|
141
|
+
await Promise.allSettled(arrayConsumers.map(({ ownedClient }) => ownedClient?.destroy()));
|
|
133
142
|
});
|
|
134
143
|
done();
|
|
135
144
|
}
|
package/dist/esm/index.d.mts
CHANGED
|
@@ -2,17 +2,14 @@ import { type Message, MiniSQSClient } from "@fgiova/mini-sqs-client";
|
|
|
2
2
|
import { type HooksOptions, SQSConsumer } from "@fgiova/sqs-consumer";
|
|
3
3
|
import type { FastifyInstance } from "fastify";
|
|
4
4
|
import type { Pool } from "undici";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
declare function sqsConsumerPlugin(fastify: FastifyInstance, options: {
|
|
5
|
+
type ConsumerEntry = {
|
|
6
|
+
consumer: SQSConsumer;
|
|
7
|
+
meta: {
|
|
8
|
+
pendingMessages: number;
|
|
9
|
+
};
|
|
10
|
+
ownedClient?: MiniSQSClient;
|
|
11
|
+
};
|
|
12
|
+
export type ConsumerOptions = {
|
|
16
13
|
arn: string;
|
|
17
14
|
credentials?: {
|
|
18
15
|
accessKeyId: string;
|
|
@@ -35,6 +32,13 @@ declare function sqsConsumerPlugin(fastify: FastifyInstance, options: {
|
|
|
35
32
|
secretAccessKey: string;
|
|
36
33
|
};
|
|
37
34
|
};
|
|
38
|
-
}
|
|
35
|
+
};
|
|
36
|
+
declare module "fastify" {
|
|
37
|
+
interface FastifyInstance {
|
|
38
|
+
sqsConsumers: Record<string, ConsumerEntry>;
|
|
39
|
+
addSQSConsumer: (consumerOptions: ConsumerOptions) => string | null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
declare function sqsConsumerPlugin(fastify: FastifyInstance, options: ConsumerOptions[], done: (err?: Error) => void): void;
|
|
39
43
|
export declare const fastifyPlugin: typeof sqsConsumerPlugin;
|
|
40
44
|
export default fastifyPlugin;
|
package/dist/esm/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { setTimeout as sleep } from "node:timers/promises";
|
|
2
3
|
import { MiniSQSClient } from "@fgiova/mini-sqs-client";
|
|
3
4
|
import { SQSConsumer } from "@fgiova/sqs-consumer";
|
|
4
5
|
// @ts-expect-error
|
|
@@ -6,11 +7,13 @@ import { Unpromise } from "@watchable/unpromise";
|
|
|
6
7
|
import fp from "fastify-plugin";
|
|
7
8
|
function createConsumer(fastify, queueArn, handlerFunction, timeout = 90000, waitTimeSeconds = 20, batchSize = 1, attributeNames = [], messageAttributeNames = [], parallelExecution, hooks, sqs, credentials) {
|
|
8
9
|
const meta = { pendingMessages: 0 };
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
let ownedClient;
|
|
11
|
+
if (!(sqs instanceof MiniSQSClient)) {
|
|
12
|
+
credentials = credentials ?? sqs?.credentials;
|
|
13
|
+
ownedClient = new MiniSQSClient(queueArn.split(":")[3], sqs?.endpoint, sqs?.undiciOptions,
|
|
14
|
+
/* c8 ignore next 1 */
|
|
15
|
+
credentials ? { credentials } : undefined);
|
|
16
|
+
}
|
|
14
17
|
return {
|
|
15
18
|
consumer: new SQSConsumer({
|
|
16
19
|
queueARN: queueArn,
|
|
@@ -28,15 +31,7 @@ function createConsumer(fastify, queueArn, handlerFunction, timeout = 90000, wai
|
|
|
28
31
|
},
|
|
29
32
|
hooks,
|
|
30
33
|
clientOptions: {
|
|
31
|
-
sqsClient:
|
|
32
|
-
endpoint: typeof sqs === "object" && !(sqs instanceof MiniSQSClient)
|
|
33
|
-
? sqs.endpoint
|
|
34
|
-
: undefined,
|
|
35
|
-
undiciOptions: typeof sqs === "object" && !(sqs instanceof MiniSQSClient)
|
|
36
|
-
? sqs.undiciOptions
|
|
37
|
-
: undefined,
|
|
38
|
-
/* c8 ignore next 1 */
|
|
39
|
-
signer: credentials ? { credentials } : undefined,
|
|
34
|
+
sqsClient: ownedClient ?? sqs,
|
|
40
35
|
},
|
|
41
36
|
handler: async function handleMessageFunction(message) {
|
|
42
37
|
let timeoutId;
|
|
@@ -45,7 +40,7 @@ function createConsumer(fastify, queueArn, handlerFunction, timeout = 90000, wai
|
|
|
45
40
|
return await Unpromise.race([
|
|
46
41
|
handlerFunction(message, fastify),
|
|
47
42
|
new Promise((_, reject) => {
|
|
48
|
-
timeoutId = setTimeout(() => reject(new Error("Handler execution timed out")), timeout + 1000);
|
|
43
|
+
timeoutId = setTimeout(() => reject(new Error("Handler execution timed out")), timeout + 1000).unref();
|
|
49
44
|
}),
|
|
50
45
|
]);
|
|
51
46
|
}
|
|
@@ -62,68 +57,82 @@ function createConsumer(fastify, queueArn, handlerFunction, timeout = 90000, wai
|
|
|
62
57
|
},
|
|
63
58
|
}),
|
|
64
59
|
meta,
|
|
60
|
+
ownedClient,
|
|
65
61
|
};
|
|
66
62
|
}
|
|
67
63
|
function sqsConsumerPlugin(fastify, options, done) {
|
|
68
|
-
const consumers =
|
|
64
|
+
const consumers = Object.create(null);
|
|
69
65
|
let maxExecutionTimeout = 90000;
|
|
66
|
+
let isClosing = false;
|
|
67
|
+
let isReady = false;
|
|
70
68
|
fastify.decorate("sqsConsumers", consumers);
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
const
|
|
69
|
+
const startConsumer = (consumerName) => {
|
|
70
|
+
const { consumer, ownedClient } = consumers[consumerName];
|
|
71
|
+
const consumerStart = consumer.start();
|
|
72
|
+
consumerStart.catch(async (e) => {
|
|
73
|
+
fastify.log.error(e);
|
|
74
|
+
delete consumers[consumerName];
|
|
75
|
+
await consumer.stop().catch((stopErr) => fastify.log.error(stopErr));
|
|
76
|
+
await ownedClient?.destroy().catch((err) => fastify.log.error(err));
|
|
77
|
+
});
|
|
78
|
+
return consumerStart;
|
|
79
|
+
};
|
|
80
|
+
const addSQSConsumer = (consumerOptions) => {
|
|
81
|
+
const { name, arn: queueArn, handlerFunction, timeout, waitTimeSeconds, batchSize, attributeNames, messageAttributeNames, events, sqs, parallelExecution, credentials, } = consumerOptions;
|
|
82
|
+
const consumerName = name || randomUUID();
|
|
83
|
+
if (consumers[consumerName]) {
|
|
84
|
+
fastify.log.warn(`[fastify-sqs-consumer] Consumer with name ${consumerName} already exists. Skipping creation.`);
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
if (isClosing) {
|
|
88
|
+
fastify.log.warn(`[fastify-sqs-consumer] Cannot add consumer ${consumerName} while closing. Skipping creation.`);
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
maxExecutionTimeout = Math.max(maxExecutionTimeout, consumerOptions.timeout ?? 0);
|
|
74
92
|
try {
|
|
75
|
-
consumers[
|
|
76
|
-
|
|
93
|
+
consumers[consumerName] = createConsumer(fastify, queueArn, handlerFunction, timeout, waitTimeSeconds, batchSize, attributeNames, messageAttributeNames, parallelExecution, events, sqs, credentials);
|
|
94
|
+
if (isReady) {
|
|
95
|
+
void startConsumer(consumerName);
|
|
96
|
+
}
|
|
97
|
+
return consumerName;
|
|
98
|
+
} /* c8 ignore next 4 */
|
|
77
99
|
catch (e) {
|
|
78
100
|
fastify.log.error(e);
|
|
101
|
+
return null;
|
|
79
102
|
}
|
|
103
|
+
};
|
|
104
|
+
fastify.decorate("addSQSConsumer", addSQSConsumer);
|
|
105
|
+
for (const handler of options) {
|
|
106
|
+
addSQSConsumer(handler);
|
|
80
107
|
}
|
|
81
108
|
fastify.addHook("onReady", (done) => {
|
|
109
|
+
isReady = true;
|
|
82
110
|
for (const consumerName of Object.keys(consumers)) {
|
|
83
|
-
|
|
84
|
-
fastify.log.error(e);
|
|
85
|
-
await consumers[consumerName].consumer
|
|
86
|
-
.stop()
|
|
87
|
-
.catch((stopErr) => fastify.log.error(stopErr));
|
|
88
|
-
delete consumers[consumerName];
|
|
89
|
-
});
|
|
111
|
+
void startConsumer(consumerName);
|
|
90
112
|
}
|
|
91
113
|
done();
|
|
92
114
|
});
|
|
93
|
-
fastify.addHook("
|
|
94
|
-
|
|
115
|
+
fastify.addHook("preClose", async () => {
|
|
116
|
+
isClosing = true;
|
|
117
|
+
});
|
|
118
|
+
fastify.addHook("onClose", async () => {
|
|
95
119
|
const arrayConsumers = Object.values(consumers);
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
if (Date.now()
|
|
120
|
+
const deadline = Date.now() + maxExecutionTimeout + 2000;
|
|
121
|
+
let stopped = false;
|
|
122
|
+
Promise.allSettled(arrayConsumers.map(({ consumer }) => consumer
|
|
123
|
+
.stop()
|
|
124
|
+
.catch((e) => fastify.log.error(`[fastify-sqs-consumer] Error stopping consumer: ${e}`)))).then(() => {
|
|
125
|
+
stopped = true;
|
|
126
|
+
});
|
|
127
|
+
while (!stopped ||
|
|
128
|
+
arrayConsumers.some(({ meta }) => meta.pendingMessages > 0)) {
|
|
129
|
+
if (Date.now() > deadline) {
|
|
106
130
|
fastify.log.warn("[fastify-sqs-consumer] Consumers are taking too long to stop... forcing shutdown");
|
|
107
|
-
|
|
108
|
-
return done();
|
|
109
|
-
}
|
|
110
|
-
/* c8 ignore next 5 */
|
|
111
|
-
if (isRunning) {
|
|
112
|
-
fastify.log.debug("[fastify-sqs-consumer] Some consumers still running... please wait");
|
|
113
|
-
return null;
|
|
131
|
+
break;
|
|
114
132
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
}
|
|
119
|
-
if (pendingMessages <= 0) {
|
|
120
|
-
clearInterval(interval);
|
|
121
|
-
return done();
|
|
122
|
-
}
|
|
123
|
-
else {
|
|
124
|
-
fastify.log.debug(`[fastify-sqs-consumer] ${pendingMessages} pending messages`);
|
|
125
|
-
}
|
|
126
|
-
}, 500);
|
|
133
|
+
await sleep(500);
|
|
134
|
+
}
|
|
135
|
+
await Promise.allSettled(arrayConsumers.map(({ ownedClient }) => ownedClient?.destroy()));
|
|
127
136
|
});
|
|
128
137
|
done();
|
|
129
138
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fgiova/fastify-sqs-consumer",
|
|
3
3
|
"description": "sqs-consumer fastify plugin",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.2.0",
|
|
5
5
|
"main": "./dist/cjs/index.js",
|
|
6
6
|
"types": "./dist/cjs/index.d.ts",
|
|
7
7
|
"exports": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"test:debug": "tap --only --timeout=0",
|
|
30
30
|
"test:local": "TEST_LOCAL=true tap",
|
|
31
31
|
"test:local:debug": "TEST_LOCAL=true tap --only --timeout=0",
|
|
32
|
-
"test:coverage": "tap --coverage-report=lcovonly --coverage-report=text"
|
|
32
|
+
"test:coverage": "tap --timeout=90 --coverage-report=lcovonly --coverage-report=text"
|
|
33
33
|
},
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"clock-mock": "^2.0.4",
|
|
78
78
|
"conventional-changelog-conventionalcommits": "^9.1.0",
|
|
79
79
|
"dotenv": "^10.0.0",
|
|
80
|
-
"fastify": "^5.
|
|
80
|
+
"fastify": "^5.12.5",
|
|
81
81
|
"semantic-release": "^25.0.3",
|
|
82
82
|
"tap": "^21.1.0",
|
|
83
83
|
"testcontainers": "^11.5.1",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"typescript": "^5.1.6"
|
|
86
86
|
},
|
|
87
87
|
"dependencies": {
|
|
88
|
-
"@fgiova/sqs-consumer": "^3.2.
|
|
88
|
+
"@fgiova/sqs-consumer": "^3.2.1",
|
|
89
89
|
"@watchable/unpromise": "^1.0.2",
|
|
90
90
|
"fastify-plugin": "^5.1.0"
|
|
91
91
|
}
|