@hahnpro/flow-sdk 4.15.0 → 4.15.1
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/dist/FlowApplication.d.ts +3 -0
- package/dist/FlowApplication.js +33 -5
- package/package.json +6 -6
|
@@ -15,10 +15,13 @@ export declare class FlowApplication {
|
|
|
15
15
|
private elements;
|
|
16
16
|
private logger;
|
|
17
17
|
private outputStreamMap;
|
|
18
|
+
private outputQueueMetrics;
|
|
18
19
|
private performanceMap;
|
|
19
20
|
private properties;
|
|
20
21
|
private _rpcClient;
|
|
21
22
|
constructor(modules: ClassType<any>[], flow: Flow, logger?: Logger, amqpConnection?: AmqpConnection, skipApi?: boolean);
|
|
23
|
+
private setQueueMetrics;
|
|
24
|
+
private updateMetrics;
|
|
22
25
|
subscribe: (streamId: string, observer: PartialObserver<FlowEvent>) => import("rxjs").Subscription;
|
|
23
26
|
emit: (event: FlowEvent) => void;
|
|
24
27
|
emitPartial: (completeEvent: FlowEvent, partialEvent: FlowEvent) => void;
|
package/dist/FlowApplication.js
CHANGED
|
@@ -14,7 +14,9 @@ const api_1 = require("./api");
|
|
|
14
14
|
const utils_1 = require("./utils");
|
|
15
15
|
const FlowLogger_1 = require("./FlowLogger");
|
|
16
16
|
const RpcClient_1 = require("./RpcClient");
|
|
17
|
-
const MAX_EVENT_SIZE_BYTES = 512 * 1024;
|
|
17
|
+
const MAX_EVENT_SIZE_BYTES = +process.env.MAX_EVENT_SIZE_BYTES || 512 * 1024;
|
|
18
|
+
const WARN_EVENT_PROCESSING_SEC = +process.env.WARN_EVENT_PROCESSING_SEC || 60;
|
|
19
|
+
const WARN_EVENT_QUEUE_SIZE = +process.env.WARN_EVENT_QUEUE_SIZE || 100;
|
|
18
20
|
class FlowApplication {
|
|
19
21
|
constructor(modules, flow, logger, amqpConnection, skipApi) {
|
|
20
22
|
var _a, _b, _c;
|
|
@@ -22,7 +24,31 @@ class FlowApplication {
|
|
|
22
24
|
this.declarations = {};
|
|
23
25
|
this.elements = {};
|
|
24
26
|
this.outputStreamMap = new Map();
|
|
27
|
+
this.outputQueueMetrics = new Map();
|
|
25
28
|
this.performanceMap = new Map();
|
|
29
|
+
this.setQueueMetrics = (id) => {
|
|
30
|
+
const metrics = this.outputQueueMetrics.get(id) || { size: 0, lastAdd: 0, lastRemove: Date.now(), warnings: 0 };
|
|
31
|
+
const secsProcessing = Math.round((metrics.lastAdd - metrics.lastRemove) / 1000);
|
|
32
|
+
metrics.size++;
|
|
33
|
+
metrics.lastAdd = Date.now();
|
|
34
|
+
if (secsProcessing >= WARN_EVENT_PROCESSING_SEC * (metrics.warnings + 1)) {
|
|
35
|
+
this.logger.warn(`Input stream "${id}" has ${metrics.size} queued events and the last event has been processing for ${secsProcessing}s`);
|
|
36
|
+
metrics.warnings++;
|
|
37
|
+
}
|
|
38
|
+
else if (metrics.size % WARN_EVENT_QUEUE_SIZE === 0) {
|
|
39
|
+
this.logger.warn(`Input stream "${id}" has ${metrics.size} queued events`);
|
|
40
|
+
}
|
|
41
|
+
this.outputQueueMetrics.set(id, metrics);
|
|
42
|
+
};
|
|
43
|
+
this.updateMetrics = (id) => {
|
|
44
|
+
const metrics = this.outputQueueMetrics.get(id);
|
|
45
|
+
if (metrics) {
|
|
46
|
+
metrics.size = metrics.size > 0 ? metrics.size - 1 : 0;
|
|
47
|
+
metrics.lastRemove = Date.now();
|
|
48
|
+
metrics.warnings = 0;
|
|
49
|
+
this.outputQueueMetrics.set(id, metrics);
|
|
50
|
+
}
|
|
51
|
+
};
|
|
26
52
|
this.subscribe = (streamId, observer) => this.getOutputStream(streamId).subscribe(observer);
|
|
27
53
|
this.emit = (event) => {
|
|
28
54
|
if (event) {
|
|
@@ -188,7 +214,8 @@ class FlowApplication {
|
|
|
188
214
|
if (!source || !target) {
|
|
189
215
|
continue;
|
|
190
216
|
}
|
|
191
|
-
const
|
|
217
|
+
const sourceStreamId = `${source}.${sourceStream}`;
|
|
218
|
+
const targetStreamId = `${target}.${targetStream}`;
|
|
192
219
|
const element = this.elements[target];
|
|
193
220
|
if (!element || !element.constructor) {
|
|
194
221
|
throw new Error(target + ' has not been initialized');
|
|
@@ -199,9 +226,9 @@ class FlowApplication {
|
|
|
199
226
|
}
|
|
200
227
|
const streamOptions = Reflect.getMetadata(`stream:options:${targetStream}`, element.constructor) || {};
|
|
201
228
|
const concurrent = streamOptions.concurrent || 1;
|
|
202
|
-
const outputStream = this.getOutputStream(
|
|
229
|
+
const outputStream = this.getOutputStream(sourceStreamId);
|
|
203
230
|
outputStream
|
|
204
|
-
.pipe((0, operators_1.mergeMap)(async (event) => {
|
|
231
|
+
.pipe((0, operators_1.tap)(() => this.setQueueMetrics(targetStreamId)), (0, operators_1.mergeMap)(async (event) => {
|
|
205
232
|
this.performanceMap.set(event.getId(), perf_hooks_1.performance.eventLoopUtilization());
|
|
206
233
|
try {
|
|
207
234
|
await element[streamHandler](event);
|
|
@@ -216,12 +243,13 @@ class FlowApplication {
|
|
|
216
243
|
}
|
|
217
244
|
return event;
|
|
218
245
|
}, concurrent), (0, operators_1.tap)((event) => {
|
|
246
|
+
this.updateMetrics(targetStreamId);
|
|
219
247
|
let elu = this.performanceMap.get(event.getId());
|
|
220
248
|
if (elu) {
|
|
221
249
|
this.performanceMap.delete(event.getId());
|
|
222
250
|
elu = perf_hooks_1.performance.eventLoopUtilization(elu);
|
|
223
251
|
if (elu.utilization > 0.7 && elu.active > 1000) {
|
|
224
|
-
this.logger.warn(`High event loop utilization detected for ${
|
|
252
|
+
this.logger.warn(`High event loop utilization detected for ${targetStreamId} with event ${event.getId()}! Handler has been active for ${Number(elu.active).toFixed(2)}ms with a utilization of ${Number(elu.utilization * 100).toFixed(2)}%. Consider refactoring or move tasks to a worker thread.`);
|
|
225
253
|
}
|
|
226
254
|
}
|
|
227
255
|
}))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hahnpro/flow-sdk",
|
|
3
|
-
"version": "4.15.
|
|
3
|
+
"version": "4.15.1",
|
|
4
4
|
"description": "SDK for building Flow Modules",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"amqp-connection-manager": "^3.7.0",
|
|
28
28
|
"amqplib": "^0.8.0",
|
|
29
|
-
"axios": "^0.
|
|
29
|
+
"axios": "^0.22.0",
|
|
30
30
|
"class-transformer": "0.3.1",
|
|
31
31
|
"class-validator": "~0.12.2",
|
|
32
32
|
"class-validator-jsonschema": "^2.2.0",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"p-queue": "^6.6.2",
|
|
39
39
|
"python-shell": "^3.0.0",
|
|
40
40
|
"reflect-metadata": "^0.1.13",
|
|
41
|
-
"rxjs": "^7.3.
|
|
41
|
+
"rxjs": "^7.3.1",
|
|
42
42
|
"string-interp": "^0.3.6",
|
|
43
43
|
"uuid": "^8.3.2"
|
|
44
44
|
},
|
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
"@types/amqp-connection-manager": "^2.0.12",
|
|
48
48
|
"@types/amqplib": "^0.8.2",
|
|
49
49
|
"@types/jest": "^27.0.2",
|
|
50
|
-
"@types/lodash": "^4.14.
|
|
51
|
-
"@types/node": "^14.17.
|
|
50
|
+
"@types/lodash": "^4.14.175",
|
|
51
|
+
"@types/node": "^14.17.20",
|
|
52
52
|
"axios-mock-adapter": "^1.20.0",
|
|
53
|
-
"jest": "^27.2.
|
|
53
|
+
"jest": "^27.2.4",
|
|
54
54
|
"typescript": "^4.4.3"
|
|
55
55
|
},
|
|
56
56
|
"engines": {
|