From 43bed7cf5640a11a631f6a0009c54e3e35a2f261 Mon Sep 17 00:00:00 2001 From: Mononaut Date: Sat, 4 Mar 2023 23:13:55 -0600 Subject: [PATCH 1/2] Monitor heap memory usage --- backend/src/index.ts | 29 +++++++++++++++++++++++++++++ backend/src/utils/format.ts | 29 +++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 backend/src/utils/format.ts diff --git a/backend/src/index.ts b/backend/src/index.ts index 3ace7a5f2..f2b845ea1 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -38,6 +38,8 @@ import forensicsService from './tasks/lightning/forensics.service'; import priceUpdater from './tasks/price-updater'; import chainTips from './api/chain-tips'; import { AxiosError } from 'axios'; +import v8 from 'v8'; +import { formatBytes, getBytesUnit } from './utils/format'; class Server { private wss: WebSocket.Server | undefined; @@ -45,6 +47,11 @@ class Server { private app: Application; private currentBackendRetryInterval = 5; + private maxHeapSize: number = 0; + private heapLogInterval: number = 60; + private warnedHeapCritical: boolean = false; + private lastHeapLogTime: number | null = null; + constructor() { this.app = express(); @@ -137,6 +144,8 @@ class Server { this.runMainUpdateLoop(); } + setInterval(() => { this.healthCheck(); }, 2500); + if (config.BISQ.ENABLED) { bisq.startBisqService(); bisq.setPriceCallbackFunction((price) => websocketHandler.setExtraInitProperties('bsq-price', price)); @@ -255,6 +264,26 @@ class Server { channelsRoutes.initRoutes(this.app); } } + + healthCheck(): void { + const now = Date.now(); + const stats = v8.getHeapStatistics(); + this.maxHeapSize = Math.max(stats.used_heap_size, this.maxHeapSize); + const warnThreshold = 0.95 * stats.heap_size_limit; + + const byteUnits = getBytesUnit(Math.max(this.maxHeapSize, stats.heap_size_limit)); + + if (!this.warnedHeapCritical && this.maxHeapSize > warnThreshold) { + this.warnedHeapCritical = true; + logger.warn(`Used ${(this.maxHeapSize / stats.heap_size_limit).toFixed(2)}% of heap limit (${formatBytes(this.maxHeapSize, byteUnits, true)} / ${formatBytes(stats.heap_size_limit, byteUnits)})!`); + } + if (this.lastHeapLogTime === null || (now - this.lastHeapLogTime) > (this.heapLogInterval * 1000)) { + logger.debug(`Memory usage: ${formatBytes(this.maxHeapSize, byteUnits)} / ${formatBytes(stats.heap_size_limit, byteUnits)}`); + this.warnedHeapCritical = false; + this.maxHeapSize = 0; + this.lastHeapLogTime = now; + } + } } ((): Server => new Server())(); diff --git a/backend/src/utils/format.ts b/backend/src/utils/format.ts new file mode 100644 index 000000000..a18ce1892 --- /dev/null +++ b/backend/src/utils/format.ts @@ -0,0 +1,29 @@ +const byteUnits = ['B', 'kB', 'MB', 'GB', 'TB']; + +export function getBytesUnit(bytes: number): string { + if (isNaN(bytes) || !isFinite(bytes)) { + return 'B'; + } + + let unitIndex = 0; + while (unitIndex < byteUnits.length && bytes > 1024) { + unitIndex++; + bytes /= 1024; + } + + return byteUnits[unitIndex]; +} + +export function formatBytes(bytes: number, toUnit: string, skipUnit = false): string { + if (isNaN(bytes) || !isFinite(bytes)) { + return `${bytes}`; + } + + let unitIndex = 0; + while (unitIndex < byteUnits.length && (toUnit && byteUnits[unitIndex] !== toUnit || (!toUnit && bytes > 1024))) { + unitIndex++; + bytes /= 1024; + } + + return `${bytes.toFixed(2)}${skipUnit ? '' : ' ' + byteUnits[unitIndex]}`; +} \ No newline at end of file From f37946118cf8799f3881129a09934c5272b1e5e6 Mon Sep 17 00:00:00 2001 From: wiz Date: Sun, 5 Mar 2023 15:45:28 +0900 Subject: [PATCH 2/2] Change heap size warning to 80% utilization --- backend/src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/src/index.ts b/backend/src/index.ts index f2b845ea1..fbe9c08c2 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -269,7 +269,7 @@ class Server { const now = Date.now(); const stats = v8.getHeapStatistics(); this.maxHeapSize = Math.max(stats.used_heap_size, this.maxHeapSize); - const warnThreshold = 0.95 * stats.heap_size_limit; + const warnThreshold = 0.8 * stats.heap_size_limit; const byteUnits = getBytesUnit(Math.max(this.maxHeapSize, stats.heap_size_limit));