@cap-js-community/event-queue 1.10.6 → 1.10.8
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/Lock.cds +0 -1
- package/package.json +5 -5
- package/src/EventQueueProcessorBase.js +1 -1
- package/src/config.js +2 -1
- package/src/outbox/EventQueueGenericOutboxHandler.js +28 -28
- package/src/outbox/eventQueueAsOutbox.js +1 -1
- package/src/periodicEvents.js +10 -2
- package/src/processEventQueue.js +5 -2
- package/src/runner/openEvents.js +24 -25
- package/src/runner/runner.js +30 -20
- package/src/shared/distributedLock.js +32 -30
- package/src/shared/redis.js +10 -0
- package/srv/service/admin-service.cds +1 -0
package/db/Lock.cds
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cap-js-community/event-queue",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.8",
|
|
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",
|
|
@@ -55,10 +55,10 @@
|
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
57
|
"@cap-js/cds-test": "^0.3.0",
|
|
58
|
-
"@cap-js/hana": "^1.
|
|
59
|
-
"@cap-js/sqlite": "^
|
|
60
|
-
"@sap/cds": "^
|
|
61
|
-
"@sap/cds-dk": "^
|
|
58
|
+
"@cap-js/hana": "^2.1.0",
|
|
59
|
+
"@cap-js/sqlite": "^2.0.1",
|
|
60
|
+
"@sap/cds": "^9.0.3",
|
|
61
|
+
"@sap/cds-dk": "^9.0.4",
|
|
62
62
|
"eslint": "^8.57.0",
|
|
63
63
|
"eslint-config-prettier": "^9.1.0",
|
|
64
64
|
"eslint-plugin-jest": "^28.6.0",
|
|
@@ -1016,7 +1016,7 @@ class EventQueueProcessorBase {
|
|
|
1016
1016
|
|
|
1017
1017
|
// NOTE: do not pass current date as we always want to calc. a future date
|
|
1018
1018
|
const cronExpression = CronExpressionParser.parse(this.#eventConfig.cron, {
|
|
1019
|
-
tz: eventConfig.tz,
|
|
1019
|
+
tz: this.#eventConfig.tz,
|
|
1020
1020
|
});
|
|
1021
1021
|
return cronExpression.next();
|
|
1022
1022
|
}
|
package/src/config.js
CHANGED
|
@@ -28,6 +28,7 @@ const DEFAULT_CHECK_FOR_NEXT_CHUNK = true;
|
|
|
28
28
|
const SUFFIX_PERIODIC = "_PERIODIC";
|
|
29
29
|
const CAP_EVENT_TYPE = "CAP_OUTBOX";
|
|
30
30
|
const CAP_PARALLEL_DEFAULT = 5;
|
|
31
|
+
const CAP_MAX_ATTEMPTS_DEFAULT = 5;
|
|
31
32
|
const DELETE_TENANT_BLOCK_AFTER_MS = 5 * 60 * 1000;
|
|
32
33
|
const PRIORITIES = Object.values(Priorities);
|
|
33
34
|
const UTC_DEFAULT = false;
|
|
@@ -387,7 +388,7 @@ class Config {
|
|
|
387
388
|
kind: config.kind ?? "persistent-outbox",
|
|
388
389
|
selectMaxChunkSize: config.selectMaxChunkSize ?? config.chunkSize,
|
|
389
390
|
parallelEventProcessing: config.parallelEventProcessing ?? (config.parallel && CAP_PARALLEL_DEFAULT),
|
|
390
|
-
retryAttempts: config.retryAttempts ?? config.maxAttempts,
|
|
391
|
+
retryAttempts: config.retryAttempts ?? config.maxAttempts ?? CAP_MAX_ATTEMPTS_DEFAULT,
|
|
391
392
|
...config,
|
|
392
393
|
});
|
|
393
394
|
eventConfig.internalEvent = true;
|
|
@@ -61,7 +61,7 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
|
61
61
|
}
|
|
62
62
|
} else {
|
|
63
63
|
for (const actionName in genericClusterEvents) {
|
|
64
|
-
const
|
|
64
|
+
const reg = new cds.Request({
|
|
65
65
|
event: EVENT_QUEUE_ACTIONS.CLUSTER,
|
|
66
66
|
user: this.context.user,
|
|
67
67
|
eventQueue: {
|
|
@@ -74,14 +74,14 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
|
74
74
|
this.#clusterByDataProperty(actionName, genericClusterEvents[actionName], propertyName, cb),
|
|
75
75
|
},
|
|
76
76
|
});
|
|
77
|
-
const clusterResult = await this.__srvUnboxed.tx(this.context).send(
|
|
77
|
+
const clusterResult = await this.__srvUnboxed.tx(this.context).send(reg);
|
|
78
78
|
if (this.#validateCluster(clusterResult)) {
|
|
79
79
|
Object.assign(clusterMap, clusterResult);
|
|
80
80
|
} else {
|
|
81
81
|
this.logger.error(
|
|
82
82
|
"cluster result of handler is not valid. Check the documentation for the expected structure. Continuing without clustering!",
|
|
83
83
|
{
|
|
84
|
-
handler:
|
|
84
|
+
handler: reg.event,
|
|
85
85
|
clusterResult: JSON.stringify(clusterResult),
|
|
86
86
|
}
|
|
87
87
|
);
|
|
@@ -92,7 +92,7 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
for (const actionName in specificClusterEvents) {
|
|
95
|
-
const
|
|
95
|
+
const reg = new cds.Request({
|
|
96
96
|
event: `${EVENT_QUEUE_ACTIONS.CLUSTER}.${actionName}`,
|
|
97
97
|
user: this.context.user,
|
|
98
98
|
eventQueue: {
|
|
@@ -105,14 +105,14 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
|
105
105
|
this.#clusterByDataProperty(actionName, specificClusterEvents[actionName], propertyName, cb),
|
|
106
106
|
},
|
|
107
107
|
});
|
|
108
|
-
const clusterResult = await this.__srvUnboxed.tx(this.context).send(
|
|
108
|
+
const clusterResult = await this.__srvUnboxed.tx(this.context).send(reg);
|
|
109
109
|
if (this.#validateCluster(clusterResult)) {
|
|
110
110
|
Object.assign(clusterMap, clusterResult);
|
|
111
111
|
} else {
|
|
112
112
|
this.logger.error(
|
|
113
113
|
"cluster result of handler is not valid. Check the documentation for the expected structure. Continuing without clustering!",
|
|
114
114
|
{
|
|
115
|
-
handler:
|
|
115
|
+
handler: reg.event,
|
|
116
116
|
clusterResult: JSON.stringify(clusterResult),
|
|
117
117
|
}
|
|
118
118
|
);
|
|
@@ -264,12 +264,12 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
|
264
264
|
return payload;
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
-
const {
|
|
267
|
+
const { reg, userId } = this.#buildDispatchData(this.context, payload, {
|
|
268
268
|
queueEntries: [queueEntry],
|
|
269
269
|
});
|
|
270
|
-
|
|
271
|
-
await this.#setContextUser(this.context, userId,
|
|
272
|
-
const data = await this.__srvUnboxed.tx(this.context).send(
|
|
270
|
+
reg.event = handlerName;
|
|
271
|
+
await this.#setContextUser(this.context, userId, reg);
|
|
272
|
+
const data = await this.__srvUnboxed.tx(this.context).send(reg);
|
|
273
273
|
if (data) {
|
|
274
274
|
payload.data = data;
|
|
275
275
|
return payload;
|
|
@@ -285,12 +285,12 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
|
285
285
|
return await super.hookForExceededEvents(exceededEvent);
|
|
286
286
|
}
|
|
287
287
|
|
|
288
|
-
const {
|
|
288
|
+
const { reg, userId } = this.#buildDispatchData(this.context, exceededEvent.payload, {
|
|
289
289
|
queueEntries: [exceededEvent],
|
|
290
290
|
});
|
|
291
|
-
await this.#setContextUser(this.context, userId,
|
|
292
|
-
|
|
293
|
-
await this.__srvUnboxed.tx(this.context).send(
|
|
291
|
+
await this.#setContextUser(this.context, userId, reg);
|
|
292
|
+
reg.event = handlerName;
|
|
293
|
+
await this.__srvUnboxed.tx(this.context).send(reg);
|
|
294
294
|
}
|
|
295
295
|
|
|
296
296
|
// NOTE: Currently not exposed to CAP service; we wait for a valid use case
|
|
@@ -310,37 +310,37 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
|
310
310
|
|
|
311
311
|
async processPeriodicEvent(processContext, key, queueEntry) {
|
|
312
312
|
const [, action] = this.eventSubType.split(".");
|
|
313
|
-
const
|
|
314
|
-
await this.#setContextUser(processContext, config.userId,
|
|
315
|
-
await this.__srvUnboxed.tx(processContext).emit(
|
|
313
|
+
const reg = new cds.Event({ event: action, eventQueue: { processor: this, key, queueEntries: [queueEntry] } });
|
|
314
|
+
await this.#setContextUser(processContext, config.userId, reg);
|
|
315
|
+
await this.__srvUnboxed.tx(processContext).emit(reg);
|
|
316
316
|
}
|
|
317
317
|
|
|
318
318
|
#buildDispatchData(context, payload, { key, queueEntries } = {}) {
|
|
319
319
|
const { useEventQueueUser } = this.eventConfig;
|
|
320
320
|
const userId = useEventQueueUser ? config.userId : payload.contextUser;
|
|
321
|
-
const
|
|
321
|
+
const reg = payload._fromSend ? new cds.Request(payload) : new cds.Event(payload);
|
|
322
322
|
const invocationFn = payload._fromSend ? "send" : "emit";
|
|
323
|
-
delete
|
|
324
|
-
delete
|
|
325
|
-
|
|
326
|
-
return {
|
|
323
|
+
delete reg._fromSend;
|
|
324
|
+
delete reg.contextUser;
|
|
325
|
+
reg.eventQueue = { processor: this, key, queueEntries, payload };
|
|
326
|
+
return { reg, userId, invocationFn };
|
|
327
327
|
}
|
|
328
328
|
|
|
329
|
-
async #setContextUser(context, userId,
|
|
329
|
+
async #setContextUser(context, userId, reg) {
|
|
330
330
|
context.user = new cds.User.Privileged({
|
|
331
331
|
id: userId,
|
|
332
332
|
tokenInfo: await common.getTokenInfo(this.baseContext.tenant),
|
|
333
333
|
});
|
|
334
|
-
if (
|
|
335
|
-
|
|
334
|
+
if (reg) {
|
|
335
|
+
reg.user = context.user;
|
|
336
336
|
}
|
|
337
337
|
}
|
|
338
338
|
|
|
339
339
|
async processEvent(processContext, key, queueEntries, payload) {
|
|
340
340
|
try {
|
|
341
|
-
const { userId, invocationFn,
|
|
342
|
-
await this.#setContextUser(processContext, userId,
|
|
343
|
-
const result = await this.__srvUnboxed.tx(processContext)[invocationFn](
|
|
341
|
+
const { userId, invocationFn, reg } = this.#buildDispatchData(processContext, payload, { key, queueEntries });
|
|
342
|
+
await this.#setContextUser(processContext, userId, reg);
|
|
343
|
+
const result = await this.__srvUnboxed.tx(processContext)[invocationFn](reg);
|
|
344
344
|
return this.#determineResultStatus(result, queueEntries);
|
|
345
345
|
} catch (err) {
|
|
346
346
|
this.logger.error("error processing outboxed service call", err, {
|
|
@@ -52,7 +52,7 @@ function outboxed(srv, customOpts) {
|
|
|
52
52
|
outboxOpts = config.addCAPOutboxEventSpecificAction(srv.name, req.event);
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
if (
|
|
55
|
+
if (["persistent-outbox", "persistent-queue"].includes(outboxOpts.kind)) {
|
|
56
56
|
await _mapToEventAndPublish(context, srv.name, req, !!specificSettings);
|
|
57
57
|
return;
|
|
58
58
|
}
|
package/src/periodicEvents.js
CHANGED
|
@@ -7,9 +7,12 @@ const { EventProcessingStatus } = require("./constants");
|
|
|
7
7
|
const { processChunkedSync } = require("./shared/common");
|
|
8
8
|
const eventConfig = require("./config");
|
|
9
9
|
|
|
10
|
-
const COMPONENT_NAME = "/eventQueue/periodicEvents";
|
|
11
10
|
const CHUNK_SIZE_INSERT_PERIODIC_EVENTS = 4;
|
|
12
11
|
|
|
12
|
+
const ALLOWED_PERIODIC_SEC_DIFF = 30;
|
|
13
|
+
|
|
14
|
+
const COMPONENT_NAME = "/eventQueue/periodicEvents";
|
|
15
|
+
|
|
13
16
|
const checkAndInsertPeriodicEvents = async (context) => {
|
|
14
17
|
const now = new Date();
|
|
15
18
|
const tx = cds.tx(context);
|
|
@@ -117,11 +120,16 @@ const _determineChangedCron = (existingEventsCron) => {
|
|
|
117
120
|
const config = eventConfig.getEventConfig(event.type, event.subType);
|
|
118
121
|
const eventStartAfter = new Date(event.startAfter);
|
|
119
122
|
const eventCreatedAt = new Date(event.createdAt);
|
|
123
|
+
const randomOffset = config.randomOffset ?? eventConfig.randomOffsetPeriodicEvents ?? 0;
|
|
120
124
|
const cronExpression = CronExpressionParser.parse(config.cron, {
|
|
121
125
|
currentDate: eventCreatedAt,
|
|
122
126
|
tz: config.tz,
|
|
123
127
|
});
|
|
124
|
-
|
|
128
|
+
// report as changed if diff created than ALLOWED_PERIODIC_SEC_DIFF + the random event offset seconds
|
|
129
|
+
return (
|
|
130
|
+
Math.abs(cronExpression.next().getTime() - eventStartAfter.getTime()) >
|
|
131
|
+
(ALLOWED_PERIODIC_SEC_DIFF + randomOffset) * 1000
|
|
132
|
+
);
|
|
125
133
|
});
|
|
126
134
|
};
|
|
127
135
|
|
package/src/processEventQueue.js
CHANGED
|
@@ -318,8 +318,11 @@ const _checkEventIsBlocked = async (baseInstance) => {
|
|
|
318
318
|
|
|
319
319
|
const _processEvent = async (eventTypeInstance, processContext, key, queueEntries, payload) => {
|
|
320
320
|
let traceContext;
|
|
321
|
-
if (
|
|
322
|
-
|
|
321
|
+
if (eventTypeInstance.inheritTraceContext) {
|
|
322
|
+
const uniqueTraceContext = [...new Set(queueEntries.map((entry) => entry.context?.traceContext).filter((a) => a))];
|
|
323
|
+
if (uniqueTraceContext.length === 1) {
|
|
324
|
+
traceContext = uniqueTraceContext[0];
|
|
325
|
+
}
|
|
323
326
|
}
|
|
324
327
|
|
|
325
328
|
return await trace(
|
package/src/runner/openEvents.js
CHANGED
|
@@ -38,34 +38,33 @@ const getOpenQueueEntries = async (tx, filterAppSpecificEvents = true) => {
|
|
|
38
38
|
for (const { type, subType } of entries) {
|
|
39
39
|
if (eventConfig.isCapOutboxEvent(type)) {
|
|
40
40
|
const [srvName, actionName] = subType.split(".");
|
|
41
|
-
|
|
42
|
-
.to(srvName)
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
41
|
+
try {
|
|
42
|
+
const service = await cds.connect.to(srvName);
|
|
43
|
+
if (!filterAppSpecificEvents) {
|
|
44
|
+
return; // will be done in finally
|
|
45
|
+
}
|
|
47
46
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
result.push({ type, subType });
|
|
58
|
-
}
|
|
59
|
-
} else {
|
|
60
|
-
result.push({ type, subType });
|
|
61
|
-
}
|
|
62
|
-
})
|
|
63
|
-
.catch(() => {})
|
|
64
|
-
.finally(() => {
|
|
65
|
-
if (!filterAppSpecificEvents) {
|
|
47
|
+
if (!service) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
cds.outboxed(service);
|
|
51
|
+
if (actionName) {
|
|
52
|
+
config.addCAPOutboxEventSpecificAction(srvName, actionName);
|
|
53
|
+
}
|
|
54
|
+
if (filterAppSpecificEvents) {
|
|
55
|
+
if (eventConfig.shouldBeProcessedInThisApplication(type, subType)) {
|
|
66
56
|
result.push({ type, subType });
|
|
67
57
|
}
|
|
68
|
-
}
|
|
58
|
+
} else {
|
|
59
|
+
result.push({ type, subType });
|
|
60
|
+
}
|
|
61
|
+
} catch {
|
|
62
|
+
/* ignore catch */
|
|
63
|
+
} finally {
|
|
64
|
+
if (!filterAppSpecificEvents) {
|
|
65
|
+
result.push({ type, subType });
|
|
66
|
+
}
|
|
67
|
+
}
|
|
69
68
|
} else {
|
|
70
69
|
if (filterAppSpecificEvents) {
|
|
71
70
|
if (
|
package/src/runner/runner.js
CHANGED
|
@@ -138,8 +138,12 @@ const _executeEventsAllTenantsRedis = async (tenantIds) => {
|
|
|
138
138
|
if (!couldAcquireLock) {
|
|
139
139
|
return;
|
|
140
140
|
}
|
|
141
|
+
} catch (err) {
|
|
142
|
+
logger.error("executing event queue run for multi instance and tenant failed", err);
|
|
143
|
+
}
|
|
141
144
|
|
|
142
|
-
|
|
145
|
+
for (const tenantId of tenantIds) {
|
|
146
|
+
try {
|
|
143
147
|
await cds.tx({ tenant: tenantId }, async (tx) => {
|
|
144
148
|
await trace(
|
|
145
149
|
tx.context,
|
|
@@ -168,9 +172,9 @@ const _executeEventsAllTenantsRedis = async (tenantIds) => {
|
|
|
168
172
|
{ newRootSpan: true }
|
|
169
173
|
);
|
|
170
174
|
});
|
|
175
|
+
} catch (err) {
|
|
176
|
+
logger.error("broadcasting events for tenant failed", { tenantId }, err);
|
|
171
177
|
}
|
|
172
|
-
} catch (err) {
|
|
173
|
-
logger.info("executing event queue run for multi instance and tenant failed", err);
|
|
174
178
|
}
|
|
175
179
|
};
|
|
176
180
|
|
|
@@ -179,23 +183,29 @@ const _executeEventsAllTenants = async (tenantIds, runId) => {
|
|
|
179
183
|
for (const tenantId of tenantIds) {
|
|
180
184
|
const id = cds.utils.uuid();
|
|
181
185
|
let tenantContext;
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
186
|
+
let events;
|
|
187
|
+
try {
|
|
188
|
+
events = await trace(
|
|
189
|
+
{ id, tenant: tenantId },
|
|
190
|
+
"fetch-openEvents-and-tokenInfo",
|
|
191
|
+
async () => {
|
|
192
|
+
const user = await cds.tx({ tenant: tenantId }, async () => {
|
|
193
|
+
return new cds.User.Privileged({ id: config.userId, tokenInfo: await common.getTokenInfo(tenantId) });
|
|
194
|
+
});
|
|
195
|
+
tenantContext = {
|
|
196
|
+
tenant: tenantId,
|
|
197
|
+
user,
|
|
198
|
+
};
|
|
199
|
+
return await cds.tx(tenantContext, async (tx) => {
|
|
200
|
+
return await openEvents.getOpenQueueEntries(tx);
|
|
201
|
+
});
|
|
202
|
+
},
|
|
203
|
+
{ newRootSpan: true }
|
|
204
|
+
);
|
|
205
|
+
} catch (err) {
|
|
206
|
+
cds.log(COMPONENT_NAME).error("fetching open events for tenant failed", { tenantId }, err);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
199
209
|
|
|
200
210
|
if (!events.length) {
|
|
201
211
|
continue;
|
|
@@ -108,7 +108,7 @@ const _renewLockRedis = async (context, fullKey, expiryTime, { value = "true" }
|
|
|
108
108
|
|
|
109
109
|
const _checkLockExistsRedis = async (context, fullKey) => {
|
|
110
110
|
const client = await redis.createMainClientAndConnect(config.redisOptions);
|
|
111
|
-
return await client.
|
|
111
|
+
return await client.exists(fullKey);
|
|
112
112
|
};
|
|
113
113
|
|
|
114
114
|
const _checkLockExistsDb = async (context, fullKey) => {
|
|
@@ -191,43 +191,45 @@ const _generateKey = (context, tenantScoped, key) => {
|
|
|
191
191
|
};
|
|
192
192
|
|
|
193
193
|
const getAllLocksRedis = async () => {
|
|
194
|
-
const
|
|
195
|
-
const
|
|
196
|
-
const
|
|
197
|
-
for (const key of keys) {
|
|
198
|
-
const [, tenant, guidOrType, subType] = key.split("##");
|
|
199
|
-
if (subType) {
|
|
200
|
-
relevantKeys[key] = { tenant, guidOrType, subType };
|
|
201
|
-
}
|
|
202
|
-
}
|
|
194
|
+
const clientOrCluster = await redis.createMainClientAndConnect(config.redisOptions);
|
|
195
|
+
const output = [];
|
|
196
|
+
const results = [];
|
|
203
197
|
|
|
204
|
-
|
|
205
|
-
|
|
198
|
+
let clients;
|
|
199
|
+
if (redis.isClusterMode()) {
|
|
200
|
+
clients = clientOrCluster.masters.map((master) => master.client);
|
|
201
|
+
} else {
|
|
202
|
+
clients = [clientOrCluster];
|
|
206
203
|
}
|
|
207
204
|
|
|
208
|
-
|
|
209
|
-
for (const
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
205
|
+
// NOTE: use SCAN because KEYS is not supported for cluster clients
|
|
206
|
+
for (const client of clients) {
|
|
207
|
+
for await (const key of client.scanIterator({ MATCH: "EVENT*", COUNT: 1000 })) {
|
|
208
|
+
const [, tenant, guidOrType, subType] = key.split("##");
|
|
209
|
+
if (!subType) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
213
212
|
|
|
214
|
-
|
|
213
|
+
const pipeline = client.multi();
|
|
214
|
+
output.push({
|
|
215
|
+
tenant: tenant,
|
|
216
|
+
type: guidOrType,
|
|
217
|
+
subType: subType,
|
|
218
|
+
});
|
|
219
|
+
pipeline.ttl(key).get(key);
|
|
220
|
+
const replies = await pipeline.exec();
|
|
221
|
+
results.push(...replies);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
215
224
|
|
|
216
225
|
let counter = 0;
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
const
|
|
220
|
-
|
|
221
|
-
result.push({
|
|
222
|
-
tenant: value.tenant,
|
|
223
|
-
type: value.guidOrType,
|
|
224
|
-
subType: value.subType,
|
|
225
|
-
ttl,
|
|
226
|
-
createdAt,
|
|
227
|
-
});
|
|
226
|
+
for (const row of output) {
|
|
227
|
+
const ttl = results[counter];
|
|
228
|
+
const createdAt = results[counter + 1];
|
|
229
|
+
Object.assign(row, { ttl, createdAt });
|
|
228
230
|
counter = counter + 2;
|
|
229
231
|
}
|
|
230
|
-
return
|
|
232
|
+
return output;
|
|
231
233
|
};
|
|
232
234
|
|
|
233
235
|
const shutdownHandler = async () => {
|
package/src/shared/redis.js
CHANGED
|
@@ -178,6 +178,15 @@ const connectionCheck = async (options) => {
|
|
|
178
178
|
});
|
|
179
179
|
};
|
|
180
180
|
|
|
181
|
+
const isClusterMode = () => {
|
|
182
|
+
if (!("__clusterMode" in isClusterMode)) {
|
|
183
|
+
const env = getEnvInstance();
|
|
184
|
+
const { credentials } = env.redisRequires;
|
|
185
|
+
isClusterMode.__clusterMode = credentials.cluster_mode;
|
|
186
|
+
}
|
|
187
|
+
return isClusterMode.__clusterMode;
|
|
188
|
+
};
|
|
189
|
+
|
|
181
190
|
module.exports = {
|
|
182
191
|
createClientAndConnect,
|
|
183
192
|
createMainClientAndConnect,
|
|
@@ -186,4 +195,5 @@ module.exports = {
|
|
|
186
195
|
closeMainClient,
|
|
187
196
|
closeSubscribeClient,
|
|
188
197
|
connectionCheck,
|
|
198
|
+
isClusterMode,
|
|
189
199
|
};
|