@cap-js-community/event-queue 1.11.0 → 2.0.0-beta.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/db/Event.cds CHANGED
@@ -25,4 +25,5 @@ entity Event: cuid {
25
25
  startAfter: Timestamp;
26
26
  context: LargeString;
27
27
  error: String;
28
+ namespace: String default 'default';
28
29
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cap-js-community/event-queue",
3
- "version": "1.11.0",
3
+ "version": "2.0.0-beta.0",
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",
@@ -47,18 +47,20 @@
47
47
  "node": ">=18"
48
48
  },
49
49
  "dependencies": {
50
- "@sap/xssec": "^4.10.0",
50
+ "@cap-js-community/common": "0.3.2",
51
+ "@sap/xssec": "^4.11.0",
51
52
  "cron-parser": "^5.4.0",
52
- "redis": "^4.7.0",
53
53
  "verror": "^1.10.1",
54
54
  "yaml": "^2.7.1"
55
55
  },
56
56
  "devDependencies": {
57
+ "@actions/core": "^1.11.1",
57
58
  "@cap-js/cds-test": "^0.4.0",
58
- "@cap-js/hana": "^2.3.3",
59
- "@cap-js/sqlite": "^2.0.3",
60
- "@sap/cds": "^9.4.1",
61
- "@sap/cds-dk": "^9.4.1",
59
+ "@cap-js/hana": "^2.3.4",
60
+ "@cap-js/sqlite": "^2.0.4",
61
+ "@opentelemetry/api": "^1.9.0",
62
+ "@sap/cds": "^9.4.4",
63
+ "@sap/cds-dk": "^9.4.2",
62
64
  "eslint": "^8.57.0",
63
65
  "eslint-config-prettier": "^9.1.0",
64
66
  "eslint-plugin-jest": "^28.6.0",
@@ -67,9 +69,7 @@
67
69
  "hdb": "^2.26.1",
68
70
  "jest": "^29.7.0",
69
71
  "prettier": "^2.8.8",
70
- "sqlite3": "^5.1.7",
71
- "@opentelemetry/api": "^1.9.0",
72
- "@actions/core": "^1.11.1"
72
+ "sqlite3": "^5.1.7"
73
73
  },
74
74
  "homepage": "https://cap-js-community.github.io/event-queue/",
75
75
  "repository": {
@@ -100,6 +100,12 @@
100
100
  }
101
101
  },
102
102
  "requires": {
103
+ "xsuaa-eventQueue": {
104
+ "vcap": {
105
+ "label": "xsuaa",
106
+ "plan": "application"
107
+ }
108
+ },
103
109
  "redis-eventQueue": {
104
110
  "options": {},
105
111
  "vcap": {
@@ -42,15 +42,17 @@ class EventQueueProcessorBase {
42
42
  #keepAliveRunner;
43
43
  #currentKeepAlivePromise = Promise.resolve();
44
44
  #etagMap;
45
+ #namespace;
45
46
 
46
47
  constructor(context, eventType, eventSubType, config) {
47
48
  this.__context = context;
48
49
  this.__baseContext = context;
49
50
  this.__tx = cds.tx(context);
50
51
  this.__baseLogger = cds.log(COMPONENT_NAME);
52
+ this.#namespace = config.namespace;
51
53
  this.#eventSchedulerInstance = eventScheduler.getInstance();
52
54
  this.#config = eventConfig;
53
- this.#isPeriodic = this.#config.isPeriodicEvent(eventType, eventSubType);
55
+ this.#isPeriodic = this.#config.isPeriodicEvent(eventType, eventSubType, this.#namespace);
54
56
  this.__logger = null;
55
57
  this.__eventProcessingMap = {};
56
58
  this.__statusMap = {};
@@ -178,6 +180,7 @@ class EventQueueProcessorBase {
178
180
  this.__context.tenant,
179
181
  this.#eventType,
180
182
  this.#eventSubType,
183
+ this.#namespace,
181
184
  new Date(Date.now() + 5 * 1000) // add some offset to make sure all locks are released
182
185
  );
183
186
  }
@@ -477,6 +480,7 @@ class EventQueueProcessorBase {
477
480
  this.__context.tenant,
478
481
  this.#eventType,
479
482
  this.#eventSubType,
483
+ this.#namespace,
480
484
  startAfter
481
485
  );
482
486
  }
@@ -606,6 +610,8 @@ class EventQueueProcessorBase {
606
610
  this.#eventType,
607
611
  "AND subType=",
608
612
  this.#eventSubType,
613
+ "AND namespace =",
614
+ this.#namespace,
609
615
  "AND ( startAfter IS NULL OR startAfter <=",
610
616
  refDateStartAfter.toISOString(),
611
617
  " ) AND ( status =",
@@ -743,6 +749,7 @@ class EventQueueProcessorBase {
743
749
  this.__context.tenant,
744
750
  this.#eventType,
745
751
  this.#eventSubType,
752
+ delayedEvent.namespace,
746
753
  delayedEvent.startAfter
747
754
  );
748
755
  }
@@ -960,7 +967,7 @@ class EventQueueProcessorBase {
960
967
  return await trace(this.baseContext, "acquire-lock", async () => {
961
968
  const lockAcquired = await distributedLock.acquireLock(
962
969
  this.__context,
963
- [this.#eventType, this.#eventSubType].join("##"),
970
+ [this.#namespace, this.#eventType, this.#eventSubType].join("##"),
964
971
  { keepTrackOfLock: true, expiryTime: this.#eventConfig.keepAliveMaxInProgressTime * 1000 }
965
972
  );
966
973
  if (!lockAcquired) {
@@ -1002,7 +1009,10 @@ class EventQueueProcessorBase {
1002
1009
  }
1003
1010
  try {
1004
1011
  await trace(this.baseContext, "release-lock", async () => {
1005
- await distributedLock.releaseLock(this.context, [this.#eventType, this.#eventSubType].join("##"));
1012
+ await distributedLock.releaseLock(
1013
+ this.context,
1014
+ [this.#namespace, this.#eventType, this.#eventSubType].join("##")
1015
+ );
1006
1016
  });
1007
1017
  } catch (err) {
1008
1018
  this.logger.error("Releasing distributed lock failed.", err);
@@ -1035,6 +1045,7 @@ class EventQueueProcessorBase {
1035
1045
  const newEvent = {
1036
1046
  type: this.#eventType,
1037
1047
  subType: this.#eventSubType,
1048
+ namespace: this.#eventConfig.namespace,
1038
1049
  startAfter: new Date(newStartAfter),
1039
1050
  };
1040
1051
  const { relative } = this.#eventSchedulerInstance.calculateOffset(
@@ -1293,6 +1304,10 @@ class EventQueueProcessorBase {
1293
1304
  get inheritTraceContext() {
1294
1305
  return this.#eventConfig.inheritTraceContext;
1295
1306
  }
1307
+
1308
+ get namespace() {
1309
+ return this.#namespace;
1310
+ }
1296
1311
  }
1297
1312
 
1298
1313
  module.exports = EventQueueProcessorBase;
package/src/config.js CHANGED
@@ -4,18 +4,12 @@ const cds = require("@sap/cds");
4
4
  const { CronExpressionParser } = require("cron-parser");
5
5
 
6
6
  const { getEnvInstance } = require("./shared/env");
7
- const redis = require("./shared/redis");
8
7
  const EventQueueError = require("./EventQueueError");
9
8
  const { Priorities } = require("./constants");
10
9
 
11
10
  const FOR_UPDATE_TIMEOUT = 10;
12
11
  const GLOBAL_TX_TIMEOUT = 30 * 60 * 1000;
13
12
  const REDIS_PREFIX = "EVENT_QUEUE";
14
- const REDIS_CONFIG_CHANNEL = "CONFIG_CHANNEL";
15
- const REDIS_OFFBOARD_TENANT_CHANNEL = "REDIS_OFFBOARD_TENANT_CHANNEL";
16
- const REDIS_CONFIG_BLOCKLIST_CHANNEL = "REDIS_CONFIG_BLOCKLIST_CHANNEL";
17
- const COMMAND_BLOCK = "EVENT_BLOCK";
18
- const COMMAND_UNBLOCK = "EVENT_UNBLOCK";
19
13
  const COMPONENT_NAME = "/eventQueue/config";
20
14
  const MIN_INTERVAL_SEC = 10;
21
15
  const DEFAULT_LOAD = 1;
@@ -53,6 +47,7 @@ const ALLOWED_EVENT_OPTIONS_BASE = [
53
47
  "keepAliveMaxInProgressTime",
54
48
  "appNames",
55
49
  "appInstances",
50
+ "namespace",
56
51
  "internalEvent",
57
52
  ];
58
53
 
@@ -98,7 +93,6 @@ class Config {
98
93
  #env;
99
94
  #eventMap;
100
95
  #updatePeriodicEvents;
101
- #blockedEvents;
102
96
  #isEventBlockedCb;
103
97
  #thresholdLoggingEventProcessing;
104
98
  #useAsCAPOutbox;
@@ -112,7 +106,6 @@ class Config {
112
106
  #cronTimezone;
113
107
  #randomOffsetPeriodicEvents;
114
108
  #redisNamespace;
115
- #publishEventBlockList;
116
109
  #crashOnRedisUnavailable;
117
110
  #tenantIdFilterAuthContextCb;
118
111
  #tenantIdFilterEventProcessingCb;
@@ -120,6 +113,8 @@ class Config {
120
113
  #configPeriodicEvents;
121
114
  #enableAdminService;
122
115
  #disableProcessingOfSuspendedTenants;
116
+ #namespace;
117
+ #processingNamespaces;
123
118
  static #instance;
124
119
  constructor() {
125
120
  this.#logger = cds.log(COMPONENT_NAME);
@@ -137,12 +132,11 @@ class Config {
137
132
  this.#processEventsAfterPublish = null;
138
133
  this.#disableRedis = null;
139
134
  this.#env = getEnvInstance();
140
- this.#blockedEvents = {};
141
135
  }
142
136
 
143
- getEventConfig(type, subType) {
144
- return this.#eventMap[this.generateKey(type, subType)]
145
- ? { ...this.#eventMap[this.generateKey(type, subType)] }
137
+ getEventConfig(type, subType, namespace = this.namespace) {
138
+ return this.#eventMap[this.generateKey(namespace, type, subType)]
139
+ ? { ...this.#eventMap[this.generateKey(namespace, type, subType)] }
146
140
  : undefined;
147
141
  }
148
142
 
@@ -150,8 +144,8 @@ class Config {
150
144
  return type === CAP_EVENT_TYPE;
151
145
  }
152
146
 
153
- hasEventAfterCommitFlag(type, subType) {
154
- return this.#eventMap[this.generateKey(type, subType)]?.processAfterCommit ?? true;
147
+ hasEventAfterCommitFlag(type, subType, namespace = this.namespace) {
148
+ return this.#eventMap[this.generateKey(namespace, type, subType)]?.processAfterCommit ?? true;
155
149
  }
156
150
 
157
151
  _checkRedisIsBound() {
@@ -178,10 +172,10 @@ class Config {
178
172
  return actionSpecificCall ? rawSubType : serviceName;
179
173
  }
180
174
 
181
- shouldBeProcessedInThisApplication(type, rawSubType) {
175
+ shouldBeProcessedInThisApplication(type, rawSubType, namespace = this.namespace) {
182
176
  const subType = this.#normalizeSubType(rawSubType);
183
177
 
184
- const config = this.#eventMap[this.generateKey(type, subType)];
178
+ const config = this.#eventMap[this.generateKey(namespace, type, subType)];
185
179
  const appNameConfig = config._appNameMap;
186
180
  const appInstanceConfig = config._appInstancesMap;
187
181
  let result = true;
@@ -226,38 +220,6 @@ class Config {
226
220
  return this.#redisEnabled;
227
221
  }
228
222
 
229
- attachConfigChangeHandler() {
230
- this.#attachBlockListChangeHandler();
231
- redis.subscribeRedisChannel(this.redisOptions, REDIS_CONFIG_CHANNEL, (messageData) => {
232
- try {
233
- const { key, value } = JSON.parse(messageData);
234
- if (this[key] !== value) {
235
- this.#logger.info("received config change", { key, value });
236
- this[key] = value;
237
- }
238
- } catch (err) {
239
- this.#logger.error("could not parse event config change", err, {
240
- messageData,
241
- });
242
- }
243
- });
244
- }
245
-
246
- attachRedisUnsubscribeHandler() {
247
- this.#logger.info("attached redis handle for unsubscribe events");
248
- redis.subscribeRedisChannel(this.redisOptions, REDIS_OFFBOARD_TENANT_CHANNEL, (messageData) => {
249
- try {
250
- const { tenantId } = JSON.parse(messageData);
251
- this.#logger.info("received unsubscribe broadcast event", { tenantId });
252
- this.executeUnsubscribeHandlers(tenantId);
253
- } catch (err) {
254
- this.#logger.error("could not parse unsubscribe broadcast event", err, {
255
- messageData,
256
- });
257
- }
258
- });
259
- }
260
-
261
223
  executeUnsubscribeHandlers(tenantId) {
262
224
  this.#unsubscribedTenants[tenantId] = true;
263
225
  setTimeout(() => delete this.#unsubscribedTenants[tenantId], DELETE_TENANT_BLOCK_AFTER_MS);
@@ -272,122 +234,24 @@ class Config {
272
234
  }
273
235
  }
274
236
 
275
- handleUnsubscribe(tenantId) {
276
- if (this.redisEnabled) {
277
- redis
278
- .publishMessage(this.redisOptions, REDIS_OFFBOARD_TENANT_CHANNEL, JSON.stringify({ tenantId }))
279
- .catch((error) => {
280
- this.#logger.error(`publishing tenant unsubscribe failed. tenantId: ${tenantId}`, error);
281
- });
282
- } else {
283
- this.executeUnsubscribeHandlers(tenantId);
284
- }
285
- }
286
-
287
237
  attachUnsubscribeHandler(cb) {
288
238
  this.#unsubscribeHandlers.push(cb);
289
239
  }
290
240
 
291
- publishConfigChange(key, value) {
292
- if (!this.redisEnabled) {
293
- this.#logger.info("redis not connected, config change won't be published", { key, value });
294
- return;
295
- }
296
- redis.publishMessage(this.redisOptions, REDIS_CONFIG_CHANNEL, JSON.stringify({ key, value })).catch((error) => {
297
- this.#logger.error(`publishing config change failed key: ${key}, value: ${value}`, error);
298
- });
299
- }
300
-
301
- #attachBlockListChangeHandler() {
302
- redis.subscribeRedisChannel(this.redisOptions, REDIS_CONFIG_BLOCKLIST_CHANNEL, (messageData) => {
303
- try {
304
- const { command, key, tenant } = JSON.parse(messageData);
305
- if (command === COMMAND_BLOCK) {
306
- this.#blockEventLocalState(key, tenant);
307
- } else {
308
- this.#unblockEventLocalState(key, tenant);
309
- }
310
- } catch (err) {
311
- this.#logger.error("could not parse event blocklist change", err, {
312
- messageData,
313
- });
314
- }
315
- });
316
- }
317
-
318
- blockEvent(type, subType, isPeriodic, tenant = "*") {
319
- const typeWithSuffix = `${type}${isPeriodic ? SUFFIX_PERIODIC : ""}`;
320
- const config = this.getEventConfig(typeWithSuffix, subType);
321
- if (!config) {
322
- return;
323
- }
324
- const key = this.generateKey(typeWithSuffix, subType);
325
- this.#blockEventLocalState(key, tenant);
326
- if (!this.redisEnabled || !this.publishEventBlockList) {
327
- return;
328
- }
329
-
330
- redis
331
- .publishMessage(
332
- this.redisOptions,
333
- REDIS_CONFIG_BLOCKLIST_CHANNEL,
334
- JSON.stringify({ command: COMMAND_BLOCK, key, tenant })
335
- )
336
- .catch((error) => {
337
- this.#logger.error(`publishing config block failed key: ${key}`, error);
338
- });
339
- }
340
-
341
- #blockEventLocalState(key, tenant) {
342
- this.#blockedEvents[key] ??= {};
343
- this.#blockedEvents[key][tenant] = true;
344
- return key;
345
- }
346
-
347
- clearPeriodicEventBlockList() {
348
- this.#blockedEvents = {};
349
- }
350
-
351
- unblockEvent(type, subType, isPeriodic, tenant = "*") {
352
- const typeWithSuffix = `${type}${isPeriodic ? SUFFIX_PERIODIC : ""}`;
353
- const key = this.generateKey(typeWithSuffix, subType);
354
- const config = this.getEventConfig(typeWithSuffix, subType);
355
- if (!config) {
356
- return;
357
- }
358
- this.#unblockEventLocalState(key, tenant);
359
- if (!this.redisEnabled) {
360
- return;
361
- }
362
-
363
- redis
364
- .publishMessage(
365
- this.redisOptions,
366
- REDIS_CONFIG_BLOCKLIST_CHANNEL,
367
- JSON.stringify({ command: COMMAND_UNBLOCK, key, tenant })
368
- )
369
- .catch((error) => {
370
- this.#logger.error(`publishing config block failed key: ${key}`, error);
371
- });
372
- }
373
-
374
241
  addCAPOutboxEventBase(serviceName, config) {
375
- if (this.#eventMap[this.generateKey(CAP_EVENT_TYPE, serviceName)]) {
376
- const index = this.#config.events.findIndex(
377
- (event) => event.type === CAP_EVENT_TYPE && event.subType === serviceName
378
- );
379
- this.#config.events.splice(index, 1);
380
- }
242
+ const namespace = config.namespace ?? this.#namespace;
243
+ delete this.#eventMap[this.generateKey(namespace, CAP_EVENT_TYPE, serviceName)];
381
244
 
382
- // NOTE: CAP outbox defaults are injected by cds.requires.outbox
245
+ // NOTE: CAP outbox defaults are injected by cds.requires.outbox // cds.requires.queue
383
246
  const eventConfig = this.#sanitizeParamsAdHocEvent({
384
247
  type: CAP_EVENT_TYPE,
385
248
  subType: serviceName,
386
249
  impl: "./outbox/EventQueueGenericOutboxHandler",
387
- kind: config.kind ?? "persistent-outbox",
250
+ kind: config.kind ?? "persistent-queue",
388
251
  selectMaxChunkSize: config.selectMaxChunkSize ?? config.chunkSize,
389
252
  parallelEventProcessing: config.parallelEventProcessing ?? (config.parallel && CAP_PARALLEL_DEFAULT),
390
253
  retryAttempts: config.retryAttempts ?? config.maxAttempts ?? CAP_MAX_ATTEMPTS_DEFAULT,
254
+ namespace,
391
255
  ...config,
392
256
  });
393
257
  eventConfig.internalEvent = true;
@@ -395,21 +259,20 @@ class Config {
395
259
  this.#basicEventTransformation(eventConfig);
396
260
  this.#validateAdHocEvents(this.#eventMap, eventConfig, false);
397
261
  this.#basicEventTransformationAfterValidate(eventConfig);
398
- this.#config.events.push(eventConfig);
399
- this.#eventMap[this.generateKey(CAP_EVENT_TYPE, serviceName)] = eventConfig;
262
+ this.#eventMap[this.generateKey(namespace, CAP_EVENT_TYPE, serviceName)] = eventConfig;
400
263
  }
401
264
 
402
265
  addCAPOutboxEventSpecificAction(serviceName, actionName) {
403
266
  const subType = [serviceName, actionName].join(".");
404
- if (this.#eventMap[this.generateKey(CAP_EVENT_TYPE, subType)]) {
405
- const index = this.#config.events.findIndex(
406
- (event) => event.type === CAP_EVENT_TYPE && event.subType === serviceName
407
- );
408
- this.#config.events.splice(index, 1);
267
+
268
+ const specific = this.#findBaseCAPServiceWithoutNamespace(subType);
269
+ if (specific) {
270
+ delete this.#eventMap[this.generateKey(specific.namespace, CAP_EVENT_TYPE, subType)];
409
271
  }
410
272
 
273
+ const base = this.#findBaseCAPServiceWithoutNamespace(serviceName);
411
274
  const eventConfig = this.#sanitizeParamsAdHocEvent({
412
- ...this.getEventConfig(CAP_EVENT_TYPE, serviceName),
275
+ ...this.getEventConfig(CAP_EVENT_TYPE, serviceName, base.namespace),
413
276
  ...this.getCdsOutboxEventSpecificConfig(serviceName, actionName),
414
277
  subType,
415
278
  });
@@ -418,28 +281,14 @@ class Config {
418
281
  this.#basicEventTransformation(eventConfig);
419
282
  this.#validateAdHocEvents(this.#eventMap, eventConfig, false);
420
283
  this.#basicEventTransformationAfterValidate(eventConfig);
421
- this.#config.events.push(eventConfig);
422
- this.#eventMap[this.generateKey(CAP_EVENT_TYPE, subType)] = eventConfig;
284
+ this.#eventMap[this.generateKey(eventConfig.namespace, CAP_EVENT_TYPE, subType)] = eventConfig;
423
285
  return eventConfig;
424
286
  }
425
287
 
426
- #unblockEventLocalState(key, tenant) {
427
- const map = this.#blockedEvents[key];
428
- if (!map) {
429
- return;
430
- }
431
- this.#blockedEvents[key][tenant] = false;
432
- return key;
433
- }
434
-
435
- isEventBlocked(type, subType, isPeriodicEvent, tenant) {
436
- const map = this.#blockedEvents[this.generateKey(`${type}${isPeriodicEvent ? SUFFIX_PERIODIC : ""}`, subType)];
437
- if (!map) {
438
- return false;
439
- }
440
- const tenantSpecific = map[tenant];
441
- const allTenants = map["*"];
442
- return tenantSpecific ?? allTenants;
288
+ #findBaseCAPServiceWithoutNamespace(serviceName) {
289
+ return Object.values(this.#eventMap).find(
290
+ (event) => event.type === CAP_EVENT_TYPE && event.subType === serviceName
291
+ );
443
292
  }
444
293
 
445
294
  get isEventQueueActive() {
@@ -469,10 +318,11 @@ class Config {
469
318
 
470
319
  #cdsPeriodicOutboxServicesFromEnv() {
471
320
  return Object.entries(cds.env.requires).reduce((result, [name, value]) => {
472
- if (value.outbox?.events) {
473
- for (const fnName in value.outbox.events) {
474
- const base = { ...value.outbox };
475
- const fnConfig = value.outbox.events[fnName];
321
+ const config = value.outbox ?? value.queued;
322
+ if (config?.events) {
323
+ for (const fnName in config.events) {
324
+ const base = { ...config };
325
+ const fnConfig = config.events[fnName];
476
326
  if (fnConfig.interval || fnConfig.cron) {
477
327
  if ("interval" in base || "cron" in base) {
478
328
  this.#logger.error(
@@ -502,8 +352,10 @@ class Config {
502
352
  }
503
353
 
504
354
  getCdsOutboxEventSpecificConfig(serviceName, action) {
505
- if (cds.env.requires[serviceName]?.outbox?.events?.[action]) {
506
- return cds.env.requires[serviceName].outbox.events[action];
355
+ const srv = cds.env.requires[serviceName];
356
+ const config = srv?.outbox ?? srv?.queued;
357
+ if (config?.events?.[action]) {
358
+ return config.events[action];
507
359
  } else {
508
360
  return null;
509
361
  }
@@ -531,7 +383,7 @@ class Config {
531
383
  this.#basicEventTransformation(eventSanitized);
532
384
  this.#validateAdHocEvents(result, eventSanitized);
533
385
  this.#basicEventTransformationAfterValidate(eventSanitized);
534
- result[this.generateKey(eventSanitized.type, eventSanitized.subType)] = eventSanitized;
386
+ result[this.generateKey(eventSanitized.namespace, eventSanitized.type, eventSanitized.subType)] = eventSanitized;
535
387
  return result;
536
388
  }, {});
537
389
  this.#eventMap = config.periodicEvents.reduce((result, event) => {
@@ -541,7 +393,7 @@ class Config {
541
393
  this.#basicEventTransformation(eventSanitized);
542
394
  this.#validatePeriodicConfig(result, eventSanitized);
543
395
  this.#basicEventTransformationAfterValidate(eventSanitized);
544
- result[this.generateKey(eventSanitized.type, eventSanitized.subType)] = eventSanitized;
396
+ result[this.generateKey(eventSanitized.namespace, eventSanitized.type, eventSanitized.subType)] = eventSanitized;
545
397
  return result;
546
398
  }, this.#eventMap);
547
399
  this.#config = config;
@@ -554,6 +406,7 @@ class Config {
554
406
  event.keepAliveInterval = event.keepAliveInterval ?? DEFAULT_KEEP_ALIVE_INTERVAL;
555
407
  event.keepAliveMaxInProgressTime = event.keepAliveInterval * DEFAULT_MAX_FACTOR_STUCK_2_KEEP_ALIVE_INTERVAL;
556
408
  event.checkForNextChunk = event.checkForNextChunk ?? DEFAULT_CHECK_FOR_NEXT_CHUNK;
409
+ event.namespace = event.namespace === undefined ? this.#namespace : event.namespace;
557
410
  }
558
411
 
559
412
  #sanitizeParamsBase(config, allowList) {
@@ -623,7 +476,7 @@ class Config {
623
476
  }
624
477
 
625
478
  #validatePeriodicConfig(eventMap, event) {
626
- const key = this.generateKey(event.type, event.subType);
479
+ const key = this.generateKey(event.namespace, event.type, event.subType);
627
480
  if (eventMap[key] && eventMap[key].isPeriodic) {
628
481
  throw EventQueueError.duplicateEventRegistration(event.type, event.subType);
629
482
  }
@@ -674,7 +527,7 @@ class Config {
674
527
  }
675
528
 
676
529
  #validateAdHocEvents(eventMap, event, checkForDuplication = true) {
677
- const key = this.generateKey(event.type, event.subType);
530
+ const key = this.generateKey(event.namespace, event.type, event.subType);
678
531
  if (eventMap[key] && !eventMap[key].isPeriodic && checkForDuplication) {
679
532
  throw EventQueueError.duplicateEventRegistration(event.type, event.subType);
680
533
  }
@@ -695,22 +548,22 @@ class Config {
695
548
  this.#basicEventValidation(event);
696
549
  }
697
550
 
698
- generateKey(type, subType) {
699
- return [type, subType].join("##");
551
+ generateKey(namespace, type, subType) {
552
+ return [namespace, type, subType].join("##");
700
553
  }
701
554
 
702
- removeEvent(type, subType) {
703
- const index = this.#config.events.findIndex((event) => event.type === "CAP_OUTBOX");
704
- if (index >= 0) {
705
- this.#config.events.splice(index, 1);
706
- }
707
- delete this.#eventMap[this.generateKey(type, subType)];
555
+ removeEvent(type, subType, namespace = this.namespace) {
556
+ delete this.#eventMap[this.generateKey(namespace, type, subType)];
708
557
  }
709
558
 
710
559
  isTenantUnsubscribed(tenantId) {
711
560
  return this.#unsubscribedTenants[tenantId];
712
561
  }
713
562
 
563
+ shouldProcessNamespace(namespace) {
564
+ return this.#processingNamespaces.includes(namespace);
565
+ }
566
+
714
567
  get fileContent() {
715
568
  return this.#config;
716
569
  }
@@ -735,8 +588,8 @@ class Config {
735
588
  return Object.values(this.#eventMap).filter((e) => e.isPeriodic);
736
589
  }
737
590
 
738
- isPeriodicEvent(type, subType) {
739
- return this.#eventMap[this.generateKey(type, subType)]?.isPeriodic;
591
+ isPeriodicEvent(type, subType, namespace = this.namespace) {
592
+ return this.#eventMap[this.generateKey(namespace, type, subType)]?.isPeriodic;
740
593
  }
741
594
 
742
595
  get allEvents() {
@@ -755,14 +608,6 @@ class Config {
755
608
  this.#forUpdateTimeout = value;
756
609
  }
757
610
 
758
- get publishEventBlockList() {
759
- return this.#publishEventBlockList;
760
- }
761
-
762
- set publishEventBlockList(value) {
763
- this.#publishEventBlockList = value;
764
- }
765
-
766
611
  get crashOnRedisUnavailable() {
767
612
  return this.#crashOnRedisUnavailable;
768
613
  }
@@ -937,7 +782,6 @@ class Config {
937
782
  get redisOptions() {
938
783
  return {
939
784
  ...this.#redisOptions,
940
- redisNamespace: `${[REDIS_PREFIX, this.redisNamespace].filter((a) => a).join("_")}`,
941
785
  };
942
786
  }
943
787
 
@@ -946,7 +790,7 @@ class Config {
946
790
  }
947
791
 
948
792
  get redisNamespace() {
949
- return this.#redisNamespace;
793
+ return `${[REDIS_PREFIX, this.#redisNamespace].filter((a) => a).join("##")}`;
950
794
  }
951
795
 
952
796
  set insertEventsBeforeCommit(value) {
@@ -993,6 +837,22 @@ class Config {
993
837
  this.#disableProcessingOfSuspendedTenants = value;
994
838
  }
995
839
 
840
+ get namespace() {
841
+ return this.#namespace;
842
+ }
843
+
844
+ set namespace(value) {
845
+ this.#namespace = value;
846
+ }
847
+
848
+ get processingNamespaces() {
849
+ return this.#processingNamespaces;
850
+ }
851
+
852
+ set processingNamespaces(value) {
853
+ this.#processingNamespaces = value;
854
+ }
855
+
996
856
  /**
997
857
  @return { Config }
998
858
  **/
package/src/dbHandler.js CHANGED
@@ -28,8 +28,11 @@ const registerEventQueueDbHandler = (dbService) => {
28
28
  const data = Array.isArray(req.query.INSERT.entries) ? req.query.INSERT.entries : [req.query.INSERT.entries];
29
29
  const eventCombinations = Object.keys(
30
30
  data.reduce((result, event) => {
31
- const key = [event.type, event.subType].join("##");
32
- if (!config.hasEventAfterCommitFlag(event.type, event.subType) || eventQueuePublishEvents[key]) {
31
+ const key = [event.type, event.subType, event.namespace].join("##");
32
+ if (
33
+ !config.hasEventAfterCommitFlag(event.type, event.subType, event.namespace) ||
34
+ eventQueuePublishEvents[key]
35
+ ) {
33
36
  return result;
34
37
  }
35
38
  eventQueuePublishEvents[key] = true;
@@ -41,8 +44,8 @@ const registerEventQueueDbHandler = (dbService) => {
41
44
  eventCombinations.length &&
42
45
  req.on("succeeded", () => {
43
46
  const events = eventCombinations.map((eventCombination) => {
44
- const [type, subType] = eventCombination.split("##");
45
- return { type, subType };
47
+ const [type, subType, namespace] = eventCombination.split("##");
48
+ return { type, subType, namespace };
46
49
  });
47
50
 
48
51
  redisPub.broadcastEvent(req.tenant, events).catch((err) => {