@cap-js-community/event-queue 0.2.2 → 0.2.4
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/package.json +1 -1
- package/src/EventQueueError.js +14 -0
- package/src/EventQueueProcessorBase.js +83 -31
- package/src/config.js +32 -14
- package/src/dbHandler.js +2 -3
- package/src/index.js +1 -1
- package/src/initialize.js +23 -30
- package/src/periodicEvents.js +17 -16
- package/src/processEventQueue.js +59 -61
- package/src/publishEvent.js +3 -4
- package/src/redisPubSub.js +8 -6
- package/src/runner.js +89 -70
- package/src/shared/WorkerQueue.js +69 -28
- package/src/shared/cdsHelper.js +2 -2
- package/src/shared/common.js +1 -72
- package/src/shared/distributedLock.js +15 -21
- package/src/shared/eventScheduler.js +13 -8
package/src/processEventQueue.js
CHANGED
|
@@ -4,32 +4,22 @@ const pathLib = require("path");
|
|
|
4
4
|
|
|
5
5
|
const cds = require("@sap/cds");
|
|
6
6
|
|
|
7
|
-
const
|
|
7
|
+
const config = require("./config");
|
|
8
8
|
const { TransactionMode } = require("./constants");
|
|
9
|
-
const { limiter
|
|
9
|
+
const { limiter } = require("./shared/common");
|
|
10
10
|
|
|
11
11
|
const { executeInNewTransaction, TriggerRollback } = require("./shared/cdsHelper");
|
|
12
12
|
|
|
13
13
|
const COMPONENT_NAME = "eventQueue/processEventQueue";
|
|
14
14
|
const MAX_EXECUTION_TIME = 5 * 60 * 1000;
|
|
15
15
|
|
|
16
|
-
const eventQueueRunner = async (context, events) => {
|
|
17
|
-
const startTime = new Date();
|
|
18
|
-
const funnel = new Funnel();
|
|
19
|
-
await Promise.allSettled(
|
|
20
|
-
events.map((event) =>
|
|
21
|
-
funnel.run(event.load, async () => processEventQueue(context, event.type, event.subType, startTime))
|
|
22
|
-
)
|
|
23
|
-
);
|
|
24
|
-
};
|
|
25
|
-
|
|
26
16
|
const processEventQueue = async (context, eventType, eventSubType, startTime = new Date()) => {
|
|
27
17
|
let iterationCounter = 0;
|
|
28
18
|
let shouldContinue = true;
|
|
29
19
|
let baseInstance;
|
|
30
20
|
try {
|
|
31
21
|
let eventTypeInstance;
|
|
32
|
-
const eventConfig =
|
|
22
|
+
const eventConfig = config.getEventConfig(eventType, eventSubType);
|
|
33
23
|
const [err, EventTypeClass] = resilientRequire(eventConfig?.impl);
|
|
34
24
|
if (!eventConfig || err || !(typeof EventTypeClass.constructor === "function")) {
|
|
35
25
|
cds.log(COMPONENT_NAME).error("No Implementation found in the provided configuration file.", {
|
|
@@ -111,7 +101,7 @@ const processEventQueue = async (context, eventType, eventSubType, startTime = n
|
|
|
111
101
|
}
|
|
112
102
|
}
|
|
113
103
|
} catch (err) {
|
|
114
|
-
cds.log(COMPONENT_NAME).error("Processing event queue failed with unexpected error.
|
|
104
|
+
cds.log(COMPONENT_NAME).error("Processing event queue failed with unexpected error.", err, {
|
|
115
105
|
eventType,
|
|
116
106
|
eventSubType,
|
|
117
107
|
});
|
|
@@ -137,58 +127,67 @@ const reevaluateShouldContinue = (eventTypeInstance, iterationCounter, startTime
|
|
|
137
127
|
// TODO: don't forget to release lock
|
|
138
128
|
const processPeriodicEvent = async (eventTypeInstance) => {
|
|
139
129
|
let queueEntry;
|
|
130
|
+
let processNext = true;
|
|
131
|
+
|
|
140
132
|
try {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
133
|
+
while (processNext) {
|
|
134
|
+
await executeInNewTransaction(
|
|
135
|
+
eventTypeInstance.context,
|
|
136
|
+
`eventQueue-periodic-scheduleNext-${eventTypeInstance.eventType}##${eventTypeInstance.eventSubType}`,
|
|
137
|
+
async (tx) => {
|
|
138
|
+
eventTypeInstance.processEventContext = tx.context;
|
|
139
|
+
const queueEntries = await eventTypeInstance.getQueueEntriesAndSetToInProgress();
|
|
140
|
+
if (!queueEntries.length) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (queueEntries.length > 1) {
|
|
144
|
+
queueEntry = await eventTypeInstance.handleDuplicatedPeriodicEventEntry(queueEntries);
|
|
145
|
+
} else {
|
|
146
|
+
queueEntry = queueEntries[0];
|
|
147
|
+
}
|
|
148
|
+
processNext = await eventTypeInstance.scheduleNextPeriodEvent(queueEntry);
|
|
154
149
|
}
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
);
|
|
150
|
+
);
|
|
158
151
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
152
|
+
if (!queueEntry) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
162
155
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
156
|
+
await executeInNewTransaction(
|
|
157
|
+
eventTypeInstance.context,
|
|
158
|
+
`eventQueue-periodic-process-${eventTypeInstance.eventType}##${eventTypeInstance.eventSubType}`,
|
|
159
|
+
async (tx) => {
|
|
160
|
+
eventTypeInstance.processEventContext = tx.context;
|
|
161
|
+
eventTypeInstance.setTxForEventProcessing(queueEntry.ID, cds.tx(tx.context));
|
|
162
|
+
try {
|
|
163
|
+
await eventTypeInstance.processPeriodicEvent(tx.context, queueEntry.ID, queueEntry);
|
|
164
|
+
} catch (err) {
|
|
165
|
+
eventTypeInstance.handleErrorDuringPeriodicEventProcessing(err, queueEntry);
|
|
166
|
+
throw new TriggerRollback();
|
|
167
|
+
}
|
|
168
|
+
if (
|
|
169
|
+
eventTypeInstance.transactionMode !== TransactionMode.alwaysCommit ||
|
|
170
|
+
eventTypeInstance.shouldRollbackTransaction(queueEntry.ID)
|
|
171
|
+
) {
|
|
172
|
+
throw new TriggerRollback();
|
|
173
|
+
}
|
|
180
174
|
}
|
|
181
|
-
|
|
182
|
-
);
|
|
175
|
+
);
|
|
183
176
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
177
|
+
await executeInNewTransaction(
|
|
178
|
+
eventTypeInstance.context,
|
|
179
|
+
`eventQueue-periodic-setStatus-${eventTypeInstance.eventType}##${eventTypeInstance.eventSubType}`,
|
|
180
|
+
async (tx) => {
|
|
181
|
+
eventTypeInstance.processEventContext = tx.context;
|
|
182
|
+
await eventTypeInstance.setPeriodicEventStatus(queueEntry.ID);
|
|
183
|
+
}
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
} catch (err) {
|
|
187
|
+
cds.log(COMPONENT_NAME).error("Processing periodic events failed with unexpected error.", err, {
|
|
188
|
+
eventType: eventTypeInstance?.eventType,
|
|
189
|
+
eventSubType: eventTypeInstance?.eventSubType,
|
|
190
|
+
});
|
|
192
191
|
} finally {
|
|
193
192
|
await eventTypeInstance?.handleReleaseLock();
|
|
194
193
|
}
|
|
@@ -269,5 +268,4 @@ const resilientRequire = (path) => {
|
|
|
269
268
|
|
|
270
269
|
module.exports = {
|
|
271
270
|
processEventQueue,
|
|
272
|
-
eventQueueRunner,
|
|
273
271
|
};
|
package/src/publishEvent.js
CHANGED
|
@@ -28,13 +28,12 @@ const EventQueueError = require("./EventQueueError");
|
|
|
28
28
|
* @returns {Promise} Returns a promise which resolves to the result of the database insert operation.
|
|
29
29
|
*/
|
|
30
30
|
const publishEvent = async (tx, events, skipBroadcast = false) => {
|
|
31
|
-
|
|
32
|
-
if (!configInstance.initialized) {
|
|
31
|
+
if (!config.initialized) {
|
|
33
32
|
throw EventQueueError.notInitialized();
|
|
34
33
|
}
|
|
35
34
|
const eventsForProcessing = Array.isArray(events) ? events : [events];
|
|
36
35
|
for (const { type, subType, startAfter } of eventsForProcessing) {
|
|
37
|
-
const eventConfig =
|
|
36
|
+
const eventConfig = config.getEventConfig(type, subType);
|
|
38
37
|
if (!eventConfig) {
|
|
39
38
|
throw EventQueueError.unknownEventType(type, subType);
|
|
40
39
|
}
|
|
@@ -47,7 +46,7 @@ const publishEvent = async (tx, events, skipBroadcast = false) => {
|
|
|
47
46
|
}
|
|
48
47
|
}
|
|
49
48
|
tx._skipEventQueueBroadcase = skipBroadcast;
|
|
50
|
-
const result = await tx.run(INSERT.into(
|
|
49
|
+
const result = await tx.run(INSERT.into(config.tableNameEventQueue).entries(eventsForProcessing));
|
|
51
50
|
tx._skipEventQueueBroadcase = false;
|
|
52
51
|
return result;
|
|
53
52
|
};
|
package/src/redisPubSub.js
CHANGED
|
@@ -11,7 +11,7 @@ const COMPONENT_NAME = "eventQueue/redisPubSub";
|
|
|
11
11
|
let subscriberClientPromise;
|
|
12
12
|
|
|
13
13
|
const initEventQueueRedisSubscribe = () => {
|
|
14
|
-
if (subscriberClientPromise || !config.
|
|
14
|
+
if (subscriberClientPromise || !config.redisEnabled) {
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
17
|
redis.subscribeRedisChannel(EVENT_MESSAGE_CHANNEL, messageHandlerProcessEvents);
|
|
@@ -36,9 +36,8 @@ const messageHandlerProcessEvents = async (messageData) => {
|
|
|
36
36
|
|
|
37
37
|
const broadcastEvent = async (tenantId, type, subType) => {
|
|
38
38
|
const logger = cds.log(COMPONENT_NAME);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (configInstance.registerAsEventProcessor) {
|
|
39
|
+
if (!config.redisEnabled) {
|
|
40
|
+
if (config.registerAsEventProcessor) {
|
|
42
41
|
await runEventCombinationForTenant(tenantId, type, subType);
|
|
43
42
|
}
|
|
44
43
|
return;
|
|
@@ -49,7 +48,10 @@ const broadcastEvent = async (tenantId, type, subType) => {
|
|
|
49
48
|
[type, subType].join("##")
|
|
50
49
|
);
|
|
51
50
|
if (result) {
|
|
52
|
-
logger.info("skip publish redis event as no lock is available"
|
|
51
|
+
logger.info("skip publish redis event as no lock is available", {
|
|
52
|
+
type,
|
|
53
|
+
subType,
|
|
54
|
+
});
|
|
53
55
|
return;
|
|
54
56
|
}
|
|
55
57
|
logger.debug("publishing redis event", {
|
|
@@ -59,7 +61,7 @@ const broadcastEvent = async (tenantId, type, subType) => {
|
|
|
59
61
|
});
|
|
60
62
|
await redis.publishMessage(EVENT_MESSAGE_CHANNEL, JSON.stringify({ tenantId, type, subType }));
|
|
61
63
|
} catch (err) {
|
|
62
|
-
logger.error(
|
|
64
|
+
logger.error("publish event failed!", err, {
|
|
63
65
|
tenantId,
|
|
64
66
|
type,
|
|
65
67
|
subType,
|
package/src/runner.js
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
const { randomUUID } = require("crypto");
|
|
4
4
|
|
|
5
5
|
const eventQueueConfig = require("./config");
|
|
6
|
-
const {
|
|
7
|
-
const
|
|
6
|
+
const { processEventQueue } = require("./processEventQueue");
|
|
7
|
+
const WorkerQueue = require("./shared/WorkerQueue");
|
|
8
8
|
const cdsHelper = require("./shared/cdsHelper");
|
|
9
9
|
const distributedLock = require("./shared/distributedLock");
|
|
10
10
|
const SetIntervalDriftSafe = require("./shared/SetIntervalDriftSafe");
|
|
@@ -19,8 +19,9 @@ const EVENT_QUEUE_RUN_PERIODIC_EVENT = "EVENT_QUEUE_RUN_PERIODIC_EVENT";
|
|
|
19
19
|
const OFFSET_FIRST_RUN = 10 * 1000;
|
|
20
20
|
|
|
21
21
|
let tenantIdHash;
|
|
22
|
+
let singleRunDone;
|
|
22
23
|
|
|
23
|
-
const singleTenant = () => _scheduleFunction(_checkPeriodicEventsSingleTenant,
|
|
24
|
+
const singleTenant = () => _scheduleFunction(_checkPeriodicEventsSingleTenant, _singleTenantDb);
|
|
24
25
|
|
|
25
26
|
const multiTenancyDb = () => _scheduleFunction(_multiTenancyPeriodicEvents, _multiTenancyDb);
|
|
26
27
|
|
|
@@ -28,8 +29,7 @@ const multiTenancyRedis = () => _scheduleFunction(_multiTenancyPeriodicEvents, _
|
|
|
28
29
|
|
|
29
30
|
const _scheduleFunction = async (singleRunFn, periodicFn) => {
|
|
30
31
|
const logger = cds.log(COMPONENT_NAME);
|
|
31
|
-
const
|
|
32
|
-
const eventsForAutomaticRun = configInstance.allEvents;
|
|
32
|
+
const eventsForAutomaticRun = eventQueueConfig.allEvents;
|
|
33
33
|
if (!eventsForAutomaticRun.length) {
|
|
34
34
|
logger.warn("no events for automatic run are configured - skipping runner registration");
|
|
35
35
|
return;
|
|
@@ -37,10 +37,14 @@ const _scheduleFunction = async (singleRunFn, periodicFn) => {
|
|
|
37
37
|
|
|
38
38
|
const fnWithRunningCheck = () => {
|
|
39
39
|
const logger = cds.log(COMPONENT_NAME);
|
|
40
|
-
if (
|
|
40
|
+
if (eventQueueConfig.isRunnerDeactivated) {
|
|
41
41
|
logger.info("runner is deactivated via config variable. Skipping this run.");
|
|
42
42
|
return;
|
|
43
43
|
}
|
|
44
|
+
if (!singleRunDone) {
|
|
45
|
+
singleRunDone = true;
|
|
46
|
+
singleRunFn().catch(() => (singleRunDone = false));
|
|
47
|
+
}
|
|
44
48
|
return periodicFn();
|
|
45
49
|
};
|
|
46
50
|
|
|
@@ -51,9 +55,8 @@ const _scheduleFunction = async (singleRunFn, periodicFn) => {
|
|
|
51
55
|
});
|
|
52
56
|
|
|
53
57
|
setTimeout(() => {
|
|
54
|
-
singleRunFn();
|
|
55
58
|
fnWithRunningCheck();
|
|
56
|
-
const intervalRunner = new SetIntervalDriftSafe(
|
|
59
|
+
const intervalRunner = new SetIntervalDriftSafe(eventQueueConfig.runInterval);
|
|
57
60
|
intervalRunner.run(fnWithRunningCheck);
|
|
58
61
|
}, offsetDependingOnLastRun).unref();
|
|
59
62
|
};
|
|
@@ -63,7 +66,7 @@ const _multiTenancyRedis = async () => {
|
|
|
63
66
|
const emptyContext = new cds.EventContext({});
|
|
64
67
|
logger.info("executing event queue run for multi instance and tenant");
|
|
65
68
|
const tenantIds = await cdsHelper.getAllTenantIds();
|
|
66
|
-
|
|
69
|
+
_checkAndTriggerPeriodicEventUpdate(tenantIds);
|
|
67
70
|
|
|
68
71
|
const runId = await _acquireRunId(emptyContext);
|
|
69
72
|
|
|
@@ -72,10 +75,10 @@ const _multiTenancyRedis = async () => {
|
|
|
72
75
|
return;
|
|
73
76
|
}
|
|
74
77
|
|
|
75
|
-
|
|
78
|
+
return _executeEventsAllTenants(tenantIds, runId);
|
|
76
79
|
};
|
|
77
80
|
|
|
78
|
-
const
|
|
81
|
+
const _checkAndTriggerPeriodicEventUpdate = (tenantIds) => {
|
|
79
82
|
const hash = hashStringTo32Bit(JSON.stringify(tenantIds));
|
|
80
83
|
if (!tenantIdHash) {
|
|
81
84
|
tenantIdHash = hash;
|
|
@@ -84,25 +87,52 @@ const _checkAndTriggerPriodicEventUpdate = (tenantIds) => {
|
|
|
84
87
|
if (tenantIdHash && tenantIdHash !== hash) {
|
|
85
88
|
cds.log(COMPONENT_NAME).info("tenant id hash changed, triggering updating periodic events!");
|
|
86
89
|
_multiTenancyPeriodicEvents().catch((err) => {
|
|
87
|
-
cds.log(COMPONENT_NAME).error("Error during triggering updating periodic events!
|
|
90
|
+
cds.log(COMPONENT_NAME).error("Error during triggering updating periodic events!", err);
|
|
88
91
|
});
|
|
89
92
|
}
|
|
90
93
|
};
|
|
91
94
|
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
const
|
|
95
|
+
const _executeEventsAllTenants = (tenantIds, runId) => {
|
|
96
|
+
const events = eventQueueConfig.allEvents;
|
|
97
|
+
const promises = [];
|
|
95
98
|
tenantIds.forEach((tenantId) => {
|
|
96
|
-
|
|
99
|
+
events.forEach((event) => {
|
|
100
|
+
promises.push(
|
|
101
|
+
WorkerQueue.instance.addToQueue(event.load, async () => {
|
|
102
|
+
try {
|
|
103
|
+
const lockId = `${runId}_${event.type}_${event.subType}`;
|
|
104
|
+
const tenantContext = new cds.EventContext({ tenant: tenantId });
|
|
105
|
+
const couldAcquireLock = await distributedLock.acquireLock(tenantContext, lockId, {
|
|
106
|
+
expiryTime: eventQueueConfig.runInterval * 0.95,
|
|
107
|
+
});
|
|
108
|
+
if (!couldAcquireLock) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
await runEventCombinationForTenant(tenantId, event.type, event.subType, true);
|
|
112
|
+
} catch (err) {
|
|
113
|
+
cds.log(COMPONENT_NAME).error("executing event-queue run for tenant failed", {
|
|
114
|
+
tenantId,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
return promises;
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const _executePeriodicEventsAllTenants = (tenantIds, runId) => {
|
|
125
|
+
tenantIds.forEach((tenantId) => {
|
|
126
|
+
WorkerQueue.instance.addToQueue(1, async () => {
|
|
97
127
|
try {
|
|
98
128
|
const tenantContext = new cds.EventContext({ tenant: tenantId });
|
|
99
129
|
const couldAcquireLock = await distributedLock.acquireLock(tenantContext, runId, {
|
|
100
|
-
expiryTime:
|
|
130
|
+
expiryTime: eventQueueConfig.runInterval * 0.95,
|
|
101
131
|
});
|
|
102
132
|
if (!couldAcquireLock) {
|
|
103
133
|
return;
|
|
104
134
|
}
|
|
105
|
-
await
|
|
135
|
+
await _checkPeriodicEventsSingleTenant(tenantId);
|
|
106
136
|
} catch (err) {
|
|
107
137
|
cds.log(COMPONENT_NAME).error("executing event-queue run for tenant failed", {
|
|
108
138
|
tenantId,
|
|
@@ -112,49 +142,33 @@ const _executeAllTenantsGeneric = (tenantIds, runId, fn) => {
|
|
|
112
142
|
});
|
|
113
143
|
};
|
|
114
144
|
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
tenant: tenantId,
|
|
128
|
-
// NOTE: we need this because of logging otherwise logs would not contain the subdomain
|
|
129
|
-
http: { req: { authInfo: { getSubdomain: () => subdomain } } },
|
|
130
|
-
});
|
|
131
|
-
cds.context = context;
|
|
132
|
-
logger.info("executing eventQueue run", {
|
|
133
|
-
tenantId,
|
|
134
|
-
subdomain,
|
|
135
|
-
...(runId ? { runId } : null),
|
|
136
|
-
});
|
|
137
|
-
await eventQueueRunner(context, eventsForAutomaticRun);
|
|
138
|
-
} catch (err) {
|
|
139
|
-
logger.error(`Couldn't process eventQueue for tenant! Next try after defined interval. Error: ${err}`, {
|
|
140
|
-
tenantId,
|
|
141
|
-
redisEnabled: configInstance.redisEnabled,
|
|
145
|
+
const _singleTenantDb = async (tenantId) => {
|
|
146
|
+
const events = eventQueueConfig.allEvents;
|
|
147
|
+
events.forEach((event) => {
|
|
148
|
+
WorkerQueue.instance.addToQueue(event.load, async () => {
|
|
149
|
+
try {
|
|
150
|
+
await runEventCombinationForTenant(tenantId, event.type, event.subType, true);
|
|
151
|
+
} catch (err) {
|
|
152
|
+
cds.log(COMPONENT_NAME).error("executing event-queue run for tenant failed", {
|
|
153
|
+
tenantId,
|
|
154
|
+
redisEnabled: eventQueueConfig.redisEnabled,
|
|
155
|
+
});
|
|
156
|
+
}
|
|
142
157
|
});
|
|
143
|
-
}
|
|
158
|
+
});
|
|
144
159
|
};
|
|
145
160
|
|
|
146
161
|
const _acquireRunId = async (context) => {
|
|
147
|
-
const configInstance = eventQueueConfig.getConfigInstance();
|
|
148
162
|
let runId = randomUUID();
|
|
149
163
|
const couldSetValue = await distributedLock.setValueWithExpire(context, EVENT_QUEUE_RUN_ID, runId, {
|
|
150
164
|
tenantScoped: false,
|
|
151
|
-
expiryTime:
|
|
165
|
+
expiryTime: eventQueueConfig.runInterval * 0.95,
|
|
152
166
|
});
|
|
153
167
|
|
|
154
168
|
if (couldSetValue) {
|
|
155
169
|
await distributedLock.setValueWithExpire(context, EVENT_QUEUE_RUN_TS, new Date().toISOString(), {
|
|
156
170
|
tenantScoped: false,
|
|
157
|
-
expiryTime:
|
|
171
|
+
expiryTime: eventQueueConfig.runInterval,
|
|
158
172
|
overrideValue: true,
|
|
159
173
|
});
|
|
160
174
|
} else {
|
|
@@ -167,13 +181,12 @@ const _acquireRunId = async (context) => {
|
|
|
167
181
|
};
|
|
168
182
|
|
|
169
183
|
const _calculateOffsetForFirstRun = async () => {
|
|
170
|
-
const configInstance = eventQueueConfig.getConfigInstance();
|
|
171
184
|
let offsetDependingOnLastRun = OFFSET_FIRST_RUN;
|
|
172
185
|
const now = Date.now();
|
|
173
186
|
// NOTE: this is only supported with Redis, because this is a tenant agnostic information
|
|
174
187
|
// currently there is no proper place to store this information beside t0 schema
|
|
175
188
|
try {
|
|
176
|
-
if (
|
|
189
|
+
if (eventQueueConfig.redisEnabled) {
|
|
177
190
|
const dummyContext = new cds.EventContext({});
|
|
178
191
|
let lastRunTs = await distributedLock.checkLockExistsAndReturnValue(dummyContext, EVENT_QUEUE_RUN_TS, {
|
|
179
192
|
tenantScoped: false,
|
|
@@ -182,7 +195,7 @@ const _calculateOffsetForFirstRun = async () => {
|
|
|
182
195
|
const ts = new Date(now).toISOString();
|
|
183
196
|
const couldSetValue = await distributedLock.setValueWithExpire(dummyContext, EVENT_QUEUE_RUN_TS, ts, {
|
|
184
197
|
tenantScoped: false,
|
|
185
|
-
expiryTime:
|
|
198
|
+
expiryTime: eventQueueConfig.runInterval,
|
|
186
199
|
});
|
|
187
200
|
if (couldSetValue) {
|
|
188
201
|
lastRunTs = ts;
|
|
@@ -192,20 +205,17 @@ const _calculateOffsetForFirstRun = async () => {
|
|
|
192
205
|
});
|
|
193
206
|
}
|
|
194
207
|
}
|
|
195
|
-
offsetDependingOnLastRun = new Date(lastRunTs).getTime() +
|
|
208
|
+
offsetDependingOnLastRun = new Date(lastRunTs).getTime() + eventQueueConfig.runInterval - now;
|
|
196
209
|
}
|
|
197
210
|
} catch (err) {
|
|
198
211
|
cds
|
|
199
212
|
.log(COMPONENT_NAME)
|
|
200
|
-
.error(
|
|
201
|
-
"calculating offset for first run failed, falling back to default. Runs might be out-of-sync. Error:",
|
|
202
|
-
err
|
|
203
|
-
);
|
|
213
|
+
.error("calculating offset for first run failed, falling back to default. Runs might be out-of-sync.", err);
|
|
204
214
|
}
|
|
205
215
|
return offsetDependingOnLastRun;
|
|
206
216
|
};
|
|
207
217
|
|
|
208
|
-
const runEventCombinationForTenant = async (tenantId, type, subType) => {
|
|
218
|
+
const runEventCombinationForTenant = async (tenantId, type, subType, skipWorkerPool) => {
|
|
209
219
|
try {
|
|
210
220
|
const subdomain = await getSubdomainForTenantId(tenantId);
|
|
211
221
|
const context = new cds.EventContext({
|
|
@@ -214,7 +224,15 @@ const runEventCombinationForTenant = async (tenantId, type, subType) => {
|
|
|
214
224
|
http: { req: { authInfo: { getSubdomain: () => subdomain } } },
|
|
215
225
|
});
|
|
216
226
|
cds.context = context;
|
|
217
|
-
|
|
227
|
+
if (skipWorkerPool) {
|
|
228
|
+
return await processEventQueue(context, type, subType);
|
|
229
|
+
} else {
|
|
230
|
+
const config = eventQueueConfig.getEventConfig(type, subType);
|
|
231
|
+
return await WorkerQueue.instance.addToQueue(
|
|
232
|
+
config.load,
|
|
233
|
+
async () => await processEventQueue(context, type, subType)
|
|
234
|
+
);
|
|
235
|
+
}
|
|
218
236
|
} catch (err) {
|
|
219
237
|
const logger = cds.log(COMPONENT_NAME);
|
|
220
238
|
logger.error("error executing event combination for tenant", err, {
|
|
@@ -230,12 +248,10 @@ const _multiTenancyDb = async () => {
|
|
|
230
248
|
try {
|
|
231
249
|
logger.info("executing event queue run for single instance and multi tenant");
|
|
232
250
|
const tenantIds = await cdsHelper.getAllTenantIds();
|
|
233
|
-
|
|
234
|
-
|
|
251
|
+
_checkAndTriggerPeriodicEventUpdate(tenantIds);
|
|
252
|
+
return _executeEventsAllTenants(tenantIds, EVENT_QUEUE_RUN_ID);
|
|
235
253
|
} catch (err) {
|
|
236
|
-
logger.error(
|
|
237
|
-
`Couldn't fetch tenant ids for event queue processing! Next try after defined interval. Error: ${err}`
|
|
238
|
-
);
|
|
254
|
+
logger.error("Couldn't fetch tenant ids for event queue processing! Next try after defined interval.", err);
|
|
239
255
|
}
|
|
240
256
|
};
|
|
241
257
|
|
|
@@ -246,15 +262,18 @@ const _multiTenancyPeriodicEvents = async () => {
|
|
|
246
262
|
const tenantIds = await cdsHelper.getAllTenantIds();
|
|
247
263
|
_executePeriodicEventsAllTenants(tenantIds, EVENT_QUEUE_RUN_PERIODIC_EVENT);
|
|
248
264
|
} catch (err) {
|
|
249
|
-
logger.error(
|
|
265
|
+
logger.error("Couldn't fetch tenant ids for updating periodic event processing!", err);
|
|
250
266
|
}
|
|
251
267
|
};
|
|
252
268
|
|
|
253
269
|
const _checkPeriodicEventsSingleTenant = async (tenantId) => {
|
|
254
270
|
const logger = cds.log(COMPONENT_NAME);
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
271
|
+
if (!eventQueueConfig.updatePeriodicEvents || !eventQueueConfig.periodicEvents.length) {
|
|
272
|
+
logger.info("updating of periodic events is disabled or no periodic events configured", {
|
|
273
|
+
updateEnabled: eventQueueConfig.updatePeriodicEvents,
|
|
274
|
+
events: eventQueueConfig.periodicEvents.length,
|
|
275
|
+
});
|
|
276
|
+
return;
|
|
258
277
|
}
|
|
259
278
|
try {
|
|
260
279
|
const subdomain = await cdsHelper.getSubdomainForTenantId(tenantId);
|
|
@@ -272,9 +291,9 @@ const _checkPeriodicEventsSingleTenant = async (tenantId) => {
|
|
|
272
291
|
await periodicEvents.checkAndInsertPeriodicEvents(tx.context);
|
|
273
292
|
});
|
|
274
293
|
} catch (err) {
|
|
275
|
-
logger.error(
|
|
294
|
+
logger.error("Couldn't update periodic events for tenant! Next try after defined interval.", err, {
|
|
276
295
|
tenantId,
|
|
277
|
-
redisEnabled:
|
|
296
|
+
redisEnabled: eventQueueConfig.redisEnabled,
|
|
278
297
|
});
|
|
279
298
|
}
|
|
280
299
|
};
|