@cap-js-community/event-queue 1.10.8 → 1.10.10
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 +5 -5
- package/src/config.js +3 -2
- package/src/periodicEvents.js +3 -0
- package/src/runner/openEvents.js +7 -13
- package/src/runner/runner.js +1 -0
- package/src/shared/cdsHelper.js +17 -6
- package/src/shared/distributedLock.js +3 -1
- package/srv/service/admin-service.cds +19 -9
- package/srv/service/admin-service.js +16 -2
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.10",
|
|
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",
|
|
@@ -54,17 +54,17 @@
|
|
|
54
54
|
"yaml": "^2.7.1"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
57
|
-
"@cap-js/cds-test": "^0.
|
|
57
|
+
"@cap-js/cds-test": "^0.4.0",
|
|
58
58
|
"@cap-js/hana": "^2.1.0",
|
|
59
59
|
"@cap-js/sqlite": "^2.0.1",
|
|
60
|
-
"@sap/cds": "^9.0
|
|
61
|
-
"@sap/cds-dk": "^9.0
|
|
60
|
+
"@sap/cds": "^9.1.0",
|
|
61
|
+
"@sap/cds-dk": "^9.1.0",
|
|
62
62
|
"eslint": "^8.57.0",
|
|
63
63
|
"eslint-config-prettier": "^9.1.0",
|
|
64
64
|
"eslint-plugin-jest": "^28.6.0",
|
|
65
65
|
"eslint-plugin-node": "^11.1.0",
|
|
66
66
|
"express": "^4.21.2",
|
|
67
|
-
"hdb": "^
|
|
67
|
+
"hdb": "^2.25.1",
|
|
68
68
|
"jest": "^29.7.0",
|
|
69
69
|
"prettier": "^2.8.8",
|
|
70
70
|
"sqlite3": "^5.1.7",
|
package/src/config.js
CHANGED
|
@@ -484,10 +484,11 @@ class Config {
|
|
|
484
484
|
delete base.interval;
|
|
485
485
|
}
|
|
486
486
|
|
|
487
|
-
|
|
487
|
+
const subType = `${name}.${fnName}`;
|
|
488
|
+
result[subType] = Object.assign(
|
|
488
489
|
{
|
|
489
490
|
type: CAP_EVENT_TYPE,
|
|
490
|
-
subType
|
|
491
|
+
subType,
|
|
491
492
|
impl: "./outbox/EventQueueGenericOutboxHandler",
|
|
492
493
|
internalEvent: true,
|
|
493
494
|
},
|
package/src/periodicEvents.js
CHANGED
|
@@ -15,6 +15,9 @@ const COMPONENT_NAME = "/eventQueue/periodicEvents";
|
|
|
15
15
|
|
|
16
16
|
const checkAndInsertPeriodicEvents = async (context) => {
|
|
17
17
|
const now = new Date();
|
|
18
|
+
cds.log(COMPONENT_NAME).info("updating periodic events", {
|
|
19
|
+
tenant: context.tenant,
|
|
20
|
+
});
|
|
18
21
|
const tx = cds.tx(context);
|
|
19
22
|
const baseCqn = SELECT.from(eventConfig.tableNameEventQueue)
|
|
20
23
|
.where([
|
package/src/runner/openEvents.js
CHANGED
|
@@ -40,23 +40,17 @@ const getOpenQueueEntries = async (tx, filterAppSpecificEvents = true) => {
|
|
|
40
40
|
const [srvName, actionName] = subType.split(".");
|
|
41
41
|
try {
|
|
42
42
|
const service = await cds.connect.to(srvName);
|
|
43
|
-
if (!filterAppSpecificEvents) {
|
|
44
|
-
return; // will be done in finally
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
if (!service) {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
cds.outboxed(service);
|
|
51
|
-
if (actionName) {
|
|
52
|
-
config.addCAPOutboxEventSpecificAction(srvName, actionName);
|
|
53
|
-
}
|
|
54
43
|
if (filterAppSpecificEvents) {
|
|
44
|
+
if (!service) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
cds.outboxed(service);
|
|
48
|
+
if (actionName) {
|
|
49
|
+
config.addCAPOutboxEventSpecificAction(srvName, actionName);
|
|
50
|
+
}
|
|
55
51
|
if (eventConfig.shouldBeProcessedInThisApplication(type, subType)) {
|
|
56
52
|
result.push({ type, subType });
|
|
57
53
|
}
|
|
58
|
-
} else {
|
|
59
|
-
result.push({ type, subType });
|
|
60
54
|
}
|
|
61
55
|
} catch {
|
|
62
56
|
/* ignore catch */
|
package/src/runner/runner.js
CHANGED
|
@@ -509,6 +509,7 @@ const _checkPeriodicEventsSingleTenantOneTime = async () => {
|
|
|
509
509
|
tenantScoped: false,
|
|
510
510
|
});
|
|
511
511
|
if (!couldAcquireLock) {
|
|
512
|
+
logger.info("skipping updating periodic events - lock not acquired");
|
|
512
513
|
return;
|
|
513
514
|
}
|
|
514
515
|
return await cds.tx({}, async (tx) => await periodicEvents.checkAndInsertPeriodicEvents(tx.context));
|
package/src/shared/cdsHelper.js
CHANGED
|
@@ -150,7 +150,9 @@ const getAllTenantIds = async () => {
|
|
|
150
150
|
}, []);
|
|
151
151
|
};
|
|
152
152
|
|
|
153
|
-
const
|
|
153
|
+
const TENANT_COLUMNS = ["subscribedSubdomain", "createdAt", "modifiedAt"];
|
|
154
|
+
|
|
155
|
+
const getAllTenantWithMetadata = async () => {
|
|
154
156
|
const response = await _getAllTenantBase();
|
|
155
157
|
if (!response) {
|
|
156
158
|
return null;
|
|
@@ -160,10 +162,19 @@ const getAllTenantWithSubdomain = async () => {
|
|
|
160
162
|
const tenantId = row.subscribedTenantId ?? row.tenant;
|
|
161
163
|
result = await result;
|
|
162
164
|
if (await common.isTenantIdValidCb(TenantIdCheckTypes.eventProcessing, tenantId)) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
const data = Object.entries(row).reduce(
|
|
166
|
+
(result, [key, value]) => {
|
|
167
|
+
if (TENANT_COLUMNS.includes(key)) {
|
|
168
|
+
result[key] = value;
|
|
169
|
+
} else {
|
|
170
|
+
result.metadata[key] = value;
|
|
171
|
+
}
|
|
172
|
+
return result;
|
|
173
|
+
},
|
|
174
|
+
{ metadata: {} }
|
|
175
|
+
);
|
|
176
|
+
data.metadata = JSON.stringify(data.metadata);
|
|
177
|
+
result.push(data);
|
|
167
178
|
}
|
|
168
179
|
return result;
|
|
169
180
|
}, []);
|
|
@@ -172,5 +183,5 @@ const getAllTenantWithSubdomain = async () => {
|
|
|
172
183
|
module.exports = {
|
|
173
184
|
executeInNewTransaction,
|
|
174
185
|
getAllTenantIds,
|
|
175
|
-
|
|
186
|
+
getAllTenantWithMetadata,
|
|
176
187
|
};
|
|
@@ -121,8 +121,9 @@ const _checkLockExistsDb = async (context, fullKey) => {
|
|
|
121
121
|
|
|
122
122
|
const _releaseLockRedis = async (context, fullKey) => {
|
|
123
123
|
const client = await redis.createMainClientAndConnect(config.redisOptions);
|
|
124
|
-
await client.del(fullKey);
|
|
124
|
+
const result = await client.del(fullKey);
|
|
125
125
|
delete existingLocks[fullKey];
|
|
126
|
+
return result === 1;
|
|
126
127
|
};
|
|
127
128
|
|
|
128
129
|
const _releaseLockDb = async (context, fullKey) => {
|
|
@@ -130,6 +131,7 @@ const _releaseLockDb = async (context, fullKey) => {
|
|
|
130
131
|
await tx.run(DELETE.from(config.tableNameEventLock).where("code =", fullKey));
|
|
131
132
|
});
|
|
132
133
|
delete existingLocks[fullKey];
|
|
134
|
+
return true;
|
|
133
135
|
};
|
|
134
136
|
|
|
135
137
|
const _acquireLockDB = async (
|
|
@@ -13,14 +13,14 @@ service EventQueueAdminService {
|
|
|
13
13
|
null as space: String,
|
|
14
14
|
*
|
|
15
15
|
} actions {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
16
|
+
action setStatusAndAttempts(
|
|
17
|
+
// TODO: remove tenant as soon as CAP issue is fixed https://github.tools.sap/cap/issues/issues/18445
|
|
18
|
+
@mandatory
|
|
19
|
+
tenant: String,
|
|
20
|
+
status: db.Status,
|
|
21
|
+
@assert.range: [0,100]
|
|
22
|
+
attempts: Integer) returns Event;
|
|
23
|
+
}
|
|
24
24
|
|
|
25
25
|
@cds.persistence.skip
|
|
26
26
|
@readonly
|
|
@@ -32,12 +32,22 @@ service EventQueueAdminService {
|
|
|
32
32
|
space: String;
|
|
33
33
|
ttl: Integer;
|
|
34
34
|
createdAt: Integer;
|
|
35
|
-
}
|
|
35
|
+
} actions {
|
|
36
|
+
action releaseLock(
|
|
37
|
+
// TODO: remove tenant as soon as CAP issue is fixed https://github.tools.sap/cap/issues/issues/18445
|
|
38
|
+
@mandatory
|
|
39
|
+
tenant: String,
|
|
40
|
+
@mandatory
|
|
41
|
+
type: String,
|
|
42
|
+
@mandatory
|
|
43
|
+
subType: String) returns Boolean;
|
|
44
|
+
}
|
|
36
45
|
|
|
37
46
|
@readonly
|
|
38
47
|
@cds.persistence.skip
|
|
39
48
|
entity Tenant {
|
|
40
49
|
Key ID: String;
|
|
41
50
|
subdomain: String;
|
|
51
|
+
metadata: String;
|
|
42
52
|
}
|
|
43
53
|
}
|
|
@@ -5,6 +5,7 @@ const cdsHelper = require("../../src/shared/cdsHelper");
|
|
|
5
5
|
const { EventProcessingStatus } = require("../../src");
|
|
6
6
|
const config = require("../../src/config");
|
|
7
7
|
const distributedLock = require("../../src/shared/distributedLock");
|
|
8
|
+
const redisPub = require("../../src/redis/redisPub");
|
|
8
9
|
|
|
9
10
|
module.exports = class AdminService extends cds.ApplicationService {
|
|
10
11
|
async init() {
|
|
@@ -61,7 +62,7 @@ module.exports = class AdminService extends cds.ApplicationService {
|
|
|
61
62
|
});
|
|
62
63
|
|
|
63
64
|
this.on("READ", Tenant, async () => {
|
|
64
|
-
const tenants = await cdsHelper.
|
|
65
|
+
const tenants = await cdsHelper.getAllTenantWithMetadata();
|
|
65
66
|
return tenants ?? [];
|
|
66
67
|
});
|
|
67
68
|
|
|
@@ -82,14 +83,27 @@ module.exports = class AdminService extends cds.ApplicationService {
|
|
|
82
83
|
return req.reject(400, "No status or attempts provided");
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
await cds.tx({ tenant, headers: { "z-id": tenant } }, async () => {
|
|
86
|
+
const event = await cds.tx({ tenant, headers: { "z-id": tenant } }, async () => {
|
|
87
|
+
const event = await SELECT.one.from(EventDb).where({ ID: req.params[0].ID ?? req.params[0] });
|
|
86
88
|
await UPDATE.entity(EventDb)
|
|
87
89
|
.set(updateData)
|
|
88
90
|
.where({ ID: req.params[0].ID ?? req.params[0] });
|
|
91
|
+
return event;
|
|
92
|
+
});
|
|
93
|
+
redisPub.broadcastEvent(tenant, event).catch(() => {
|
|
94
|
+
/* ignore errors */
|
|
89
95
|
});
|
|
90
96
|
return await this.send(new cds.Request({ query: req.query, headers: req.headers }));
|
|
91
97
|
});
|
|
92
98
|
|
|
99
|
+
this.on("releaseLock", async (req) => {
|
|
100
|
+
cds.log("eventQueue").info("Releasing event-queue lock", req.data);
|
|
101
|
+
const { tenant, type, subType } = req.data;
|
|
102
|
+
return await cds.tx({ tenant }, async (tx) => {
|
|
103
|
+
return await distributedLock.releaseLock(tx.context, [type, subType].join("##"));
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
93
107
|
await super.init();
|
|
94
108
|
}
|
|
95
109
|
|