From: Nizamudeen A Date: Fri, 8 Aug 2025 06:42:20 +0000 (+0530) Subject: mgr/dashboard: fix memory leak in prometheus service X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=26765720b972453bc69f23cd5644366bba998391;p=ceph.git mgr/dashboard: fix memory leak in prometheus service Prometheus API calls in the Cluster Utilization call is subscribed in the for loop multiple times but this is not properly unsubscribed. As we stay in the dashboard page for longer time, it produces a significant memory leak which eventually lags the UI. Attempting to fix it by properly handling the subscription Fixes: https://tracker.ceph.com/issues/72511 Signed-off-by: Nizamudeen A --- diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/prometheus.service.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/prometheus.service.ts index fedc7b8de0f6d..8e1151da13d0d 100644 --- a/src/pybind/mgr/dashboard/frontend/src/app/shared/api/prometheus.service.ts +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/api/prometheus.service.ts @@ -2,7 +2,7 @@ import { HttpClient } from '@angular/common/http'; import { Injectable } from '@angular/core'; import { Observable, Subscription, forkJoin, timer } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { map, switchMap } from 'rxjs/operators'; import { AlertmanagerSilence } from '../models/alertmanager-silence'; import { @@ -141,37 +141,47 @@ export class PrometheusService { if (this.timerGetPrometheusDataSub) { this.timerGetPrometheusDataSub.unsubscribe(); } - this.timerGetPrometheusDataSub = timer(0, this.timerTime).subscribe(() => { - selectedTime = this.updateTimeStamp(selectedTime); - for (const queryName in queries) { - if (queries.hasOwnProperty(queryName)) { - const query = queries[queryName]; - this.getPrometheusData({ - params: encodeURIComponent(query), - start: selectedTime['start'], - end: selectedTime['end'], - step: selectedTime['step'] - }).subscribe((data: any) => { - if (data.result.length) { - queriesResults[queryName] = data.result[0].values; - } else { - queriesResults[queryName] = []; - } - if ( - queriesResults[queryName] !== undefined && - queriesResults[queryName] !== '' && - checkNan - ) { - queriesResults[queryName].forEach((valueArray: any[]) => { - if (isNaN(parseFloat(valueArray[1]))) { - valueArray[1] = '0'; - } - }); + this.timerGetPrometheusDataSub = timer(0, this.timerTime) + .pipe( + switchMap(() => { + selectedTime = this.updateTimeStamp(selectedTime); + const observables = []; + for (const queryName in queries) { + if (queries.hasOwnProperty(queryName)) { + const query = queries[queryName]; + observables.push( + this.getPrometheusData({ + params: encodeURIComponent(query), + start: selectedTime['start'], + end: selectedTime['end'], + step: selectedTime['step'] + }).pipe(map((data: any) => ({ queryName, data }))) + ); } - }); - } - } - }); + } + return forkJoin(observables); + }) + ) + .subscribe((results: any) => { + results.forEach(({ queryName, data }: any) => { + if (data.result.length) { + queriesResults[queryName] = data.result[0].values; + } else { + queriesResults[queryName] = []; + } + if ( + queriesResults[queryName] !== undefined && + queriesResults[queryName] !== '' && + checkNan + ) { + queriesResults[queryName].forEach((valueArray: any[]) => { + if (isNaN(parseFloat(valueArray[1]))) { + valueArray[1] = '0'; + } + }); + } + }); + }); }); return queriesResults; }