@cap-js-community/event-queue 1.10.0-beta.1 → 1.10.0-beta.3
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/config.js +25 -12
- package/src/initialize.js +1 -0
- package/src/outbox/EventQueueGenericOutboxHandler.js +94 -40
- package/src/outbox/eventQueueAsOutbox.js +2 -1
- package/src/runner/runner.js +4 -4
- package/src/shared/distributedLock.js +2 -3
- package/src/shared/redis.js +5 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cap-js-community/event-queue",
|
|
3
|
-
"version": "1.10.0-beta.
|
|
3
|
+
"version": "1.10.0-beta.3",
|
|
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",
|
package/src/config.js
CHANGED
|
@@ -10,9 +10,12 @@ const { Priorities } = require("./constants");
|
|
|
10
10
|
|
|
11
11
|
const FOR_UPDATE_TIMEOUT = 10;
|
|
12
12
|
const GLOBAL_TX_TIMEOUT = 30 * 60 * 1000;
|
|
13
|
-
const
|
|
13
|
+
const REDIS_PREFIX = "EVENT_QUEUE";
|
|
14
|
+
const REDIS_CONFIG_CHANNEL = "CONFIG_CHANNEL";
|
|
14
15
|
const REDIS_OFFBOARD_TENANT_CHANNEL = "REDIS_OFFBOARD_TENANT_CHANNEL";
|
|
15
|
-
const REDIS_CONFIG_BLOCKLIST_CHANNEL = "
|
|
16
|
+
const REDIS_CONFIG_BLOCKLIST_CHANNEL = "REDIS_CONFIG_BLOCKLIST_CHANNEL";
|
|
17
|
+
const COMMAND_BLOCK = "EVENT_BLOCK";
|
|
18
|
+
const COMMAND_UNBLOCK = "EVENT_UNBLOCK";
|
|
16
19
|
const COMPONENT_NAME = "/eventQueue/config";
|
|
17
20
|
const MIN_INTERVAL_SEC = 10;
|
|
18
21
|
const DEFAULT_LOAD = 1;
|
|
@@ -22,8 +25,6 @@ const DEFAULT_KEEP_ALIVE_INTERVAL = 60;
|
|
|
22
25
|
const DEFAULT_MAX_FACTOR_STUCK_2_KEEP_ALIVE_INTERVAL = 3.5;
|
|
23
26
|
const DEFAULT_INHERIT_TRACE_CONTEXT = true;
|
|
24
27
|
const SUFFIX_PERIODIC = "_PERIODIC";
|
|
25
|
-
const COMMAND_BLOCK = "EVENT_QUEUE_EVENT_BLOCK";
|
|
26
|
-
const COMMAND_UNBLOCK = "EVENT_QUEUE_EVENT_UNBLOCK";
|
|
27
28
|
const CAP_EVENT_TYPE = "CAP_OUTBOX";
|
|
28
29
|
const CAP_PARALLEL_DEFAULT = 5;
|
|
29
30
|
const DELETE_TENANT_BLOCK_AFTER_MS = 5 * 60 * 1000;
|
|
@@ -106,6 +107,7 @@ class Config {
|
|
|
106
107
|
#unsubscribeHandlers = [];
|
|
107
108
|
#unsubscribedTenants = {};
|
|
108
109
|
#cronTimezone;
|
|
110
|
+
#redisNamespace;
|
|
109
111
|
#publishEventBlockList;
|
|
110
112
|
#crashOnRedisUnavailable;
|
|
111
113
|
#tenantIdFilterTokenInfoCb;
|
|
@@ -182,7 +184,7 @@ class Config {
|
|
|
182
184
|
|
|
183
185
|
attachConfigChangeHandler() {
|
|
184
186
|
this.#attachBlockListChangeHandler();
|
|
185
|
-
redis.subscribeRedisChannel(this
|
|
187
|
+
redis.subscribeRedisChannel(this.redisOptions, REDIS_CONFIG_CHANNEL, (messageData) => {
|
|
186
188
|
try {
|
|
187
189
|
const { key, value } = JSON.parse(messageData);
|
|
188
190
|
if (this[key] !== value) {
|
|
@@ -199,7 +201,7 @@ class Config {
|
|
|
199
201
|
|
|
200
202
|
attachRedisUnsubscribeHandler() {
|
|
201
203
|
this.#logger.info("attached redis handle for unsubscribe events");
|
|
202
|
-
redis.subscribeRedisChannel(this
|
|
204
|
+
redis.subscribeRedisChannel(this.redisOptions, REDIS_OFFBOARD_TENANT_CHANNEL, (messageData) => {
|
|
203
205
|
try {
|
|
204
206
|
const { tenantId } = JSON.parse(messageData);
|
|
205
207
|
this.#logger.info("received unsubscribe broadcast event", { tenantId });
|
|
@@ -229,7 +231,7 @@ class Config {
|
|
|
229
231
|
handleUnsubscribe(tenantId) {
|
|
230
232
|
if (this.redisEnabled) {
|
|
231
233
|
redis
|
|
232
|
-
.publishMessage(this
|
|
234
|
+
.publishMessage(this.redisOptions, REDIS_OFFBOARD_TENANT_CHANNEL, JSON.stringify({ tenantId }))
|
|
233
235
|
.catch((error) => {
|
|
234
236
|
this.#logger.error(`publishing tenant unsubscribe failed. tenantId: ${tenantId}`, error);
|
|
235
237
|
});
|
|
@@ -247,13 +249,13 @@ class Config {
|
|
|
247
249
|
this.#logger.info("redis not connected, config change won't be published", { key, value });
|
|
248
250
|
return;
|
|
249
251
|
}
|
|
250
|
-
redis.publishMessage(this
|
|
252
|
+
redis.publishMessage(this.redisOptions, REDIS_CONFIG_CHANNEL, JSON.stringify({ key, value })).catch((error) => {
|
|
251
253
|
this.#logger.error(`publishing config change failed key: ${key}, value: ${value}`, error);
|
|
252
254
|
});
|
|
253
255
|
}
|
|
254
256
|
|
|
255
257
|
#attachBlockListChangeHandler() {
|
|
256
|
-
redis.subscribeRedisChannel(this
|
|
258
|
+
redis.subscribeRedisChannel(this.redisOptions, REDIS_CONFIG_BLOCKLIST_CHANNEL, (messageData) => {
|
|
257
259
|
try {
|
|
258
260
|
const { command, key, tenant } = JSON.parse(messageData);
|
|
259
261
|
if (command === COMMAND_BLOCK) {
|
|
@@ -283,7 +285,7 @@ class Config {
|
|
|
283
285
|
|
|
284
286
|
redis
|
|
285
287
|
.publishMessage(
|
|
286
|
-
this
|
|
288
|
+
this.redisOptions,
|
|
287
289
|
REDIS_CONFIG_BLOCKLIST_CHANNEL,
|
|
288
290
|
JSON.stringify({ command: COMMAND_BLOCK, key, tenant })
|
|
289
291
|
)
|
|
@@ -316,7 +318,7 @@ class Config {
|
|
|
316
318
|
|
|
317
319
|
redis
|
|
318
320
|
.publishMessage(
|
|
319
|
-
this
|
|
321
|
+
this.redisOptions,
|
|
320
322
|
REDIS_CONFIG_BLOCKLIST_CHANNEL,
|
|
321
323
|
JSON.stringify({ command: COMMAND_UNBLOCK, key, tenant })
|
|
322
324
|
)
|
|
@@ -874,7 +876,18 @@ class Config {
|
|
|
874
876
|
}
|
|
875
877
|
|
|
876
878
|
get redisOptions() {
|
|
877
|
-
return
|
|
879
|
+
return {
|
|
880
|
+
...this.#redisOptions,
|
|
881
|
+
redisNamespace: `${[REDIS_PREFIX, this.redisNamespace].filter((a) => a).join("_")}`,
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
set redisNamespace(value) {
|
|
886
|
+
this.#redisNamespace = value;
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
get redisNamespace() {
|
|
890
|
+
return this.#redisNamespace;
|
|
878
891
|
}
|
|
879
892
|
|
|
880
893
|
set insertEventsBeforeCommit(value) {
|
package/src/initialize.js
CHANGED
|
@@ -57,36 +57,43 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
|
57
57
|
return super.clusterQueueEntries(queueEntriesWithPayloadMap);
|
|
58
58
|
}
|
|
59
59
|
const { genericClusterEvents, specificClusterEvents } = this.#clusterByAction(queueEntriesWithPayloadMap);
|
|
60
|
+
|
|
60
61
|
if (Object.keys(genericClusterEvents).length) {
|
|
61
62
|
if (!this.__genericClusterRelevantAndAvailable) {
|
|
62
|
-
|
|
63
|
+
for (const actionName in genericClusterEvents) {
|
|
64
|
+
await super.clusterQueueEntries(genericClusterEvents[actionName]);
|
|
65
|
+
}
|
|
63
66
|
} else {
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
67
|
+
for (const actionName in genericClusterEvents) {
|
|
68
|
+
const msg = new cds.Request({
|
|
69
|
+
event: `clusterQueueEntries`,
|
|
70
|
+
eventQueue: {
|
|
71
|
+
processor: this,
|
|
72
|
+
clusterByPayloadProperty: (propertyName, cb) =>
|
|
73
|
+
this.clusterByPayloadProperty(actionName, genericClusterEvents[actionName], propertyName, cb),
|
|
74
|
+
clusterByEventProperty: (propertyName, cb) =>
|
|
75
|
+
this.clusterByEventProperty(actionName, genericClusterEvents[actionName], propertyName, cb),
|
|
76
|
+
clusterByDataProperty: (propertyName, cb) =>
|
|
77
|
+
this.clusterByDataProperty(actionName, specificClusterEvents[actionName], propertyName, cb),
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
const handlerCluster = await this.__srvUnboxed.tx(this.context).send(msg);
|
|
81
|
+
this.#addToProcessingMap(handlerCluster);
|
|
82
|
+
}
|
|
77
83
|
}
|
|
78
84
|
}
|
|
79
85
|
|
|
80
86
|
for (const actionName in specificClusterEvents) {
|
|
81
87
|
const msg = new cds.Request({
|
|
82
88
|
event: `clusterQueueEntries.${actionName}`,
|
|
83
|
-
data: { queueEntriesWithPayloadMap: specificClusterEvents[actionName] },
|
|
84
89
|
eventQueue: {
|
|
85
90
|
processor: this,
|
|
86
|
-
clusterByPayloadProperty: (propertyName) =>
|
|
87
|
-
|
|
88
|
-
clusterByEventProperty: (propertyName) =>
|
|
89
|
-
|
|
91
|
+
clusterByPayloadProperty: (propertyName, cb) =>
|
|
92
|
+
this.clusterByPayloadProperty(actionName, specificClusterEvents[actionName], propertyName, cb),
|
|
93
|
+
clusterByEventProperty: (propertyName, cb) =>
|
|
94
|
+
this.clusterByEventProperty(actionName, specificClusterEvents[actionName], propertyName, cb),
|
|
95
|
+
clusterByDataProperty: (propertyName, cb) =>
|
|
96
|
+
this.clusterByDataProperty(actionName, specificClusterEvents[actionName], propertyName, cb),
|
|
90
97
|
},
|
|
91
98
|
});
|
|
92
99
|
const handlerCluster = await this.__srvUnboxed.tx(this.context).send(msg);
|
|
@@ -94,26 +101,69 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
|
94
101
|
}
|
|
95
102
|
}
|
|
96
103
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
result
|
|
100
|
-
|
|
101
|
-
payload,
|
|
102
|
-
};
|
|
103
|
-
result[payload[propertyName]].queueEntries.push(queueEntry);
|
|
104
|
+
clusterBase(queueEntriesWithPayloadMap, propertyName, refCb, cb) {
|
|
105
|
+
const clusters = Object.entries(queueEntriesWithPayloadMap).reduce((result, [, { queueEntry, payload }]) => {
|
|
106
|
+
const ref = refCb(result, payload, queueEntry);
|
|
107
|
+
ref.queueEntries.push(queueEntry);
|
|
104
108
|
return result;
|
|
105
109
|
}, {});
|
|
110
|
+
|
|
111
|
+
if (cb) {
|
|
112
|
+
for (const clustersKey in clusters) {
|
|
113
|
+
const clusterData = clusters[clustersKey];
|
|
114
|
+
const clusterResult = cb(
|
|
115
|
+
clustersKey.split("##").shift(),
|
|
116
|
+
clusterData.queueEntries.map((entry) => entry.payload.data)
|
|
117
|
+
);
|
|
118
|
+
if (!clusterResult) {
|
|
119
|
+
throw new Error("hmm??");
|
|
120
|
+
}
|
|
121
|
+
clusterData.payload.data = clusterResult;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return clusters;
|
|
106
125
|
}
|
|
107
126
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
127
|
+
#resolveRefBase(result, propertyName, actionName, payload, startRef) {
|
|
128
|
+
const parts = propertyName.split(".");
|
|
129
|
+
const data = JSON.parse(JSON.stringify(payload.data));
|
|
130
|
+
let ref = startRef;
|
|
131
|
+
for (const part of parts) {
|
|
132
|
+
ref = ref[part];
|
|
133
|
+
}
|
|
134
|
+
const key = [actionName, ref].join("##");
|
|
135
|
+
result[key] ??= {
|
|
136
|
+
queueEntries: [],
|
|
137
|
+
payload: { ...payload, data },
|
|
138
|
+
};
|
|
139
|
+
return result[key];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
clusterByPayloadProperty(actionName, queueEntriesWithPayloadMap, propertyName, cb) {
|
|
143
|
+
return this.clusterBase(
|
|
144
|
+
queueEntriesWithPayloadMap,
|
|
145
|
+
propertyName,
|
|
146
|
+
(result, payload) => this.#resolveRefBase(result, propertyName, actionName, payload, payload),
|
|
147
|
+
cb
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
clusterByEventProperty(actionName, queueEntriesWithPayloadMap, propertyName, cb) {
|
|
152
|
+
return this.clusterBase(
|
|
153
|
+
queueEntriesWithPayloadMap,
|
|
154
|
+
propertyName,
|
|
155
|
+
(result, payload, queueEntry) => this.#resolveRefBase(result, propertyName, actionName, payload, queueEntry),
|
|
156
|
+
cb
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
clusterByDataProperty(actionName, queueEntriesWithPayloadMap, propertyName, cb) {
|
|
161
|
+
return this.clusterBase(
|
|
162
|
+
queueEntriesWithPayloadMap,
|
|
163
|
+
propertyName,
|
|
164
|
+
(result, payload) => this.#resolveRefBase(result, propertyName, actionName, payload, payload.data),
|
|
165
|
+
cb
|
|
166
|
+
);
|
|
117
167
|
}
|
|
118
168
|
|
|
119
169
|
#clusterByAction(queueEntriesWithPayloadMap) {
|
|
@@ -124,7 +174,8 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
|
124
174
|
result.specificClusterEvents[clusterData.payload.event] ??= {};
|
|
125
175
|
result.specificClusterEvents[clusterData.payload.event][eventId] = clusterData;
|
|
126
176
|
} else {
|
|
127
|
-
result.genericClusterEvents[
|
|
177
|
+
result.genericClusterEvents[clusterData.payload.event] ??= {};
|
|
178
|
+
result.genericClusterEvents[clusterData.payload.event][eventId] = clusterData;
|
|
128
179
|
}
|
|
129
180
|
return result;
|
|
130
181
|
},
|
|
@@ -169,7 +220,7 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
|
169
220
|
queueEntries: [queueEntry],
|
|
170
221
|
});
|
|
171
222
|
msg.event = handlerName;
|
|
172
|
-
await this.#setContextUser(this.context, userId);
|
|
223
|
+
await this.#setContextUser(this.context, userId, msg);
|
|
173
224
|
const data = await this.__srvUnboxed.tx(this.context).send(msg);
|
|
174
225
|
if (data) {
|
|
175
226
|
payload.data = data;
|
|
@@ -204,7 +255,7 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
|
204
255
|
async processPeriodicEvent(processContext, key, queueEntry) {
|
|
205
256
|
const [, action] = this.eventSubType.split(".");
|
|
206
257
|
const msg = new cds.Event({ event: action, eventQueue: { processor: this, key, queueEntries: [queueEntry] } });
|
|
207
|
-
await this.#setContextUser(processContext, config.userId);
|
|
258
|
+
await this.#setContextUser(processContext, config.userId, msg);
|
|
208
259
|
await this.__srvUnboxed.tx(processContext).emit(msg);
|
|
209
260
|
}
|
|
210
261
|
|
|
@@ -219,17 +270,20 @@ class EventQueueGenericOutboxHandler extends EventQueueBaseClass {
|
|
|
219
270
|
return { msg, userId, invocationFn };
|
|
220
271
|
}
|
|
221
272
|
|
|
222
|
-
async #setContextUser(context, userId) {
|
|
273
|
+
async #setContextUser(context, userId, data) {
|
|
223
274
|
context.user = new cds.User.Privileged({
|
|
224
275
|
id: userId,
|
|
225
276
|
authInfo: await common.getTokenInfo(this.baseContext.tenant),
|
|
226
277
|
});
|
|
278
|
+
if (data) {
|
|
279
|
+
data.user = context.user;
|
|
280
|
+
}
|
|
227
281
|
}
|
|
228
282
|
|
|
229
283
|
async processEvent(processContext, key, queueEntries, payload) {
|
|
230
284
|
try {
|
|
231
285
|
const { userId, invocationFn, msg } = this.#buildDispatchData(processContext, payload, { key, queueEntries });
|
|
232
|
-
await this.#setContextUser(processContext, userId);
|
|
286
|
+
await this.#setContextUser(processContext, userId, msg);
|
|
233
287
|
const result = await this.__srvUnboxed.tx(processContext)[invocationFn](msg);
|
|
234
288
|
return this.#determineResultStatus(result, queueEntries);
|
|
235
289
|
} catch (err) {
|
|
@@ -47,7 +47,8 @@ function outboxed(srv, customOpts) {
|
|
|
47
47
|
customOpts || {}
|
|
48
48
|
);
|
|
49
49
|
config.addCAPOutboxEventBase(srv.name, outboxOpts);
|
|
50
|
-
|
|
50
|
+
// TODO: check req.event ?? req.method
|
|
51
|
+
const specificSettings = config.getCdsOutboxEventSpecificConfig(srv.name, req.event);
|
|
51
52
|
if (specificSettings) {
|
|
52
53
|
outboxOpts = config.addCAPOutboxEventSpecificAction(srv.name, req.event);
|
|
53
54
|
}
|
package/src/runner/runner.js
CHANGED
|
@@ -18,10 +18,10 @@ const { runEventCombinationForTenant } = require("./runnerHelper");
|
|
|
18
18
|
const { trace } = require("../shared/openTelemetry");
|
|
19
19
|
|
|
20
20
|
const COMPONENT_NAME = "/eventQueue/runner";
|
|
21
|
-
const EVENT_QUEUE_RUN_ID = "
|
|
22
|
-
const EVENT_QUEUE_RUN_TS = "
|
|
23
|
-
const EVENT_QUEUE_RUN_REDIS_CHECK = "
|
|
24
|
-
const EVENT_QUEUE_UPDATE_PERIODIC_EVENTS = "
|
|
21
|
+
const EVENT_QUEUE_RUN_ID = "RUN_ID";
|
|
22
|
+
const EVENT_QUEUE_RUN_TS = "RUN_TS";
|
|
23
|
+
const EVENT_QUEUE_RUN_REDIS_CHECK = "RUN_REDIS_CHECK";
|
|
24
|
+
const EVENT_QUEUE_UPDATE_PERIODIC_EVENTS = "UPDATE_PERIODIC_EVENTS";
|
|
25
25
|
let OFFSET_FIRST_RUN = 10 * 1000;
|
|
26
26
|
|
|
27
27
|
let tenantIdHash;
|
|
@@ -4,7 +4,6 @@ const redis = require("./redis");
|
|
|
4
4
|
const config = require("../config");
|
|
5
5
|
const cdsHelper = require("./cdsHelper");
|
|
6
6
|
|
|
7
|
-
const KEY_PREFIX = "EVENT_QUEUE";
|
|
8
7
|
const existingLocks = {};
|
|
9
8
|
const REDIS_COMMAND_OK = "OK";
|
|
10
9
|
const COMPONENT_NAME = "/eventQueue/distributedLock";
|
|
@@ -176,10 +175,10 @@ const _acquireLockDB = async (context, fullKey, expiryTime, { value = "true", ov
|
|
|
176
175
|
};
|
|
177
176
|
|
|
178
177
|
const _generateKey = (context, tenantScoped, key) => {
|
|
179
|
-
const keyParts = [];
|
|
178
|
+
const keyParts = [config.redisOptions.redisNamespace];
|
|
180
179
|
tenantScoped && keyParts.push(context.tenant);
|
|
181
180
|
keyParts.push(key);
|
|
182
|
-
return `${
|
|
181
|
+
return `${keyParts.join("##")}`;
|
|
183
182
|
};
|
|
184
183
|
|
|
185
184
|
const shutdownHandler = async () => {
|
package/src/shared/redis.js
CHANGED
|
@@ -46,6 +46,7 @@ const _createClientBase = (redisOptions = {}) => {
|
|
|
46
46
|
password: redisOptions.password ?? options.password ?? credentials.password,
|
|
47
47
|
socket,
|
|
48
48
|
});
|
|
49
|
+
delete socketOptions.redisNamespace;
|
|
49
50
|
if (credentials.cluster_mode) {
|
|
50
51
|
return redis.createCluster({
|
|
51
52
|
rootNodes: [socketOptions],
|
|
@@ -106,9 +107,10 @@ const _subscribeChannels = (options, subscribedChannels, errorHandlerCreateClien
|
|
|
106
107
|
if (client._subscribedChannels[channel]) {
|
|
107
108
|
continue;
|
|
108
109
|
}
|
|
109
|
-
|
|
110
|
+
const prefixedChannelName = [options.redisNamespace, channel].join("_");
|
|
111
|
+
cds.log(COMPONENT_NAME).info("subscribe redis client connected channel", { channel: prefixedChannelName });
|
|
110
112
|
client
|
|
111
|
-
.subscribe(
|
|
113
|
+
.subscribe(prefixedChannelName, fn)
|
|
112
114
|
.then(() => {
|
|
113
115
|
client._subscribedChannels ??= {};
|
|
114
116
|
client._subscribedChannels[channel] = 1;
|
|
@@ -133,7 +135,7 @@ const _subscribeChannels = (options, subscribedChannels, errorHandlerCreateClien
|
|
|
133
135
|
|
|
134
136
|
const publishMessage = async (options, channel, message) => {
|
|
135
137
|
const client = await createMainClientAndConnect(options);
|
|
136
|
-
return await client.publish(channel, message);
|
|
138
|
+
return await client.publish([options.redisNamespace, channel].join("_"), message);
|
|
137
139
|
};
|
|
138
140
|
|
|
139
141
|
const closeMainClient = async () => {
|