From 1c0ebc2bc04aed67cbbd8fe04dc53bf7b7d08643 Mon Sep 17 00:00:00 2001 From: Ahmad Bamieh Date: Mon, 28 Jun 2021 15:32:06 +0300 Subject: [PATCH] track event loop utilization --- .../event_loop_utilization/constants.ts | 17 ++++ .../event_loop_utilization.ts | 79 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 src/plugins/kibana_usage_collection/server/collectors/event_loop_utilization/constants.ts create mode 100644 src/plugins/kibana_usage_collection/server/collectors/event_loop_utilization/event_loop_utilization.ts diff --git a/src/plugins/kibana_usage_collection/server/collectors/event_loop_utilization/constants.ts b/src/plugins/kibana_usage_collection/server/collectors/event_loop_utilization/constants.ts new file mode 100644 index 0000000000000..552aac3d1babe --- /dev/null +++ b/src/plugins/kibana_usage_collection/server/collectors/event_loop_utilization/constants.ts @@ -0,0 +1,17 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +/** + * Start monitoring the event loop utilization after 1 minute + */ +export const MONITOR_EVENT_LOOP_UTILIZATION_START = 1 * 60 * 1000; + +/** + * Check the event loop utilization every 5 seconds + */ +export const MONITOR_EVENT_LOOP_UTILIZATION_INTERVAL = 5 * 1000; diff --git a/src/plugins/kibana_usage_collection/server/collectors/event_loop_utilization/event_loop_utilization.ts b/src/plugins/kibana_usage_collection/server/collectors/event_loop_utilization/event_loop_utilization.ts new file mode 100644 index 0000000000000..8e96a72d9372a --- /dev/null +++ b/src/plugins/kibana_usage_collection/server/collectors/event_loop_utilization/event_loop_utilization.ts @@ -0,0 +1,79 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import type { EventLoopUtilization } from 'perf_hooks'; +import { performance } from 'perf_hooks'; +import { takeUntil } from 'rxjs/operators'; +import { Observable, timer } from 'rxjs'; +import type { Logger } from 'kibana/server'; + +import { UsageCollectionSetup } from '../../../../usage_collection/server'; + +import { + MONITOR_EVENT_LOOP_UTILIZATION_START, + MONITOR_EVENT_LOOP_UTILIZATION_INTERVAL, +} from './constants'; + +export class EventLoopUtlizationCollector { + private elu: EventLoopUtilization; + + constructor() { + this.elu = performance.eventLoopUtilization(); + } + + public collect() { + const { active, idle, utilization } = performance.eventLoopUtilization(this.elu); + + return { + active, + idle, + utilization, + }; + } + + public reset() { + this.elu = performance.eventLoopUtilization(); + } +} + +/** + * The monitoring of the event loop utilization starts immediately. + * The first collection happens after 1 minute. + * The utilization is collected and reset every 5 seconds. + * logs a warning when threshold is exceeded and increments usage counter. + */ +export function startTrackingEventLoopDelaysUsage( + usageCollection: UsageCollectionSetup, + logger: Logger, + utilizationThreshold = 0.75, + stopMonitoringEventLoop$: Observable, + collectionStartDelay = MONITOR_EVENT_LOOP_UTILIZATION_START, + collectionInterval = MONITOR_EVENT_LOOP_UTILIZATION_INTERVAL +) { + const eventLoopUtilizationCollector = new EventLoopUtlizationCollector(); + const eventLoopUtilizationCounter = usageCollection.getUsageCounterByType('eventLoopUtilization'); + + timer(collectionStartDelay, collectionInterval) + .pipe(takeUntil(stopMonitoringEventLoop$)) + .subscribe(async () => { + const { active, utilization, idle } = eventLoopUtilizationCollector.collect(); + + if (utilization > utilizationThreshold) { + logger.warn( + `Eventloop delay threshold exceeded. Utilization: ${ + utilization * 100 + }%. Idle: ${idle}ms. Active: ${active}ms.` + ); + eventLoopUtilizationCounter?.incrementCounter({ + counterName: 'utilization_threshold_exceeded', + }); + } + + eventLoopUtilizationCollector.reset(); + }); +}