@cap-js-community/event-queue 1.3.6 → 1.4.1
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/cds-plugin.js +1 -1
- package/package.json +6 -5
- package/src/EventQueueProcessorBase.js +2 -102
- package/src/config.js +29 -24
- package/src/dbHandler.js +11 -8
- package/src/initialize.js +8 -67
- package/src/processEventQueue.js +0 -6
- package/src/redis/redisPub.js +90 -0
- package/src/redis/redisSub.js +92 -0
- package/src/runner/openEvents.js +52 -0
- package/src/{runner.js → runner/runner.js} +129 -117
- package/src/runner/runnerHelper.js +37 -0
- package/src/shared/cdsHelper.js +10 -28
- package/src/shared/common.js +52 -1
- package/src/shared/distributedLock.js +3 -3
- package/src/shared/eventScheduler.js +2 -2
- package/src/shared/redis.js +15 -14
- package/src/redisPubSub.js +0 -170
package/src/shared/redis.js
CHANGED
|
@@ -12,7 +12,7 @@ let mainClientPromise;
|
|
|
12
12
|
const subscriberChannelClientPromise = {};
|
|
13
13
|
let lastErrorLog = Date.now();
|
|
14
14
|
|
|
15
|
-
const createMainClientAndConnect = () => {
|
|
15
|
+
const createMainClientAndConnect = (options) => {
|
|
16
16
|
if (mainClientPromise) {
|
|
17
17
|
return mainClientPromise;
|
|
18
18
|
}
|
|
@@ -20,14 +20,14 @@ const createMainClientAndConnect = () => {
|
|
|
20
20
|
const errorHandlerCreateClient = (err) => {
|
|
21
21
|
cds.log(COMPONENT_NAME).error("error from redis client for pub/sub failed", err);
|
|
22
22
|
mainClientPromise = null;
|
|
23
|
-
setTimeout(createMainClientAndConnect, LOG_AFTER_SEC * 1000).unref();
|
|
23
|
+
setTimeout(() => createMainClientAndConnect(options), LOG_AFTER_SEC * 1000).unref();
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
mainClientPromise = createClientAndConnect(errorHandlerCreateClient);
|
|
26
|
+
mainClientPromise = createClientAndConnect(options, errorHandlerCreateClient);
|
|
27
27
|
return mainClientPromise;
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
-
const _createClientBase = () => {
|
|
30
|
+
const _createClientBase = (redisOptions) => {
|
|
31
31
|
const env = getEnvInstance();
|
|
32
32
|
try {
|
|
33
33
|
const credentials = env.getRedisCredentialsFromEnv();
|
|
@@ -40,18 +40,19 @@ const _createClientBase = () => {
|
|
|
40
40
|
defaults: {
|
|
41
41
|
password: credentials.password,
|
|
42
42
|
socket: { tls: credentials.tls },
|
|
43
|
+
...redisOptions,
|
|
43
44
|
},
|
|
44
45
|
});
|
|
45
46
|
}
|
|
46
|
-
return redis.createClient({ url });
|
|
47
|
+
return redis.createClient({ url, ...redisOptions });
|
|
47
48
|
} catch (err) {
|
|
48
49
|
throw EventQueueError.redisConnectionFailure(err);
|
|
49
50
|
}
|
|
50
51
|
};
|
|
51
52
|
|
|
52
|
-
const createClientAndConnect = async (errorHandlerCreateClient) => {
|
|
53
|
+
const createClientAndConnect = async (options, errorHandlerCreateClient) => {
|
|
53
54
|
try {
|
|
54
|
-
const client = _createClientBase();
|
|
55
|
+
const client = _createClientBase(options);
|
|
55
56
|
await client.connect();
|
|
56
57
|
client.on("error", (err) => {
|
|
57
58
|
const dateNow = Date.now();
|
|
@@ -74,14 +75,14 @@ const createClientAndConnect = async (errorHandlerCreateClient) => {
|
|
|
74
75
|
}
|
|
75
76
|
};
|
|
76
77
|
|
|
77
|
-
const subscribeRedisChannel = (channel, subscribeHandler) => {
|
|
78
|
+
const subscribeRedisChannel = (options, channel, subscribeHandler) => {
|
|
78
79
|
const errorHandlerCreateClient = (err) => {
|
|
79
80
|
cds.log(COMPONENT_NAME).error(`error from redis client for pub/sub failed for channel ${channel}`, err);
|
|
80
81
|
subscriberChannelClientPromise[channel] = null;
|
|
81
|
-
setTimeout(() => subscribeRedisChannel(channel, subscribeHandler), LOG_AFTER_SEC * 1000).unref();
|
|
82
|
+
setTimeout(() => subscribeRedisChannel(options, channel, subscribeHandler), LOG_AFTER_SEC * 1000).unref();
|
|
82
83
|
};
|
|
83
84
|
|
|
84
|
-
subscriberChannelClientPromise[channel] = createClientAndConnect(errorHandlerCreateClient)
|
|
85
|
+
subscriberChannelClientPromise[channel] = createClientAndConnect(options, errorHandlerCreateClient)
|
|
85
86
|
.then((client) => {
|
|
86
87
|
cds.log(COMPONENT_NAME).info("subscribe redis client connected channel", { channel });
|
|
87
88
|
client.subscribe(channel, subscribeHandler).catch(errorHandlerCreateClient);
|
|
@@ -93,8 +94,8 @@ const subscribeRedisChannel = (channel, subscribeHandler) => {
|
|
|
93
94
|
});
|
|
94
95
|
};
|
|
95
96
|
|
|
96
|
-
const publishMessage = async (channel, message) => {
|
|
97
|
-
const client = await createMainClientAndConnect();
|
|
97
|
+
const publishMessage = async (options, channel, message) => {
|
|
98
|
+
const client = await createMainClientAndConnect(options);
|
|
98
99
|
return await client.publish(channel, message);
|
|
99
100
|
};
|
|
100
101
|
|
|
@@ -116,9 +117,9 @@ const _resilientClientClose = async (client) => {
|
|
|
116
117
|
}
|
|
117
118
|
};
|
|
118
119
|
|
|
119
|
-
const connectionCheck = async () => {
|
|
120
|
+
const connectionCheck = async (options) => {
|
|
120
121
|
return new Promise((resolve, reject) => {
|
|
121
|
-
createClientAndConnect(reject)
|
|
122
|
+
createClientAndConnect(options, reject)
|
|
122
123
|
.then((client) => {
|
|
123
124
|
if (client) {
|
|
124
125
|
_resilientClientClose(client);
|
package/src/redisPubSub.js
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
const { promisify } = require("util");
|
|
4
|
-
|
|
5
|
-
const cds = require("@sap/cds");
|
|
6
|
-
|
|
7
|
-
const redis = require("./shared/redis");
|
|
8
|
-
const { checkLockExistsAndReturnValue } = require("./shared/distributedLock");
|
|
9
|
-
const config = require("./config");
|
|
10
|
-
const runner = require("./runner");
|
|
11
|
-
const { getSubdomainForTenantId } = require("./shared/cdsHelper");
|
|
12
|
-
|
|
13
|
-
const EVENT_MESSAGE_CHANNEL = "EVENT_QUEUE_MESSAGE_CHANNEL";
|
|
14
|
-
const COMPONENT_NAME = "/eventQueue/redisPubSub";
|
|
15
|
-
const TRIES_FOR_PUBLISH_PERIODIC_EVENT = 10;
|
|
16
|
-
const SLEEP_TIME_FOR_PUBLISH_PERIODIC_EVENT = 30 * 1000;
|
|
17
|
-
|
|
18
|
-
const wait = promisify(setTimeout);
|
|
19
|
-
let subscriberClientPromise;
|
|
20
|
-
|
|
21
|
-
const initEventQueueRedisSubscribe = () => {
|
|
22
|
-
if (subscriberClientPromise || !config.redisEnabled) {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
redis.subscribeRedisChannel(EVENT_MESSAGE_CHANNEL, _messageHandlerProcessEvents);
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const _messageHandlerProcessEvents = async (messageData) => {
|
|
29
|
-
const logger = cds.log(COMPONENT_NAME);
|
|
30
|
-
try {
|
|
31
|
-
const { tenantId, type, subType } = JSON.parse(messageData);
|
|
32
|
-
logger.debug("received redis event", {
|
|
33
|
-
tenantId,
|
|
34
|
-
type,
|
|
35
|
-
subType,
|
|
36
|
-
});
|
|
37
|
-
if (!config.isEventQueueActive) {
|
|
38
|
-
cds.log(COMPONENT_NAME).info("Skipping processing because runner is deactivated!", {
|
|
39
|
-
type,
|
|
40
|
-
subType,
|
|
41
|
-
});
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const subdomain = await getSubdomainForTenantId(tenantId);
|
|
46
|
-
const user = new cds.User.Privileged(config.userId);
|
|
47
|
-
const tenantContext = {
|
|
48
|
-
tenant: tenantId,
|
|
49
|
-
user,
|
|
50
|
-
// NOTE: we need this because of logging otherwise logs would not contain the subdomain
|
|
51
|
-
http: { req: { authInfo: { getSubdomain: () => subdomain } } },
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
if (!config.getEventConfig(type, subType)) {
|
|
55
|
-
if (config.isCapOutboxEvent(type)) {
|
|
56
|
-
try {
|
|
57
|
-
const service = await cds.connect.to(subType);
|
|
58
|
-
cds.outboxed(service);
|
|
59
|
-
} catch (err) {
|
|
60
|
-
logger.error("could not connect to outboxed service", err, {
|
|
61
|
-
type,
|
|
62
|
-
subType,
|
|
63
|
-
});
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
} else {
|
|
67
|
-
logger.error("cannot find configuration for published event. Event won't be processed", {
|
|
68
|
-
type,
|
|
69
|
-
subType,
|
|
70
|
-
});
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return await cds.tx(tenantContext, async ({ context }) => {
|
|
76
|
-
return await runner.runEventCombinationForTenant(context, type, subType);
|
|
77
|
-
});
|
|
78
|
-
} catch (err) {
|
|
79
|
-
logger.error("could not parse event information", {
|
|
80
|
-
messageData,
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const broadcastEvent = async (tenantId, type, subType) => {
|
|
86
|
-
const logger = cds.log(COMPONENT_NAME);
|
|
87
|
-
try {
|
|
88
|
-
if (!config.isEventQueueActive) {
|
|
89
|
-
cds.log(COMPONENT_NAME).info("Skipping processing because runner is deactivated!", {
|
|
90
|
-
type,
|
|
91
|
-
subType,
|
|
92
|
-
});
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
if (!config.redisEnabled) {
|
|
96
|
-
if (config.registerAsEventProcessor) {
|
|
97
|
-
let context = {};
|
|
98
|
-
if (tenantId) {
|
|
99
|
-
const subdomain = await getSubdomainForTenantId(tenantId);
|
|
100
|
-
const user = new cds.User.Privileged(config.userId);
|
|
101
|
-
context = {
|
|
102
|
-
// NOTE: we need this because of logging otherwise logs would not contain the subdomain
|
|
103
|
-
tenant: tenantId,
|
|
104
|
-
user,
|
|
105
|
-
http: { req: { authInfo: { getSubdomain: () => subdomain } } },
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return await cds.tx(context, async ({ context }) => {
|
|
110
|
-
return await runner.runEventCombinationForTenant(context, type, subType);
|
|
111
|
-
});
|
|
112
|
-
}
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
const eventConfig = config.getEventConfig(type, subType);
|
|
116
|
-
for (let i = 0; i < TRIES_FOR_PUBLISH_PERIODIC_EVENT; i++) {
|
|
117
|
-
const result = await checkLockExistsAndReturnValue(
|
|
118
|
-
new cds.EventContext({ tenant: tenantId }),
|
|
119
|
-
[type, subType].join("##")
|
|
120
|
-
);
|
|
121
|
-
if (result) {
|
|
122
|
-
logger.debug("skip publish redis event as no lock is available", {
|
|
123
|
-
type,
|
|
124
|
-
subType,
|
|
125
|
-
index: i,
|
|
126
|
-
isPeriodic: eventConfig.isPeriodic,
|
|
127
|
-
waitInterval: SLEEP_TIME_FOR_PUBLISH_PERIODIC_EVENT,
|
|
128
|
-
});
|
|
129
|
-
if (!eventConfig.isPeriodic) {
|
|
130
|
-
break;
|
|
131
|
-
}
|
|
132
|
-
await wait(SLEEP_TIME_FOR_PUBLISH_PERIODIC_EVENT);
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
logger.debug("publishing redis event", {
|
|
136
|
-
tenantId,
|
|
137
|
-
type,
|
|
138
|
-
subType,
|
|
139
|
-
});
|
|
140
|
-
await redis.publishMessage(EVENT_MESSAGE_CHANNEL, JSON.stringify({ tenantId, type, subType }));
|
|
141
|
-
break;
|
|
142
|
-
}
|
|
143
|
-
} catch (err) {
|
|
144
|
-
logger.error("publish event failed!", err, {
|
|
145
|
-
tenantId,
|
|
146
|
-
type,
|
|
147
|
-
subType,
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
const closeSubscribeClient = async () => {
|
|
153
|
-
try {
|
|
154
|
-
const client = await subscriberClientPromise;
|
|
155
|
-
if (client?.quit) {
|
|
156
|
-
await client.quit();
|
|
157
|
-
}
|
|
158
|
-
} catch (err) {
|
|
159
|
-
// ignore errors during shutdown
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
module.exports = {
|
|
164
|
-
initEventQueueRedisSubscribe,
|
|
165
|
-
broadcastEvent,
|
|
166
|
-
closeSubscribeClient,
|
|
167
|
-
__: {
|
|
168
|
-
_messageHandlerProcessEvents,
|
|
169
|
-
},
|
|
170
|
-
};
|