@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.
@@ -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;
@@ -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 streamId = `${source}.${sourceStream}`;
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(streamId);
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 ${target}.${targetStream} 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.`);
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.0",
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.21.4",
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.0",
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.174",
51
- "@types/node": "^14.17.19",
50
+ "@types/lodash": "^4.14.175",
51
+ "@types/node": "^14.17.20",
52
52
  "axios-mock-adapter": "^1.20.0",
53
- "jest": "^27.2.2",
53
+ "jest": "^27.2.4",
54
54
  "typescript": "^4.4.3"
55
55
  },
56
56
  "engines": {