@cap-js-community/event-queue 1.3.0 → 1.3.2
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 +2 -17
- package/cds-plugin.js +1 -2
- package/package.json +5 -3
- package/src/EventQueueProcessorBase.js +2 -1
- package/src/initialize.js +10 -12
- package/src/outbox/eventQueueAsOutbox.js +16 -7
- package/src/processEventQueue.js +1 -1
- package/src/redisPubSub.js +1 -1
- package/src/shared/cdsHelper.js +7 -0
package/README.md
CHANGED
|
@@ -15,24 +15,9 @@ allowing for processing at defined intervals. This feature further extends its c
|
|
|
15
15
|
transaction management, ensuring that even regularly occurring tasks are handled efficiently and effectively without
|
|
16
16
|
overloading any single instance. This makes it an ideal solution for applications needing consistent, reliable event processing.
|
|
17
17
|
|
|
18
|
-
## Getting
|
|
18
|
+
## Getting Started
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
- Activate the cds-plugin in the cds section of the package.json.
|
|
22
|
-
|
|
23
|
-
### As cds-plugin
|
|
24
|
-
|
|
25
|
-
For detailed information check out the [documentation](https://cap-js-community.github.io/event-queue/setup).
|
|
26
|
-
|
|
27
|
-
```json
|
|
28
|
-
{
|
|
29
|
-
"cds": {
|
|
30
|
-
"eventQueue": {
|
|
31
|
-
"configFilePath": "./srv/eventQueueConfig.yml"
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
```
|
|
20
|
+
Dive right in and check out the getting started [documentation](https://cap-js-community.github.io/event-queue/setup).
|
|
36
21
|
|
|
37
22
|
## Features
|
|
38
23
|
|
package/cds-plugin.js
CHANGED
|
@@ -5,7 +5,6 @@ const cds = require("@sap/cds");
|
|
|
5
5
|
const eventQueue = require("./src");
|
|
6
6
|
const COMPONENT_NAME = "/eventQueue/plugin";
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
if (!(cds.build.register || (!eventQueueConfig?.config && !eventQueueConfig?.configFilePath))) {
|
|
8
|
+
if (!cds.build.register && cds.env.eventQueue) {
|
|
10
9
|
eventQueue.initialize().catch((err) => cds.log(COMPONENT_NAME).error(err));
|
|
11
10
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cap-js-community/event-queue",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.2",
|
|
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
|
"files": [
|
|
@@ -44,14 +44,16 @@
|
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"redis": "4.6.13",
|
|
46
46
|
"verror": "1.10.1",
|
|
47
|
-
"yaml": "2.
|
|
47
|
+
"yaml": "2.4.0"
|
|
48
48
|
},
|
|
49
49
|
"devDependencies": {
|
|
50
|
+
"@cap-js/hana": "^0.0.5",
|
|
51
|
+
"@cap-js/sqlite": "^1.5.0",
|
|
50
52
|
"@sap/cds": "^7.5.3",
|
|
51
53
|
"@sap/cds-dk": "^7.5.1",
|
|
52
54
|
"eslint": "^8.56.0",
|
|
53
55
|
"eslint-config-prettier": "^9.1.0",
|
|
54
|
-
"eslint-plugin-jest": "^27.
|
|
56
|
+
"eslint-plugin-jest": "^27.9.0",
|
|
55
57
|
"eslint-plugin-node": "^11.1.0",
|
|
56
58
|
"express": "^4.18.2",
|
|
57
59
|
"hdb": "^0.19.7",
|
|
@@ -20,6 +20,7 @@ const LIMIT_PARALLEL_EVENT_PROCESSING = 10;
|
|
|
20
20
|
const SELECT_LIMIT_EVENTS_PER_TICK = 100;
|
|
21
21
|
const TRIES_FOR_EXCEEDED_EVENTS = 3;
|
|
22
22
|
const EVENT_START_AFTER_HEADROOM = 3 * 1000;
|
|
23
|
+
const ETAG_CHECK_AFTER_MIN = 10;
|
|
23
24
|
|
|
24
25
|
let serviceBindingCache = null;
|
|
25
26
|
|
|
@@ -901,7 +902,7 @@ class EventQueueProcessorBase {
|
|
|
901
902
|
* @return {Promise<boolean>} true if the db record of the event has been modified since selection
|
|
902
903
|
*/
|
|
903
904
|
async isOutdatedAndKeepalive(queueEntries) {
|
|
904
|
-
if (!this.__outdatedCheckEnabled) {
|
|
905
|
+
if (!this.__outdatedCheckEnabled || new Date() - this.__startTime <= ETAG_CHECK_AFTER_MIN * 60 * 1000) {
|
|
905
906
|
return false;
|
|
906
907
|
}
|
|
907
908
|
let eventOutdated;
|
package/src/initialize.js
CHANGED
|
@@ -40,7 +40,7 @@ const CONFIG_VARS = [
|
|
|
40
40
|
["useAsCAPOutbox", false],
|
|
41
41
|
["userId", null],
|
|
42
42
|
["enableTxConsistencyCheck", false],
|
|
43
|
-
["
|
|
43
|
+
["cleanupLocksAndEventsForDev", false],
|
|
44
44
|
];
|
|
45
45
|
|
|
46
46
|
const initialize = async ({
|
|
@@ -84,21 +84,19 @@ const initialize = async ({
|
|
|
84
84
|
);
|
|
85
85
|
|
|
86
86
|
const logger = cds.log(COMPONENT);
|
|
87
|
-
config.fileContent = await readConfigFromFile(config.configFilePath);
|
|
88
87
|
config.checkRedisEnabled();
|
|
88
|
+
cds.on("connect", (service) => {
|
|
89
|
+
if (service.name === "db") {
|
|
90
|
+
config.processEventsAfterPublish && dbHandler.registerEventQueueDbHandler(service);
|
|
91
|
+
config.cleanupLocksAndEventsForDev && registerCleanupForDevDb().catch(() => {});
|
|
92
|
+
registerEventProcessors();
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
config.fileContent = await readConfigFromFile(config.configFilePath);
|
|
89
96
|
|
|
90
|
-
if (config.processEventsAfterPublish) {
|
|
91
|
-
cds.on("connect", (service) => {
|
|
92
|
-
if (service.name === "db") {
|
|
93
|
-
dbHandler.registerEventQueueDbHandler(service);
|
|
94
|
-
config.cleanupLocksAndEventsForDev && registerCleanupForDevDb().catch(() => {});
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
97
|
!config.skipCsnCheck && (await csnCheck());
|
|
99
98
|
|
|
100
99
|
monkeyPatchCAPOutbox();
|
|
101
|
-
registerEventProcessors();
|
|
102
100
|
registerCdsShutdown();
|
|
103
101
|
logger.info("event queue initialized", {
|
|
104
102
|
registerAsEventProcessor: config.registerAsEventProcessor,
|
|
@@ -227,7 +225,7 @@ const registerCdsShutdown = () => {
|
|
|
227
225
|
|
|
228
226
|
const registerCleanupForDevDb = async () => {
|
|
229
227
|
const profile = cds.env.profiles.find((profile) => profile === "development");
|
|
230
|
-
if (!profile) {
|
|
228
|
+
if (!profile || process.env.NODE_ENV === "production") {
|
|
231
229
|
return;
|
|
232
230
|
}
|
|
233
231
|
|
|
@@ -68,21 +68,30 @@ function unboxed(srv) {
|
|
|
68
68
|
return srv[UNBOXED] || srv;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
const _mapToEventAndPublish = async (context, name,
|
|
71
|
+
const _mapToEventAndPublish = async (context, name, req) => {
|
|
72
|
+
let startAfter;
|
|
73
|
+
for (const header in req.headers ?? {}) {
|
|
74
|
+
if (header.toLocaleLowerCase() === "x-eventqueue-startafter") {
|
|
75
|
+
startAfter = req.headers[header];
|
|
76
|
+
delete req.headers[header];
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
72
80
|
const event = {
|
|
73
81
|
contextUser: context.user.id,
|
|
74
|
-
...(
|
|
75
|
-
...(
|
|
76
|
-
...(
|
|
77
|
-
...(
|
|
78
|
-
...(
|
|
79
|
-
...(
|
|
82
|
+
...(req._fromSend || (req.reply && { _fromSend: true })), // send or emit
|
|
83
|
+
...(req.inbound && { inbound: req.inbound }),
|
|
84
|
+
...(req.event && { event: req.event }),
|
|
85
|
+
...(req.data && { data: req.data }),
|
|
86
|
+
...(req.headers && { headers: req.headers }),
|
|
87
|
+
...(req.query && { query: req.query }),
|
|
80
88
|
};
|
|
81
89
|
|
|
82
90
|
await publishEvent(cds.tx(context), {
|
|
83
91
|
type: CDS_EVENT_TYPE,
|
|
84
92
|
subType: name,
|
|
85
93
|
payload: JSON.stringify(event),
|
|
94
|
+
...(startAfter && { startAfter }),
|
|
86
95
|
});
|
|
87
96
|
};
|
|
88
97
|
|
package/src/processEventQueue.js
CHANGED
|
@@ -274,7 +274,7 @@ const _checkEventIsBlocked = async (baseInstance) => {
|
|
|
274
274
|
}
|
|
275
275
|
|
|
276
276
|
if (eventBlocked) {
|
|
277
|
-
baseInstance.logger.info("skipping run because
|
|
277
|
+
baseInstance.logger.info("skipping run because event is blocked by configuration", {
|
|
278
278
|
type: baseInstance.eventType,
|
|
279
279
|
subType: baseInstance.eventSubType,
|
|
280
280
|
});
|
package/src/redisPubSub.js
CHANGED
|
@@ -112,7 +112,7 @@ const broadcastEvent = async (tenantId, type, subType) => {
|
|
|
112
112
|
[type, subType].join("##")
|
|
113
113
|
);
|
|
114
114
|
if (result) {
|
|
115
|
-
logger.
|
|
115
|
+
logger.debug("skip publish redis event as no lock is available", {
|
|
116
116
|
type,
|
|
117
117
|
subType,
|
|
118
118
|
});
|
package/src/shared/cdsHelper.js
CHANGED
|
@@ -57,6 +57,13 @@ async function executeInNewTransaction(context = {}, transactionTag, fn, args, {
|
|
|
57
57
|
);
|
|
58
58
|
} else {
|
|
59
59
|
contextTx.context.user = user;
|
|
60
|
+
try {
|
|
61
|
+
contextTx.set?.({
|
|
62
|
+
"$user.id": user.id,
|
|
63
|
+
});
|
|
64
|
+
} catch {
|
|
65
|
+
/* empty */
|
|
66
|
+
}
|
|
60
67
|
await fn(contextTx, ...parameters);
|
|
61
68
|
}
|
|
62
69
|
}
|