@cap-js-community/event-queue 0.1.49 → 0.1.51
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 -2
- package/cds-plugin.js +1 -1
- package/package.json +9 -2
- package/src/EventQueueError.js +5 -10
- package/src/EventQueueProcessorBase.js +315 -343
- package/src/config.js +1 -4
- package/src/constants.js +5 -0
- package/src/dbHandler.js +1 -4
- package/src/initialize.js +11 -39
- package/src/processEventQueue.js +65 -154
- package/src/publishEvent.js +1 -3
- package/src/redisPubSub.js +6 -20
- package/src/runner.js +40 -78
- package/src/shared/PerformanceTracer.js +1 -3
- package/src/shared/SetIntervalDriftSafe.js +37 -0
- package/src/shared/WorkerQueue.js +3 -14
- package/src/shared/cdsHelper.js +2 -11
- package/src/shared/common.js +2 -7
- package/src/shared/distributedLock.js +41 -85
- package/src/shared/redis.js +1 -3
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
const cds = require("@sap/cds");
|
|
4
4
|
|
|
5
|
-
const { executeInNewTransaction } = require("./shared/cdsHelper");
|
|
6
|
-
const { EventProcessingStatus } = require("./constants");
|
|
5
|
+
const { executeInNewTransaction, TriggerRollback } = require("./shared/cdsHelper");
|
|
6
|
+
const { EventProcessingStatus, TransactionMode } = require("./constants");
|
|
7
7
|
const distributedLock = require("./shared/distributedLock");
|
|
8
8
|
const EventQueueError = require("./EventQueueError");
|
|
9
9
|
const { arrayToFlatMap } = require("./shared/common");
|
|
@@ -17,8 +17,15 @@ const DEFAULT_RETRY_ATTEMPTS = 3;
|
|
|
17
17
|
const DEFAULT_PARALLEL_EVENT_PROCESSING = 1;
|
|
18
18
|
const LIMIT_PARALLEL_EVENT_PROCESSING = 10;
|
|
19
19
|
const SELECT_LIMIT_EVENTS_PER_TICK = 100;
|
|
20
|
+
const DEFAULT_DELETE_FINISHED_EVENTS_AFTER = 0;
|
|
21
|
+
const DAYS_TO_MS = 24 * 60 * 60 * 1000;
|
|
22
|
+
const TRIES_FOR_EXCEEDED_EVENTS = 3;
|
|
20
23
|
|
|
21
24
|
class EventQueueProcessorBase {
|
|
25
|
+
#eventsWithExceededTries = [];
|
|
26
|
+
#exceededTriesExceeded = [];
|
|
27
|
+
#selectedEventMap = {};
|
|
28
|
+
|
|
22
29
|
constructor(context, eventType, eventSubType, config) {
|
|
23
30
|
this.__context = context;
|
|
24
31
|
this.__baseContext = context;
|
|
@@ -32,30 +39,34 @@ class EventQueueProcessorBase {
|
|
|
32
39
|
this.__eventSubType = eventSubType;
|
|
33
40
|
this.__queueEntriesWithPayloadMap = {};
|
|
34
41
|
this.__config = config ?? {};
|
|
35
|
-
this.__parallelEventProcessing =
|
|
36
|
-
this.__config.parallelEventProcessing ??
|
|
37
|
-
DEFAULT_PARALLEL_EVENT_PROCESSING;
|
|
42
|
+
this.__parallelEventProcessing = this.__config.parallelEventProcessing ?? DEFAULT_PARALLEL_EVENT_PROCESSING;
|
|
38
43
|
if (this.__parallelEventProcessing > LIMIT_PARALLEL_EVENT_PROCESSING) {
|
|
39
44
|
this.__parallelEventProcessing = LIMIT_PARALLEL_EVENT_PROCESSING;
|
|
40
45
|
}
|
|
41
46
|
// NOTE: keep the feature, this might be needed again
|
|
42
47
|
this.__concurrentEventProcessing = false;
|
|
43
48
|
this.__startTime = this.__config.startTime ?? new Date();
|
|
44
|
-
this.__retryAttempts =
|
|
45
|
-
|
|
46
|
-
this.__selectMaxChunkSize =
|
|
47
|
-
this.__config.selectMaxChunkSize ?? SELECT_LIMIT_EVENTS_PER_TICK;
|
|
49
|
+
this.__retryAttempts = this.__config.retryAttempts ?? DEFAULT_RETRY_ATTEMPTS;
|
|
50
|
+
this.__selectMaxChunkSize = this.__config.selectMaxChunkSize ?? SELECT_LIMIT_EVENTS_PER_TICK;
|
|
48
51
|
this.__selectNextChunk = !!this.__config.checkForNextChunk;
|
|
49
52
|
this.__keepalivePromises = {};
|
|
50
53
|
this.__outdatedCheckEnabled = this.__config.eventOutdatedCheck ?? true;
|
|
51
|
-
this.
|
|
52
|
-
this.
|
|
54
|
+
this.__transactionMode = this.__config.transactionMode ?? TransactionMode.isolated;
|
|
55
|
+
if (this.__config.deleteFinishedEventsAfterDays) {
|
|
56
|
+
this.__deleteFinishedEventsAfter =
|
|
57
|
+
Number.isInteger(this.__config.deleteFinishedEventsAfterDays) && this.__config.deleteFinishedEventsAfterDays > 0
|
|
58
|
+
? this.__config.deleteFinishedEventsAfterDays
|
|
59
|
+
: DEFAULT_DELETE_FINISHED_EVENTS_AFTER;
|
|
60
|
+
} else {
|
|
61
|
+
this.__deleteFinishedEventsAfter = DEFAULT_DELETE_FINISHED_EVENTS_AFTER;
|
|
62
|
+
}
|
|
53
63
|
this.__emptyChunkSelected = false;
|
|
54
64
|
this.__lockAcquired = false;
|
|
55
65
|
this.__txUsageAllowed = true;
|
|
56
66
|
this.__txMap = {};
|
|
57
67
|
this.__txRollback = {};
|
|
58
68
|
this.__eventQueueConfig = eventQueueConfig.getConfigInstance();
|
|
69
|
+
this.__queueEntries = [];
|
|
59
70
|
}
|
|
60
71
|
|
|
61
72
|
/**
|
|
@@ -77,17 +88,11 @@ class EventQueueProcessorBase {
|
|
|
77
88
|
}
|
|
78
89
|
|
|
79
90
|
startPerformanceTracerEvents() {
|
|
80
|
-
this.__performanceLoggerEvents = new PerformanceTracer(
|
|
81
|
-
this.logger,
|
|
82
|
-
"Processing events"
|
|
83
|
-
);
|
|
91
|
+
this.__performanceLoggerEvents = new PerformanceTracer(this.logger, "Processing events");
|
|
84
92
|
}
|
|
85
93
|
|
|
86
94
|
startPerformanceTracerPreprocessing() {
|
|
87
|
-
this.__performanceLoggerPreprocessing = new PerformanceTracer(
|
|
88
|
-
this.logger,
|
|
89
|
-
"Preprocessing events"
|
|
90
|
-
);
|
|
95
|
+
this.__performanceLoggerPreprocessing = new PerformanceTracer(this.logger, "Preprocessing events");
|
|
91
96
|
}
|
|
92
97
|
|
|
93
98
|
endPerformanceTracerEvents() {
|
|
@@ -175,10 +180,7 @@ class EventQueueProcessorBase {
|
|
|
175
180
|
eventType: this.__eventType,
|
|
176
181
|
eventSubType: this.__eventSubType,
|
|
177
182
|
});
|
|
178
|
-
this.
|
|
179
|
-
queueEntry.ID,
|
|
180
|
-
EventProcessingStatus.Done
|
|
181
|
-
);
|
|
183
|
+
this.#determineAndAddEventStatusToMap(queueEntry.ID, EventProcessingStatus.Done);
|
|
182
184
|
}
|
|
183
185
|
|
|
184
186
|
/**
|
|
@@ -189,14 +191,19 @@ class EventQueueProcessorBase {
|
|
|
189
191
|
* In this case the events should be clustered together and only one mail should be sent.
|
|
190
192
|
*/
|
|
191
193
|
clusterQueueEntries() {
|
|
192
|
-
Object.entries(this.__queueEntriesWithPayloadMap).forEach(
|
|
193
|
-
(
|
|
194
|
-
|
|
195
|
-
}
|
|
196
|
-
);
|
|
194
|
+
Object.entries(this.__queueEntriesWithPayloadMap).forEach(([key, { queueEntry, payload }]) => {
|
|
195
|
+
this.addEntryToProcessingMap(key, queueEntry, payload);
|
|
196
|
+
});
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
|
|
199
|
+
/**
|
|
200
|
+
* This function allows to add entries to the process map. This function is needed if the function clusterQueueEntries
|
|
201
|
+
* is redefined. For each entry in the processing map the processEvent function will be called once.
|
|
202
|
+
* @param {String} key key for event
|
|
203
|
+
* @param {Object} queueEntry queueEntry which should be clustered with this key
|
|
204
|
+
* @param {Object} payload payload which should be clustered with this key
|
|
205
|
+
*/
|
|
206
|
+
addEntryToProcessingMap(key, queueEntry, payload) {
|
|
200
207
|
this.logger.debug("add entry to processing map", {
|
|
201
208
|
key,
|
|
202
209
|
queueEntry,
|
|
@@ -212,33 +219,28 @@ class EventQueueProcessorBase {
|
|
|
212
219
|
|
|
213
220
|
/**
|
|
214
221
|
* This function sets the status of multiple events to a given status. If the structure of queueEntryProcessingStatusTuple
|
|
215
|
-
* is not as expected all events will be set to error. The function respects the config
|
|
216
|
-
*
|
|
222
|
+
* is not as expected all events will be set to error. The function respects the config transactionMode. If
|
|
223
|
+
* transactionMode is isolated the status will be written to a dedicated map and returned afterwards to handle concurrent
|
|
217
224
|
* event processing.
|
|
218
225
|
* @param {Array} queueEntries which has been selected from event queue table and been modified by modifyQueueEntry
|
|
219
226
|
* @param {Array<Object>} queueEntryProcessingStatusTuple Array of tuple <queueEntryId, processingStatus>
|
|
227
|
+
* @param {boolean} returnMap Allows the function to allow the result as map
|
|
220
228
|
* @return {Object} statusMap Map which contains all events for which a status has been set so far
|
|
221
229
|
*/
|
|
222
|
-
setEventStatus(queueEntries, queueEntryProcessingStatusTuple) {
|
|
230
|
+
setEventStatus(queueEntries, queueEntryProcessingStatusTuple, returnMap = false) {
|
|
223
231
|
this.logger.debug("setting event status for entries", {
|
|
224
|
-
queueEntryProcessingStatusTuple: JSON.stringify(
|
|
225
|
-
queueEntryProcessingStatusTuple
|
|
226
|
-
),
|
|
232
|
+
queueEntryProcessingStatusTuple: JSON.stringify(queueEntryProcessingStatusTuple),
|
|
227
233
|
eventType: this.__eventType,
|
|
228
234
|
eventSubType: this.__eventSubType,
|
|
229
235
|
});
|
|
230
|
-
const statusMap = this.
|
|
236
|
+
const statusMap = this.commitOnEventLevel || returnMap ? {} : this.__statusMap;
|
|
231
237
|
try {
|
|
232
238
|
queueEntryProcessingStatusTuple.forEach(([id, processingStatus]) =>
|
|
233
|
-
this
|
|
239
|
+
this.#determineAndAddEventStatusToMap(id, processingStatus, statusMap)
|
|
234
240
|
);
|
|
235
241
|
} catch (error) {
|
|
236
242
|
queueEntries.forEach((queueEntry) =>
|
|
237
|
-
this.
|
|
238
|
-
queueEntry.ID,
|
|
239
|
-
EventProcessingStatus.Error,
|
|
240
|
-
statusMap
|
|
241
|
-
)
|
|
243
|
+
this.#determineAndAddEventStatusToMap(queueEntry.ID, EventProcessingStatus.Error, statusMap)
|
|
242
244
|
);
|
|
243
245
|
this.logger.error(
|
|
244
246
|
`The supplied status tuple doesn't have the required structure. Setting all entries to error. Error: ${error.toString()}`,
|
|
@@ -261,24 +263,16 @@ class EventQueueProcessorBase {
|
|
|
261
263
|
try {
|
|
262
264
|
queueEntry.payload = JSON.parse(queueEntry.payload);
|
|
263
265
|
} catch {
|
|
264
|
-
|
|
266
|
+
/* empty */
|
|
265
267
|
}
|
|
266
268
|
}
|
|
267
269
|
|
|
268
|
-
|
|
269
|
-
id,
|
|
270
|
-
processingStatus,
|
|
271
|
-
statusMap = this.__statusMap
|
|
272
|
-
) {
|
|
270
|
+
#determineAndAddEventStatusToMap(id, processingStatus, statusMap = this.__statusMap) {
|
|
273
271
|
if (!statusMap[id]) {
|
|
274
272
|
statusMap[id] = processingStatus;
|
|
275
273
|
return;
|
|
276
274
|
}
|
|
277
|
-
if (
|
|
278
|
-
[EventProcessingStatus.Error, EventProcessingStatus.Exceeded].includes(
|
|
279
|
-
statusMap[id]
|
|
280
|
-
)
|
|
281
|
-
) {
|
|
275
|
+
if ([EventProcessingStatus.Error, EventProcessingStatus.Exceeded].includes(statusMap[id])) {
|
|
282
276
|
// NOTE: worst aggregation --> if already error|exceeded keep this state
|
|
283
277
|
return;
|
|
284
278
|
}
|
|
@@ -298,17 +292,9 @@ class EventQueueProcessorBase {
|
|
|
298
292
|
}
|
|
299
293
|
);
|
|
300
294
|
queueEntries.forEach((queueEntry) =>
|
|
301
|
-
this.
|
|
302
|
-
queueEntry.ID,
|
|
303
|
-
EventProcessingStatus.Error
|
|
304
|
-
)
|
|
305
|
-
);
|
|
306
|
-
return Object.fromEntries(
|
|
307
|
-
queueEntries.map((queueEntry) => [
|
|
308
|
-
queueEntry.ID,
|
|
309
|
-
EventProcessingStatus.Error,
|
|
310
|
-
])
|
|
295
|
+
this.#determineAndAddEventStatusToMap(queueEntry.ID, EventProcessingStatus.Error)
|
|
311
296
|
);
|
|
297
|
+
return Object.fromEntries(queueEntries.map((queueEntry) => [queueEntry.ID, EventProcessingStatus.Error]));
|
|
312
298
|
}
|
|
313
299
|
|
|
314
300
|
/**
|
|
@@ -316,23 +302,18 @@ class EventQueueProcessorBase {
|
|
|
316
302
|
* selected events a status has been submitted. Persisting the status of events is done in a dedicated database tx.
|
|
317
303
|
* The function accepts no arguments as there are dedicated functions to set the status of events (e.g. setEventStatus)
|
|
318
304
|
*/
|
|
319
|
-
async persistEventStatus(
|
|
320
|
-
tx,
|
|
321
|
-
{ skipChecks, statusMap = this.__statusMap } = {}
|
|
322
|
-
) {
|
|
305
|
+
async persistEventStatus(tx, { skipChecks, statusMap = this.__statusMap } = {}) {
|
|
323
306
|
this.logger.debug("entering persistEventStatus", {
|
|
324
307
|
eventType: this.__eventType,
|
|
325
308
|
eventSubType: this.__eventSubType,
|
|
326
309
|
});
|
|
327
|
-
this
|
|
310
|
+
this.#ensureOnlySelectedQueueEntries(statusMap);
|
|
328
311
|
if (!skipChecks) {
|
|
329
|
-
this
|
|
312
|
+
this.#ensureEveryQueueEntryHasStatus();
|
|
330
313
|
}
|
|
331
|
-
this
|
|
314
|
+
this.#ensureEveryStatusIsAllowed(statusMap);
|
|
332
315
|
|
|
333
|
-
const { success, failed, exceeded, invalidAttempts } = Object.entries(
|
|
334
|
-
statusMap
|
|
335
|
-
).reduce(
|
|
316
|
+
const { success, failed, exceeded, invalidAttempts } = Object.entries(statusMap).reduce(
|
|
336
317
|
(result, [notificationEntityId, processingStatus]) => {
|
|
337
318
|
this.__commitedStatusMap[notificationEntityId] = processingStatus;
|
|
338
319
|
if (processingStatus === EventProcessingStatus.Open) {
|
|
@@ -372,34 +353,24 @@ class EventQueueProcessorBase {
|
|
|
372
353
|
.where("ID IN", invalidAttempts)
|
|
373
354
|
);
|
|
374
355
|
}
|
|
375
|
-
|
|
356
|
+
const ts = new Date().toISOString();
|
|
357
|
+
const updateTuples = [
|
|
358
|
+
[success, EventProcessingStatus.Done],
|
|
359
|
+
[failed, EventProcessingStatus.Error],
|
|
360
|
+
[exceeded, EventProcessingStatus.Exceeded],
|
|
361
|
+
];
|
|
362
|
+
|
|
363
|
+
for (const [eventIds, status] of updateTuples) {
|
|
364
|
+
if (!eventIds.length) {
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
376
367
|
await tx.run(
|
|
377
368
|
UPDATE.entity(this.__eventQueueConfig.tableNameEventQueue)
|
|
378
369
|
.set({
|
|
379
|
-
status:
|
|
380
|
-
lastAttemptTimestamp:
|
|
381
|
-
})
|
|
382
|
-
.where("ID IN", success)
|
|
383
|
-
);
|
|
384
|
-
}
|
|
385
|
-
if (failed.length) {
|
|
386
|
-
await tx.run(
|
|
387
|
-
UPDATE.entity(this.__eventQueueConfig.tableNameEventQueue)
|
|
388
|
-
.where("ID IN", failed)
|
|
389
|
-
.with({
|
|
390
|
-
status: EventProcessingStatus.Error,
|
|
391
|
-
lastAttemptTimestamp: new Date().toISOString(),
|
|
392
|
-
})
|
|
393
|
-
);
|
|
394
|
-
}
|
|
395
|
-
if (exceeded.length) {
|
|
396
|
-
await tx.run(
|
|
397
|
-
UPDATE.entity(this.__eventQueueConfig.tableNameEventQueue)
|
|
398
|
-
.where("ID IN", exceeded)
|
|
399
|
-
.with({
|
|
400
|
-
status: EventProcessingStatus.Exceeded,
|
|
401
|
-
lastAttemptTimestamp: new Date().toISOString(),
|
|
370
|
+
status: status,
|
|
371
|
+
lastAttemptTimestamp: ts,
|
|
402
372
|
})
|
|
373
|
+
.where("ID IN", eventIds)
|
|
403
374
|
);
|
|
404
375
|
}
|
|
405
376
|
this.logger.debug("exiting persistEventStatus", {
|
|
@@ -408,30 +379,43 @@ class EventQueueProcessorBase {
|
|
|
408
379
|
});
|
|
409
380
|
}
|
|
410
381
|
|
|
411
|
-
|
|
382
|
+
async deleteFinishedEvents(tx) {
|
|
383
|
+
if (!this.__deleteFinishedEventsAfter) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
const deleteCount = await tx.run(
|
|
387
|
+
DELETE.from(this.__eventQueueConfig.tableNameEventQueue).where(
|
|
388
|
+
"type =",
|
|
389
|
+
this.eventType,
|
|
390
|
+
"AND subType=",
|
|
391
|
+
this.eventSubType,
|
|
392
|
+
"AND lastAttemptTimestamp <=",
|
|
393
|
+
new Date(Date.now() - this.__deleteFinishedEventsAfter * DAYS_TO_MS).toISOString()
|
|
394
|
+
)
|
|
395
|
+
);
|
|
396
|
+
this.logger.debug("Deleted finished events", {
|
|
397
|
+
eventType: this.eventType,
|
|
398
|
+
eventSubType: this.eventSubType,
|
|
399
|
+
deleteFinishedEventsAfter: this.__deleteFinishedEventsAfter,
|
|
400
|
+
deleteCount,
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
#ensureEveryQueueEntryHasStatus() {
|
|
412
405
|
this.__queueEntries.forEach((queueEntry) => {
|
|
413
|
-
if (
|
|
414
|
-
queueEntry.ID in this.__statusMap ||
|
|
415
|
-
queueEntry.ID in this.__commitedStatusMap
|
|
416
|
-
) {
|
|
406
|
+
if (queueEntry.ID in this.__statusMap || queueEntry.ID in this.__commitedStatusMap) {
|
|
417
407
|
return;
|
|
418
408
|
}
|
|
419
|
-
this.logger.error(
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
}
|
|
426
|
-
);
|
|
427
|
-
this._determineAndAddEventStatusToMap(
|
|
428
|
-
queueEntry.ID,
|
|
429
|
-
EventProcessingStatus.Error
|
|
430
|
-
);
|
|
409
|
+
this.logger.error("Missing status for selected event entry. Setting status to error", {
|
|
410
|
+
eventType: this.__eventType,
|
|
411
|
+
eventSubType: this.__eventSubType,
|
|
412
|
+
queueEntry,
|
|
413
|
+
});
|
|
414
|
+
this.#determineAndAddEventStatusToMap(queueEntry.ID, EventProcessingStatus.Error);
|
|
431
415
|
});
|
|
432
416
|
}
|
|
433
417
|
|
|
434
|
-
|
|
418
|
+
#ensureEveryStatusIsAllowed(statusMap) {
|
|
435
419
|
Object.entries(statusMap).forEach(([queueEntryId, status]) => {
|
|
436
420
|
if (
|
|
437
421
|
[
|
|
@@ -444,22 +428,19 @@ class EventQueueProcessorBase {
|
|
|
444
428
|
return;
|
|
445
429
|
}
|
|
446
430
|
|
|
447
|
-
this.logger.error(
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
status: statusMap[queueEntryId],
|
|
454
|
-
}
|
|
455
|
-
);
|
|
431
|
+
this.logger.error("Not allowed event status returned. Only Open, Done, Error is allowed!", {
|
|
432
|
+
eventType: this.__eventType,
|
|
433
|
+
eventSubType: this.__eventSubType,
|
|
434
|
+
queueEntryId,
|
|
435
|
+
status: statusMap[queueEntryId],
|
|
436
|
+
});
|
|
456
437
|
delete statusMap[queueEntryId];
|
|
457
438
|
});
|
|
458
439
|
}
|
|
459
440
|
|
|
460
|
-
|
|
441
|
+
#ensureOnlySelectedQueueEntries(statusMap) {
|
|
461
442
|
Object.keys(statusMap).forEach((queueEntryId) => {
|
|
462
|
-
if (this
|
|
443
|
+
if (this.#selectedEventMap[queueEntryId]) {
|
|
463
444
|
return;
|
|
464
445
|
}
|
|
465
446
|
|
|
@@ -476,18 +457,12 @@ class EventQueueProcessorBase {
|
|
|
476
457
|
}
|
|
477
458
|
|
|
478
459
|
handleErrorDuringClustering(error) {
|
|
479
|
-
this.logger.error(
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
eventSubType: this.__eventSubType,
|
|
484
|
-
}
|
|
485
|
-
);
|
|
460
|
+
this.logger.error(`Error during clustering of events - setting all queue entries to error. Error: ${error}`, {
|
|
461
|
+
eventType: this.__eventType,
|
|
462
|
+
eventSubType: this.__eventSubType,
|
|
463
|
+
});
|
|
486
464
|
this.__queueEntries.forEach((queueEntry) => {
|
|
487
|
-
this.
|
|
488
|
-
queueEntry.ID,
|
|
489
|
-
EventProcessingStatus.Error
|
|
490
|
-
);
|
|
465
|
+
this.#determineAndAddEventStatusToMap(queueEntry.ID, EventProcessingStatus.Error);
|
|
491
466
|
});
|
|
492
467
|
}
|
|
493
468
|
|
|
@@ -500,29 +475,15 @@ class EventQueueProcessorBase {
|
|
|
500
475
|
eventSubType: this.__eventSubType,
|
|
501
476
|
}
|
|
502
477
|
);
|
|
503
|
-
this.
|
|
504
|
-
queueEntry.ID,
|
|
505
|
-
EventProcessingStatus.Error
|
|
506
|
-
);
|
|
478
|
+
this.#determineAndAddEventStatusToMap(queueEntry.ID, EventProcessingStatus.Error);
|
|
507
479
|
}
|
|
508
480
|
|
|
509
|
-
static async handleMissingTypeImplementation(
|
|
510
|
-
context,
|
|
511
|
-
|
|
512
|
-
eventSubType
|
|
513
|
-
) {
|
|
514
|
-
const baseInstance = new EventQueueProcessorBase(
|
|
515
|
-
context,
|
|
481
|
+
static async handleMissingTypeImplementation(context, eventType, eventSubType) {
|
|
482
|
+
const baseInstance = new EventQueueProcessorBase(context, eventType, eventSubType);
|
|
483
|
+
baseInstance.logger.error("No Implementation found in the provided configuration file.", {
|
|
516
484
|
eventType,
|
|
517
|
-
eventSubType
|
|
518
|
-
);
|
|
519
|
-
baseInstance.logger.error(
|
|
520
|
-
"No Implementation found in the provided configuration file.",
|
|
521
|
-
{
|
|
522
|
-
eventType,
|
|
523
|
-
eventSubType,
|
|
524
|
-
}
|
|
525
|
-
);
|
|
485
|
+
eventSubType,
|
|
486
|
+
});
|
|
526
487
|
}
|
|
527
488
|
|
|
528
489
|
/**
|
|
@@ -534,137 +495,179 @@ class EventQueueProcessorBase {
|
|
|
534
495
|
*/
|
|
535
496
|
async getQueueEntriesAndSetToInProgress() {
|
|
536
497
|
let result = [];
|
|
537
|
-
await executeInNewTransaction(
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
).toISOString(),
|
|
562
|
-
") )"
|
|
563
|
-
)
|
|
564
|
-
.orderBy("createdAt", "ID")
|
|
565
|
-
);
|
|
566
|
-
|
|
567
|
-
if (!entries.length) {
|
|
568
|
-
this.logger.debug("no entries available for processing", {
|
|
569
|
-
eventType: this.__eventType,
|
|
570
|
-
eventSubType: this.__eventSubType,
|
|
571
|
-
});
|
|
572
|
-
this.__emptyChunkSelected = true;
|
|
573
|
-
return;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
const { exceededTries, openEvents } =
|
|
577
|
-
this._filterExceededEvents(entries);
|
|
578
|
-
if (exceededTries.length) {
|
|
579
|
-
this.__eventsWithExceededTries = exceededTries;
|
|
580
|
-
}
|
|
581
|
-
result = openEvents;
|
|
582
|
-
|
|
583
|
-
if (!result.length) {
|
|
584
|
-
this.__emptyChunkSelected = true;
|
|
585
|
-
return;
|
|
586
|
-
}
|
|
498
|
+
await executeInNewTransaction(this.__baseContext, "eventQueue-getQueueEntriesAndSetToInProgress", async (tx) => {
|
|
499
|
+
const entries = await tx.run(
|
|
500
|
+
SELECT.from(this.__eventQueueConfig.tableNameEventQueue)
|
|
501
|
+
.forUpdate({ wait: this.__eventQueueConfig.forUpdateTimeout })
|
|
502
|
+
.limit(this.selectMaxChunkSize)
|
|
503
|
+
.where(
|
|
504
|
+
"type =",
|
|
505
|
+
this.__eventType,
|
|
506
|
+
"AND subType=",
|
|
507
|
+
this.__eventSubType,
|
|
508
|
+
"AND ( status =",
|
|
509
|
+
EventProcessingStatus.Open,
|
|
510
|
+
"OR ( status =",
|
|
511
|
+
EventProcessingStatus.Error,
|
|
512
|
+
"AND lastAttemptTimestamp <=",
|
|
513
|
+
this.__startTime.toISOString(),
|
|
514
|
+
") OR ( status =",
|
|
515
|
+
EventProcessingStatus.InProgress,
|
|
516
|
+
"AND lastAttemptTimestamp <=",
|
|
517
|
+
new Date(new Date().getTime() - this.__eventQueueConfig.globalTxTimeout).toISOString(),
|
|
518
|
+
") )"
|
|
519
|
+
)
|
|
520
|
+
.orderBy("createdAt", "ID")
|
|
521
|
+
);
|
|
587
522
|
|
|
588
|
-
|
|
589
|
-
|
|
523
|
+
if (!entries.length) {
|
|
524
|
+
this.logger.debug("no entries available for processing", {
|
|
590
525
|
eventType: this.__eventType,
|
|
591
526
|
eventSubType: this.__eventSubType,
|
|
592
527
|
});
|
|
528
|
+
this.__emptyChunkSelected = true;
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
593
531
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
status: EventProcessingStatus.InProgress,
|
|
599
|
-
lastAttemptTimestamp: isoTimestamp,
|
|
600
|
-
attempts: { "+=": 1 },
|
|
601
|
-
})
|
|
602
|
-
.where(
|
|
603
|
-
"ID IN",
|
|
604
|
-
result.map(({ ID }) => ID)
|
|
605
|
-
)
|
|
606
|
-
);
|
|
607
|
-
result.forEach((entry) => {
|
|
608
|
-
entry.lastAttemptTimestamp = isoTimestamp;
|
|
609
|
-
// NOTE: empty payloads are supported on DB-Level.
|
|
610
|
-
// Behaviour of event queue is: null as payload is treated as obsolete/done
|
|
611
|
-
// For supporting this convert null to empty string --> "" as payload will be processed normally
|
|
612
|
-
if (entry.payload === null) {
|
|
613
|
-
entry.payload = "";
|
|
614
|
-
}
|
|
615
|
-
});
|
|
532
|
+
this.#selectedEventMap = arrayToFlatMap(entries);
|
|
533
|
+
const { exceededTries, openEvents, exceededTriesExceeded } = this.#filterExceededEvents(entries);
|
|
534
|
+
if (exceededTries.length) {
|
|
535
|
+
this.#eventsWithExceededTries = exceededTries;
|
|
616
536
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
537
|
+
if (exceededTriesExceeded.length) {
|
|
538
|
+
this.#exceededTriesExceeded = exceededTriesExceeded;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
result = openEvents;
|
|
542
|
+
|
|
543
|
+
if (!result.length) {
|
|
544
|
+
this.__emptyChunkSelected = true;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
this.logger.info("Selected event queue entries for processing", {
|
|
548
|
+
queueEntriesCount: result.length,
|
|
549
|
+
eventType: this.__eventType,
|
|
550
|
+
eventSubType: this.__eventSubType,
|
|
551
|
+
});
|
|
552
|
+
|
|
553
|
+
const isoTimestamp = new Date().toISOString();
|
|
554
|
+
await tx.run(
|
|
555
|
+
UPDATE.entity(this.__eventQueueConfig.tableNameEventQueue)
|
|
556
|
+
.with({
|
|
557
|
+
status: EventProcessingStatus.InProgress,
|
|
558
|
+
lastAttemptTimestamp: isoTimestamp,
|
|
559
|
+
attempts: { "+=": 1 },
|
|
560
|
+
})
|
|
561
|
+
.where(
|
|
562
|
+
"ID IN",
|
|
563
|
+
entries.map(({ ID }) => ID)
|
|
564
|
+
)
|
|
565
|
+
);
|
|
566
|
+
entries.forEach((entry) => {
|
|
567
|
+
entry.lastAttemptTimestamp = isoTimestamp;
|
|
568
|
+
// NOTE: empty payloads are supported on DB-Level.
|
|
569
|
+
// Behaviour of event queue is: null as payload is treated as obsolete/done
|
|
570
|
+
// For supporting this convert null to empty string --> "" as payload will be processed normally
|
|
571
|
+
if (entry.payload === null) {
|
|
572
|
+
entry.payload = "";
|
|
573
|
+
}
|
|
574
|
+
});
|
|
575
|
+
this.__queueEntries = result;
|
|
576
|
+
this.__queueEntriesMap = arrayToFlatMap(result);
|
|
577
|
+
});
|
|
620
578
|
return result;
|
|
621
579
|
}
|
|
622
580
|
|
|
623
|
-
|
|
581
|
+
#filterExceededEvents(events) {
|
|
624
582
|
return events.reduce(
|
|
625
583
|
(result, event) => {
|
|
626
|
-
if (event.attempts === this.__retryAttempts) {
|
|
584
|
+
if (event.attempts === this.__retryAttempts + TRIES_FOR_EXCEEDED_EVENTS) {
|
|
585
|
+
result.exceededTriesExceeded.push(event);
|
|
586
|
+
} else if (event.attempts >= this.__retryAttempts) {
|
|
627
587
|
result.exceededTries.push(event);
|
|
628
588
|
} else {
|
|
629
589
|
result.openEvents.push(event);
|
|
630
590
|
}
|
|
631
591
|
return result;
|
|
632
592
|
},
|
|
633
|
-
{ exceededTries: [], openEvents: [] }
|
|
593
|
+
{ exceededTries: [], openEvents: [], exceededTriesExceeded: [] }
|
|
634
594
|
);
|
|
635
595
|
}
|
|
636
596
|
|
|
637
|
-
async handleExceededEvents(
|
|
638
|
-
await this
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
597
|
+
async handleExceededEvents() {
|
|
598
|
+
await this.#handleExceededTriesExceeded();
|
|
599
|
+
if (!this.#eventsWithExceededTries.length) {
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
for (const exceededEvent of this.#eventsWithExceededTries) {
|
|
604
|
+
await executeInNewTransaction(
|
|
605
|
+
this.context,
|
|
606
|
+
`eventQueue-handleExceededEvents-${this.eventType}##${this.eventSubType}`,
|
|
607
|
+
async (tx) => {
|
|
608
|
+
try {
|
|
609
|
+
this.processEventContext = tx.context;
|
|
610
|
+
this.modifyQueueEntry(exceededEvent);
|
|
611
|
+
await this.hookForExceededEvents({ ...exceededEvent });
|
|
612
|
+
this.logger.warn("The retry attempts for the following events are exceeded", {
|
|
613
|
+
eventType: this.__eventType,
|
|
614
|
+
eventSubType: this.__eventSubType,
|
|
615
|
+
retryAttempts: this.__retryAttempts,
|
|
616
|
+
queueEntriesId: exceededEvent.ID,
|
|
617
|
+
currentAttempt: exceededEvent.attempts,
|
|
618
|
+
});
|
|
619
|
+
await this.#persistEventQueueStatusForExceeded(this.tx, [exceededEvent], EventProcessingStatus.Exceeded);
|
|
620
|
+
} catch (err) {
|
|
621
|
+
this.logger.error(
|
|
622
|
+
`Caught error during hook for exceeded events - setting queue entry to error. Please catch your promises/exceptions. Error: ${err}`,
|
|
623
|
+
{
|
|
624
|
+
eventType: this.__eventType,
|
|
625
|
+
eventSubType: this.__eventSubType,
|
|
626
|
+
retryAttempts: this.__retryAttempts,
|
|
627
|
+
queueEntriesId: exceededEvent.ID,
|
|
628
|
+
currentAttempt: exceededEvent.attempts,
|
|
629
|
+
}
|
|
630
|
+
);
|
|
631
|
+
await executeInNewTransaction(this.context, "error-hookForExceededEvents", async (tx) =>
|
|
632
|
+
this.#persistEventQueueStatusForExceeded(tx, [exceededEvent], EventProcessingStatus.Error)
|
|
633
|
+
);
|
|
634
|
+
throw new TriggerRollback();
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
async #handleExceededTriesExceeded() {
|
|
642
|
+
if (this.#exceededTriesExceeded.length) {
|
|
643
|
+
this.logger.error("Event hook failure exceeded, status set to 'exceeded' without invoking hook again!", {
|
|
651
644
|
eventType: this.__eventType,
|
|
652
645
|
eventSubType: this.__eventSubType,
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
646
|
+
queueEntriesIds: this.#eventsWithExceededTries.map(({ ID }) => ID),
|
|
647
|
+
});
|
|
648
|
+
await executeInNewTransaction(this.context, "exceededTriesExceeded", async (tx) => {
|
|
649
|
+
await this.#persistEventQueueStatusForExceeded(tx, this.#exceededTriesExceeded, EventProcessingStatus.Exceeded);
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
async #persistEventQueueStatusForExceeded(tx, events, status) {
|
|
655
|
+
const statusMap = this.setEventStatus(
|
|
656
|
+
events,
|
|
657
|
+
events.map((e) => [e.ID, status]),
|
|
658
|
+
true
|
|
656
659
|
);
|
|
657
|
-
await this.
|
|
660
|
+
await this.persistEventStatus(tx, { statusMap, skipChecks: true });
|
|
658
661
|
}
|
|
659
662
|
|
|
660
663
|
/**
|
|
661
664
|
* This function enables the possibility to execute custom actions for events for which the retry attempts have been
|
|
662
665
|
* exceeded. As always a valid transaction is available with this.tx. This transaction will be committed after the
|
|
663
666
|
* execution of this function.
|
|
664
|
-
* @param {Object}
|
|
667
|
+
* @param {Object} exceededEvent exceeded event queue entry
|
|
665
668
|
*/
|
|
666
669
|
// eslint-disable-next-line no-unused-vars
|
|
667
|
-
async hookForExceededEvents(
|
|
670
|
+
async hookForExceededEvents(exceededEvent) {}
|
|
668
671
|
|
|
669
672
|
/**
|
|
670
673
|
* This function serves the purpose of mass enabled preloading data for processing the events which are added with
|
|
@@ -687,9 +690,7 @@ class EventQueueProcessorBase {
|
|
|
687
690
|
return false;
|
|
688
691
|
}
|
|
689
692
|
let eventOutdated;
|
|
690
|
-
const runningChecks = queueEntries
|
|
691
|
-
.map((queueEntry) => this.__keepalivePromises[queueEntry.ID])
|
|
692
|
-
.filter((p) => p);
|
|
693
|
+
const runningChecks = queueEntries.map((queueEntry) => this.__keepalivePromises[queueEntry.ID]).filter((p) => p);
|
|
693
694
|
if (runningChecks.length === queueEntries.length) {
|
|
694
695
|
const results = await Promise.allSettled(runningChecks);
|
|
695
696
|
for (const { value } of results) {
|
|
@@ -702,70 +703,54 @@ class EventQueueProcessorBase {
|
|
|
702
703
|
await Promise.allSettled(runningChecks);
|
|
703
704
|
}
|
|
704
705
|
const checkAndUpdatePromise = new Promise((resolve) => {
|
|
705
|
-
executeInNewTransaction(
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
.
|
|
706
|
+
executeInNewTransaction(this.__baseContext, "eventProcessing-isOutdatedAndKeepalive", async (tx) => {
|
|
707
|
+
const queueEntriesFresh = await tx.run(
|
|
708
|
+
SELECT.from(this.__eventQueueConfig.tableNameEventQueue)
|
|
709
|
+
.forUpdate({ wait: this.__eventQueueConfig.forUpdateTimeout })
|
|
710
|
+
.where(
|
|
711
|
+
"ID IN",
|
|
712
|
+
queueEntries.map(({ ID }) => ID)
|
|
713
|
+
)
|
|
714
|
+
.columns("ID", "lastAttemptTimestamp")
|
|
715
|
+
);
|
|
716
|
+
eventOutdated = queueEntriesFresh.some((queueEntryFresh) => {
|
|
717
|
+
const queueEntry = this.__queueEntriesMap[queueEntryFresh.ID];
|
|
718
|
+
return queueEntry?.lastAttemptTimestamp !== queueEntryFresh.lastAttemptTimestamp;
|
|
719
|
+
});
|
|
720
|
+
let newTs = new Date().toISOString();
|
|
721
|
+
if (!eventOutdated) {
|
|
722
|
+
await tx.run(
|
|
723
|
+
UPDATE.entity(this.__eventQueueConfig.tableNameEventQueue)
|
|
724
|
+
.set("lastAttemptTimestamp =", newTs)
|
|
712
725
|
.where(
|
|
713
726
|
"ID IN",
|
|
714
727
|
queueEntries.map(({ ID }) => ID)
|
|
715
728
|
)
|
|
716
|
-
.columns("ID", "lastAttemptTimestamp")
|
|
717
729
|
);
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
)
|
|
724
|
-
});
|
|
725
|
-
let newTs = new Date().toISOString();
|
|
726
|
-
if (!eventOutdated) {
|
|
727
|
-
await tx.run(
|
|
728
|
-
UPDATE.entity(this.__eventQueueConfig.tableNameEventQueue)
|
|
729
|
-
.set("lastAttemptTimestamp =", newTs)
|
|
730
|
-
.where(
|
|
731
|
-
"ID IN",
|
|
732
|
-
queueEntries.map(({ ID }) => ID)
|
|
733
|
-
)
|
|
734
|
-
);
|
|
735
|
-
} else {
|
|
736
|
-
newTs = null;
|
|
737
|
-
this.logger.warn(
|
|
738
|
-
"event data has been modified. Processing skipped.",
|
|
739
|
-
{
|
|
740
|
-
eventType: this.__eventType,
|
|
741
|
-
eventSubType: this.__eventSubType,
|
|
742
|
-
queueEntriesIds: queueEntries.map(({ ID }) => ID),
|
|
743
|
-
}
|
|
744
|
-
);
|
|
745
|
-
queueEntries.forEach(
|
|
746
|
-
({ ID: queueEntryId }) =>
|
|
747
|
-
delete this.__queueEntriesMap[queueEntryId]
|
|
748
|
-
);
|
|
749
|
-
}
|
|
750
|
-
this.__queueEntries = Object.values(this.__queueEntriesMap);
|
|
751
|
-
queueEntriesFresh.forEach((queueEntryFresh) => {
|
|
752
|
-
if (this.__queueEntriesMap[queueEntryFresh.ID]) {
|
|
753
|
-
const queueEntry = this.__queueEntriesMap[queueEntryFresh.ID];
|
|
754
|
-
if (newTs) {
|
|
755
|
-
queueEntry.lastAttemptTimestamp = newTs;
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
delete this.__keepalivePromises[queueEntryFresh.ID];
|
|
730
|
+
} else {
|
|
731
|
+
newTs = null;
|
|
732
|
+
this.logger.warn("event data has been modified. Processing skipped.", {
|
|
733
|
+
eventType: this.__eventType,
|
|
734
|
+
eventSubType: this.__eventSubType,
|
|
735
|
+
queueEntriesIds: queueEntries.map(({ ID }) => ID),
|
|
759
736
|
});
|
|
760
|
-
|
|
737
|
+
queueEntries.forEach(({ ID: queueEntryId }) => delete this.__queueEntriesMap[queueEntryId]);
|
|
761
738
|
}
|
|
762
|
-
|
|
739
|
+
this.__queueEntries = Object.values(this.__queueEntriesMap);
|
|
740
|
+
queueEntriesFresh.forEach((queueEntryFresh) => {
|
|
741
|
+
if (this.__queueEntriesMap[queueEntryFresh.ID]) {
|
|
742
|
+
const queueEntry = this.__queueEntriesMap[queueEntryFresh.ID];
|
|
743
|
+
if (newTs) {
|
|
744
|
+
queueEntry.lastAttemptTimestamp = newTs;
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
delete this.__keepalivePromises[queueEntryFresh.ID];
|
|
748
|
+
});
|
|
749
|
+
resolve(eventOutdated);
|
|
750
|
+
});
|
|
763
751
|
});
|
|
764
752
|
|
|
765
|
-
queueEntries.forEach(
|
|
766
|
-
(queueEntry) =>
|
|
767
|
-
(this.__keepalivePromises[queueEntry.ID] = checkAndUpdatePromise)
|
|
768
|
-
);
|
|
753
|
+
queueEntries.forEach((queueEntry) => (this.__keepalivePromises[queueEntry.ID] = checkAndUpdatePromise));
|
|
769
754
|
return await checkAndUpdatePromise;
|
|
770
755
|
}
|
|
771
756
|
|
|
@@ -790,15 +775,9 @@ class EventQueueProcessorBase {
|
|
|
790
775
|
return;
|
|
791
776
|
}
|
|
792
777
|
try {
|
|
793
|
-
await distributedLock.releaseLock(
|
|
794
|
-
this.context,
|
|
795
|
-
[this.eventType, this.eventSubType].join("##")
|
|
796
|
-
);
|
|
778
|
+
await distributedLock.releaseLock(this.context, [this.eventType, this.eventSubType].join("##"));
|
|
797
779
|
} catch (err) {
|
|
798
|
-
this.logger.error(
|
|
799
|
-
"Releasing distributed lock failed. Error:",
|
|
800
|
-
err.toString()
|
|
801
|
-
);
|
|
780
|
+
this.logger.error("Releasing distributed lock failed. Error:", err.toString());
|
|
802
781
|
}
|
|
803
782
|
}
|
|
804
783
|
|
|
@@ -806,26 +785,11 @@ class EventQueueProcessorBase {
|
|
|
806
785
|
return Object.values(statusMap).includes(EventProcessingStatus.Error);
|
|
807
786
|
}
|
|
808
787
|
|
|
809
|
-
getSelectNextChunk() {
|
|
810
|
-
return this.__selectNextChunk;
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
getSelectMaxChunkSize() {
|
|
814
|
-
return this.__selectMaxChunkSize;
|
|
815
|
-
}
|
|
816
|
-
|
|
817
788
|
clearEventProcessingContext() {
|
|
818
789
|
this.__processContext = null;
|
|
819
790
|
this.__processTx = null;
|
|
820
791
|
}
|
|
821
792
|
|
|
822
|
-
get shouldTriggerRollback() {
|
|
823
|
-
return (
|
|
824
|
-
this.statusMapContainsError(this.__statusMap) ||
|
|
825
|
-
this.statusMapContainsError(this.__commitedStatusMap)
|
|
826
|
-
);
|
|
827
|
-
}
|
|
828
|
-
|
|
829
793
|
get logger() {
|
|
830
794
|
return this.__logger ?? this.__baseLogger;
|
|
831
795
|
}
|
|
@@ -875,7 +839,11 @@ class EventQueueProcessorBase {
|
|
|
875
839
|
}
|
|
876
840
|
|
|
877
841
|
get commitOnEventLevel() {
|
|
878
|
-
return this.
|
|
842
|
+
return this.__transactionMode === TransactionMode.isolated;
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
get transactionMode() {
|
|
846
|
+
return this.__transactionMode;
|
|
879
847
|
}
|
|
880
848
|
|
|
881
849
|
get eventType() {
|
|
@@ -886,14 +854,18 @@ class EventQueueProcessorBase {
|
|
|
886
854
|
return this.__eventSubType;
|
|
887
855
|
}
|
|
888
856
|
|
|
889
|
-
get exceededEvents() {
|
|
890
|
-
return this.__eventsWithExceededTries;
|
|
891
|
-
}
|
|
892
|
-
|
|
893
857
|
get emptyChunkSelected() {
|
|
894
858
|
return this.__emptyChunkSelected;
|
|
895
859
|
}
|
|
896
860
|
|
|
861
|
+
get selectNextChunk() {
|
|
862
|
+
return this.__selectNextChunk;
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
get selectMaxChunkSize() {
|
|
866
|
+
return this.__selectMaxChunkSize;
|
|
867
|
+
}
|
|
868
|
+
|
|
897
869
|
set txUsageAllowed(value) {
|
|
898
870
|
this.__txUsageAllowed = value;
|
|
899
871
|
}
|