@cap-js-community/event-queue 1.8.2 → 1.8.3
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/package.json +1 -1
- package/src/initialize.js +20 -4
- package/src/redis/redisSub.js +2 -14
- package/src/shared/env.js +1 -1
- package/src/shared/redis.js +47 -15
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cap-js-community/event-queue",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.3",
|
|
4
4
|
"description": "An event queue that enables secure transactional processing of asynchronous and periodic events, featuring instant event processing with Redis Pub/Sub and load distribution across all application instances.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "src/index.d.ts",
|
package/src/initialize.js
CHANGED
|
@@ -10,7 +10,7 @@ const VError = require("verror");
|
|
|
10
10
|
const runner = require("./runner/runner");
|
|
11
11
|
const dbHandler = require("./dbHandler");
|
|
12
12
|
const config = require("./config");
|
|
13
|
-
const
|
|
13
|
+
const redisSub = require("./redis/redisSub");
|
|
14
14
|
const redis = require("./shared/redis");
|
|
15
15
|
const eventQueueAsOutbox = require("./outbox/eventQueueAsOutbox");
|
|
16
16
|
const { getAllTenantIds } = require("./shared/cdsHelper");
|
|
@@ -22,6 +22,7 @@ const readFileAsync = promisify(fs.readFile);
|
|
|
22
22
|
|
|
23
23
|
const VERROR_CLUSTER_NAME = "EventQueueInitialization";
|
|
24
24
|
const COMPONENT = "eventQueue/initialize";
|
|
25
|
+
const TIMEOUT_SHUTDOWN = 2500;
|
|
25
26
|
|
|
26
27
|
const CONFIG_VARS = [
|
|
27
28
|
["configFilePath", null],
|
|
@@ -148,7 +149,7 @@ const registerEventProcessors = () => {
|
|
|
148
149
|
const errorHandler = (err) => cds.log(COMPONENT).error("error during init runner", err);
|
|
149
150
|
|
|
150
151
|
if (config.redisEnabled) {
|
|
151
|
-
initEventQueueRedisSubscribe();
|
|
152
|
+
redisSub.initEventQueueRedisSubscribe();
|
|
152
153
|
config.attachConfigChangeHandler();
|
|
153
154
|
if (config.isMultiTenancy) {
|
|
154
155
|
runner.multiTenancyRedis().catch(errorHandler);
|
|
@@ -186,9 +187,24 @@ const mixConfigVarsWithEnv = (options) => {
|
|
|
186
187
|
};
|
|
187
188
|
|
|
188
189
|
const registerCdsShutdown = () => {
|
|
190
|
+
const isTestProfile = cds.env.profiles.find((profile) => profile.includes("test"));
|
|
191
|
+
if (isTestProfile) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
189
194
|
cds.on("shutdown", async () => {
|
|
190
|
-
await
|
|
191
|
-
|
|
195
|
+
return await new Promise((resolve) => {
|
|
196
|
+
const timeoutRef = setTimeout(() => {
|
|
197
|
+
clearTimeout(timeoutRef);
|
|
198
|
+
cds.log(COMPONENT).info("shutdown timeout reached - some locks might not have been released!");
|
|
199
|
+
resolve();
|
|
200
|
+
}, TIMEOUT_SHUTDOWN);
|
|
201
|
+
distributedLock.shutdownHandler().then(() =>
|
|
202
|
+
Promise.allSettled([redis.closeMainClient(), redis.closeSubscribeClient()]).then((result) => {
|
|
203
|
+
clearTimeout(timeoutRef);
|
|
204
|
+
resolve(result);
|
|
205
|
+
})
|
|
206
|
+
);
|
|
207
|
+
});
|
|
192
208
|
});
|
|
193
209
|
};
|
|
194
210
|
|
package/src/redis/redisSub.js
CHANGED
|
@@ -9,12 +9,12 @@ const common = require("../shared/common");
|
|
|
9
9
|
|
|
10
10
|
const EVENT_MESSAGE_CHANNEL = "EVENT_QUEUE_MESSAGE_CHANNEL";
|
|
11
11
|
const COMPONENT_NAME = "/eventQueue/redisSub";
|
|
12
|
-
let subscriberClientPromise;
|
|
13
12
|
|
|
14
13
|
const initEventQueueRedisSubscribe = () => {
|
|
15
|
-
if (
|
|
14
|
+
if (initEventQueueRedisSubscribe._initDone || !config.redisEnabled) {
|
|
16
15
|
return;
|
|
17
16
|
}
|
|
17
|
+
initEventQueueRedisSubscribe._initDone = true;
|
|
18
18
|
redis.subscribeRedisChannel(config.redisOptions, EVENT_MESSAGE_CHANNEL, _messageHandlerProcessEvents);
|
|
19
19
|
};
|
|
20
20
|
|
|
@@ -83,20 +83,8 @@ const _messageHandlerProcessEvents = async (messageData) => {
|
|
|
83
83
|
}
|
|
84
84
|
};
|
|
85
85
|
|
|
86
|
-
const closeSubscribeClient = async () => {
|
|
87
|
-
try {
|
|
88
|
-
const client = await subscriberClientPromise;
|
|
89
|
-
if (client?.quit) {
|
|
90
|
-
await client.quit();
|
|
91
|
-
}
|
|
92
|
-
} catch (err) {
|
|
93
|
-
// ignore errors during shutdown
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
|
|
97
86
|
module.exports = {
|
|
98
87
|
initEventQueueRedisSubscribe,
|
|
99
|
-
closeSubscribeClient,
|
|
100
88
|
__: {
|
|
101
89
|
_messageHandlerProcessEvents,
|
|
102
90
|
},
|
package/src/shared/env.js
CHANGED
package/src/shared/redis.js
CHANGED
|
@@ -9,7 +9,8 @@ const COMPONENT_NAME = "/eventQueue/shared/redis";
|
|
|
9
9
|
const LOG_AFTER_SEC = 5;
|
|
10
10
|
|
|
11
11
|
let mainClientPromise;
|
|
12
|
-
|
|
12
|
+
let subscriberClientPromise;
|
|
13
|
+
const subscribedChannels = {};
|
|
13
14
|
let lastErrorLog = Date.now();
|
|
14
15
|
|
|
15
16
|
const createMainClientAndConnect = (options) => {
|
|
@@ -18,7 +19,8 @@ const createMainClientAndConnect = (options) => {
|
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
const errorHandlerCreateClient = (err) => {
|
|
21
|
-
|
|
22
|
+
mainClientPromise?.then?.(_resilientClientClose);
|
|
23
|
+
cds.log(COMPONENT_NAME).error("error from redis main client:", err);
|
|
22
24
|
mainClientPromise = null;
|
|
23
25
|
setTimeout(() => createMainClientAndConnect(options), LOG_AFTER_SEC * 1000).unref();
|
|
24
26
|
};
|
|
@@ -63,7 +65,7 @@ const createClientAndConnect = async (options, errorHandlerCreateClient, isConne
|
|
|
63
65
|
client.on("error", (err) => {
|
|
64
66
|
const dateNow = Date.now();
|
|
65
67
|
if (dateNow - lastErrorLog > LOG_AFTER_SEC * 1000) {
|
|
66
|
-
cds.log(COMPONENT_NAME).error("error
|
|
68
|
+
cds.log(COMPONENT_NAME).error("error redis client:", err);
|
|
67
69
|
lastErrorLog = dateNow;
|
|
68
70
|
}
|
|
69
71
|
});
|
|
@@ -84,21 +86,48 @@ const createClientAndConnect = async (options, errorHandlerCreateClient, isConne
|
|
|
84
86
|
};
|
|
85
87
|
|
|
86
88
|
const subscribeRedisChannel = (options, channel, subscribeHandler) => {
|
|
89
|
+
subscribedChannels[channel] = subscribeHandler;
|
|
87
90
|
const errorHandlerCreateClient = (err) => {
|
|
88
91
|
cds.log(COMPONENT_NAME).error(`error from redis client for pub/sub failed for channel ${channel}`, err);
|
|
89
|
-
|
|
90
|
-
|
|
92
|
+
subscriberClientPromise?.then?.(_resilientClientClose);
|
|
93
|
+
subscriberClientPromise = null;
|
|
94
|
+
setTimeout(() => _subscribeChannels(options, subscribedChannels, subscribeHandler), LOG_AFTER_SEC * 1000).unref();
|
|
91
95
|
};
|
|
92
96
|
|
|
93
|
-
|
|
97
|
+
_subscribeChannels(options, { [channel]: subscribeHandler }, errorHandlerCreateClient);
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const _subscribeChannels = (options, subscribedChannels, errorHandlerCreateClient) => {
|
|
101
|
+
subscriberClientPromise = createClientAndConnect(options, errorHandlerCreateClient)
|
|
94
102
|
.then((client) => {
|
|
95
|
-
|
|
96
|
-
|
|
103
|
+
for (const channel in subscribedChannels) {
|
|
104
|
+
const fn = subscribedChannels[channel];
|
|
105
|
+
client._subscribedChannels ??= {};
|
|
106
|
+
if (client._subscribedChannels[channel]) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
cds.log(COMPONENT_NAME).info("subscribe redis client connected channel", { channel });
|
|
110
|
+
client
|
|
111
|
+
.subscribe(channel, fn)
|
|
112
|
+
.then(() => {
|
|
113
|
+
client._subscribedChannels ??= {};
|
|
114
|
+
client._subscribedChannels[channel] = 1;
|
|
115
|
+
})
|
|
116
|
+
.catch(() => {
|
|
117
|
+
cds.log(COMPONENT_NAME).error("error subscribe to channel - retrying...");
|
|
118
|
+
setTimeout(() => _subscribeChannels(options, [channel], fn), LOG_AFTER_SEC * 1000).unref();
|
|
119
|
+
});
|
|
120
|
+
}
|
|
97
121
|
})
|
|
98
122
|
.catch((err) => {
|
|
99
123
|
cds
|
|
100
124
|
.log(COMPONENT_NAME)
|
|
101
|
-
.error(
|
|
125
|
+
.error(
|
|
126
|
+
`error from redis client for pub/sub failed during startup - trying to reconnect - ${Object.keys(
|
|
127
|
+
subscribedChannels
|
|
128
|
+
).join(", ")}`,
|
|
129
|
+
err
|
|
130
|
+
);
|
|
102
131
|
});
|
|
103
132
|
};
|
|
104
133
|
|
|
@@ -108,11 +137,13 @@ const publishMessage = async (options, channel, message) => {
|
|
|
108
137
|
};
|
|
109
138
|
|
|
110
139
|
const closeMainClient = async () => {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
140
|
+
await _resilientClientClose(await mainClientPromise);
|
|
141
|
+
cds.log(COMPONENT_NAME).info("main redis client closed!");
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const closeSubscribeClient = async () => {
|
|
145
|
+
await _resilientClientClose(await subscriberClientPromise);
|
|
146
|
+
cds.log(COMPONENT_NAME).info("subscribe redis client closed!");
|
|
116
147
|
};
|
|
117
148
|
|
|
118
149
|
const _resilientClientClose = async (client) => {
|
|
@@ -121,7 +152,7 @@ const _resilientClientClose = async (client) => {
|
|
|
121
152
|
await client.quit();
|
|
122
153
|
}
|
|
123
154
|
} catch (err) {
|
|
124
|
-
|
|
155
|
+
cds.log(COMPONENT_NAME).info("error during redis close - continuing...", err);
|
|
125
156
|
}
|
|
126
157
|
};
|
|
127
158
|
|
|
@@ -151,5 +182,6 @@ module.exports = {
|
|
|
151
182
|
subscribeRedisChannel,
|
|
152
183
|
publishMessage,
|
|
153
184
|
closeMainClient,
|
|
185
|
+
closeSubscribeClient,
|
|
154
186
|
connectionCheck,
|
|
155
187
|
};
|