@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 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 started
18
+ ## Getting Started
19
19
 
20
- - Run `npm add @cap-js-community/event-queue` in `@sap/cds` project
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
- const eventQueueConfig = cds.env.eventQueue;
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.0",
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.3.4"
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.6.3",
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
- ["registerCleanupForDev", true],
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, msg) => {
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
- ...(msg._fromSend || (msg.reply && { _fromSend: true })), // send or emit
75
- ...(msg.inbound && { inbound: msg.inbound }),
76
- ...(msg.event && { event: msg.event }),
77
- ...(msg.data && { data: msg.data }),
78
- ...(msg.headers && { headers: msg.headers }),
79
- ...(msg.query && { query: msg.query }),
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
 
@@ -274,7 +274,7 @@ const _checkEventIsBlocked = async (baseInstance) => {
274
274
  }
275
275
 
276
276
  if (eventBlocked) {
277
- baseInstance.logger.info("skipping run because periodic event is blocked by configuration", {
277
+ baseInstance.logger.info("skipping run because event is blocked by configuration", {
278
278
  type: baseInstance.eventType,
279
279
  subType: baseInstance.eventSubType,
280
280
  });
@@ -112,7 +112,7 @@ const broadcastEvent = async (tenantId, type, subType) => {
112
112
  [type, subType].join("##")
113
113
  );
114
114
  if (result) {
115
- logger.info("skip publish redis event as no lock is available", {
115
+ logger.debug("skip publish redis event as no lock is available", {
116
116
  type,
117
117
  subType,
118
118
  });
@@ -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
  }