From 7c6ad222835770ec91b16724523710d48b179033 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Fri, 21 Feb 2025 13:48:54 -0500 Subject: [PATCH 1/5] [DO NOT MERGE] prometheus query timer --- src/shadowbox/server/manager_metrics.ts | 41 ++++++++++++++++++++----- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/shadowbox/server/manager_metrics.ts b/src/shadowbox/server/manager_metrics.ts index 3969ac391..7a52ddbd0 100644 --- a/src/shadowbox/server/manager_metrics.ts +++ b/src/shadowbox/server/manager_metrics.ts @@ -98,6 +98,31 @@ export class PrometheusManagerMetrics implements ManagerMetrics { return {bytesTransferredByUserId: usage}; } + private async promethusClientTimedQuery(query: string) { + console.time(query); + + const result = await this.prometheusClient.query(query); + + console.timeEnd(query); + + return result; + } + + private async promethusClientTimedRangeQuery( + query: string, + start: number, + end: number, + step: string + ) { + console.time(`[range] ${query}`); + + const result = await this.prometheusClient.queryRange(query, start, end, step); + + console.timeEnd(`[range] ${query}`); + + return result; + } + async getServerMetrics(timeframe: Duration): Promise { const now = new Date().getTime() / 1000; // We need to calculate consistent start and end times for Prometheus range @@ -121,34 +146,34 @@ export class PrometheusManagerMetrics implements ManagerMetrics { dataTransferredByAccessKeyRange, tunnelTimeByAccessKeyRange, ] = await Promise.all([ - this.prometheusClient.query( + this.promethusClientTimedQuery( `sum(rate(shadowsocks_data_bytes_per_location{dir=~"ct"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]))` ), - this.prometheusClient.queryRange( + this.promethusClientTimedRangeQuery( `sum(rate(shadowsocks_data_bytes_per_location{dir=~"ct"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]))`, start, end, `${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s` ), - this.prometheusClient.query( + this.promethusClientTimedQuery( `sum(increase(shadowsocks_data_bytes_per_location{dir=~"ct"}[${timeframe.seconds}s])) by (location, asn, asorg)` ), - this.prometheusClient.query( + this.promethusClientTimedQuery( `sum(increase(shadowsocks_tunnel_time_seconds_per_location[${timeframe.seconds}s])) by (location, asn, asorg)` ), - this.prometheusClient.query( + this.promethusClientTimedQuery( `sum(increase(shadowsocks_data_bytes{dir=~"ct"}[${timeframe.seconds}s])) by (access_key)` ), - this.prometheusClient.query( + this.promethusClientTimedQuery( `sum(increase(shadowsocks_tunnel_time_seconds[${timeframe.seconds}s])) by (access_key)` ), - this.prometheusClient.queryRange( + this.promethusClientTimedRangeQuery( `sum(increase(shadowsocks_data_bytes{dir=~"ct"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s])) by (access_key)`, start, end, `${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s` ), - this.prometheusClient.queryRange( + this.promethusClientTimedRangeQuery( `sum(increase(shadowsocks_tunnel_time_seconds[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s])) by (access_key)`, start, end, From f0eda2777dd0cf0e3f9e7230ca8dfa93d28834be Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Fri, 21 Feb 2025 15:42:48 -0500 Subject: [PATCH 2/5] add cache layer --- src/shadowbox/server/manager_metrics.ts | 34 +++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/shadowbox/server/manager_metrics.ts b/src/shadowbox/server/manager_metrics.ts index 7a52ddbd0..c3ac5c53c 100644 --- a/src/shadowbox/server/manager_metrics.ts +++ b/src/shadowbox/server/manager_metrics.ts @@ -98,11 +98,27 @@ export class PrometheusManagerMetrics implements ManagerMetrics { return {bytesTransferredByUserId: usage}; } + private queryCache = new Map(); + private async promethusClientTimedQuery(query: string) { + const cacheId = query; + + if (this.queryCache.has(cacheId)) { + const cached = this.queryCache.get(cacheId); + + if (cached && cached.timestamp + PROMETHEUS_RANGE_QUERY_STEP_SECONDS * 1000 > Date.now()) { + console.log(cacheId, 'cache hit'); + + return cached.result; + } + } + console.time(query); const result = await this.prometheusClient.query(query); + this.queryCache.set(cacheId, {timestamp: Date.now(), result}); + console.timeEnd(query); return result; @@ -114,11 +130,25 @@ export class PrometheusManagerMetrics implements ManagerMetrics { end: number, step: string ) { - console.time(`[range] ${query}`); + const cacheId = `${query}-${start}-${end}-${step}`; + + if (this.queryCache.has(cacheId)) { + const cached = this.queryCache.get(cacheId); + + if (cached && cached.timestamp + PROMETHEUS_RANGE_QUERY_STEP_SECONDS * 1000 > Date.now()) { + console.log(cacheId, 'cache hit'); + + return cached.result; + } + } + + console.time(cacheId); const result = await this.prometheusClient.queryRange(query, start, end, step); - console.timeEnd(`[range] ${query}`); + this.queryCache.set(cacheId, {timestamp: Date.now(), result}); + + console.timeEnd(cacheId); return result; } From e529ee5c61b4923e5fe80ec5fbd1f34625359111 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Fri, 21 Feb 2025 16:01:54 -0500 Subject: [PATCH 3/5] add query result data --- src/shadowbox/server/manager_metrics.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/shadowbox/server/manager_metrics.ts b/src/shadowbox/server/manager_metrics.ts index c3ac5c53c..307aeb67d 100644 --- a/src/shadowbox/server/manager_metrics.ts +++ b/src/shadowbox/server/manager_metrics.ts @@ -16,6 +16,7 @@ import { PrometheusClient, PrometheusMetric, PrometheusValue, + QueryResultData, } from '../infrastructure/prometheus_scraper'; import {DataUsageByUser, DataUsageTimeframe} from '../model/metrics'; @@ -98,7 +99,7 @@ export class PrometheusManagerMetrics implements ManagerMetrics { return {bytesTransferredByUserId: usage}; } - private queryCache = new Map(); + private queryCache = new Map(); private async promethusClientTimedQuery(query: string) { const cacheId = query; From 56973f1cedab9428e0eb3ea02240bd7e022079f1 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Fri, 21 Feb 2025 16:40:37 -0500 Subject: [PATCH 4/5] ready for a look --- src/shadowbox/server/manager_metrics.ts | 109 ++++++++++-------------- 1 file changed, 46 insertions(+), 63 deletions(-) diff --git a/src/shadowbox/server/manager_metrics.ts b/src/shadowbox/server/manager_metrics.ts index 307aeb67d..9c3c22523 100644 --- a/src/shadowbox/server/manager_metrics.ts +++ b/src/shadowbox/server/manager_metrics.ts @@ -19,6 +19,7 @@ import { QueryResultData, } from '../infrastructure/prometheus_scraper'; import {DataUsageByUser, DataUsageTimeframe} from '../model/metrics'; +import * as logging from '../infrastructure/logging'; const PROMETHEUS_RANGE_QUERY_STEP_SECONDS = 5 * 60; @@ -99,61 +100,6 @@ export class PrometheusManagerMetrics implements ManagerMetrics { return {bytesTransferredByUserId: usage}; } - private queryCache = new Map(); - - private async promethusClientTimedQuery(query: string) { - const cacheId = query; - - if (this.queryCache.has(cacheId)) { - const cached = this.queryCache.get(cacheId); - - if (cached && cached.timestamp + PROMETHEUS_RANGE_QUERY_STEP_SECONDS * 1000 > Date.now()) { - console.log(cacheId, 'cache hit'); - - return cached.result; - } - } - - console.time(query); - - const result = await this.prometheusClient.query(query); - - this.queryCache.set(cacheId, {timestamp: Date.now(), result}); - - console.timeEnd(query); - - return result; - } - - private async promethusClientTimedRangeQuery( - query: string, - start: number, - end: number, - step: string - ) { - const cacheId = `${query}-${start}-${end}-${step}`; - - if (this.queryCache.has(cacheId)) { - const cached = this.queryCache.get(cacheId); - - if (cached && cached.timestamp + PROMETHEUS_RANGE_QUERY_STEP_SECONDS * 1000 > Date.now()) { - console.log(cacheId, 'cache hit'); - - return cached.result; - } - } - - console.time(cacheId); - - const result = await this.prometheusClient.queryRange(query, start, end, step); - - this.queryCache.set(cacheId, {timestamp: Date.now(), result}); - - console.timeEnd(cacheId); - - return result; - } - async getServerMetrics(timeframe: Duration): Promise { const now = new Date().getTime() / 1000; // We need to calculate consistent start and end times for Prometheus range @@ -167,6 +113,8 @@ export class PrometheusManagerMetrics implements ManagerMetrics { Math.ceil(now / PROMETHEUS_RANGE_QUERY_STEP_SECONDS) * PROMETHEUS_RANGE_QUERY_STEP_SECONDS; const start = end - timeframe.seconds; + this.prunePrometheusCache(); + const [ bandwidth, bandwidthRange, @@ -177,34 +125,34 @@ export class PrometheusManagerMetrics implements ManagerMetrics { dataTransferredByAccessKeyRange, tunnelTimeByAccessKeyRange, ] = await Promise.all([ - this.promethusClientTimedQuery( + this.cachedPrometheusClient.query( `sum(rate(shadowsocks_data_bytes_per_location{dir=~"ct"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]))` ), - this.promethusClientTimedRangeQuery( + this.cachedPrometheusClient.queryRange( `sum(rate(shadowsocks_data_bytes_per_location{dir=~"ct"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]))`, start, end, `${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s` ), - this.promethusClientTimedQuery( + this.cachedPrometheusClient.query( `sum(increase(shadowsocks_data_bytes_per_location{dir=~"ct"}[${timeframe.seconds}s])) by (location, asn, asorg)` ), - this.promethusClientTimedQuery( + this.cachedPrometheusClient.query( `sum(increase(shadowsocks_tunnel_time_seconds_per_location[${timeframe.seconds}s])) by (location, asn, asorg)` ), - this.promethusClientTimedQuery( + this.cachedPrometheusClient.query( `sum(increase(shadowsocks_data_bytes{dir=~"ct"}[${timeframe.seconds}s])) by (access_key)` ), - this.promethusClientTimedQuery( + this.cachedPrometheusClient.query( `sum(increase(shadowsocks_tunnel_time_seconds[${timeframe.seconds}s])) by (access_key)` ), - this.promethusClientTimedRangeQuery( + this.cachedPrometheusClient.queryRange( `sum(increase(shadowsocks_data_bytes{dir=~"ct"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s])) by (access_key)`, start, end, `${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s` ), - this.promethusClientTimedRangeQuery( + this.cachedPrometheusClient.queryRange( `sum(increase(shadowsocks_tunnel_time_seconds[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s])) by (access_key)`, start, end, @@ -287,6 +235,41 @@ export class PrometheusManagerMetrics implements ManagerMetrics { accessKeys: Array.from(accessKeyMap.values()), }; } + + private prometheusCache = new Map(); + + private get cachedPrometheusClient() { + return new Proxy(this.prometheusClient, { + get: (target, prop) => { + if (typeof target[prop] !== 'function') { + return target[prop]; + } + + return async (query, ...args) => { + const cacheId = `${String(prop)}: ${query} (args: ${args.join(', ')}))`; + + if (this.prometheusCache.has(cacheId)) { + return this.prometheusCache.get(cacheId).result; + } + + const result = await (target[prop] as Function)(query, ...args); + + this.prometheusCache.set(cacheId, {timestamp: Date.now(), result}); + + return result; + }; + }, + }); + } + + private prunePrometheusCache() { + const now = Date.now(); + for (const [key, value] of this.prometheusCache) { + if (now - value.timestamp > PROMETHEUS_RANGE_QUERY_STEP_SECONDS * 1000) { + this.prometheusCache.delete(key); + } + } + } } function getServerMetricsLocationEntry( From 253d0982fccb1b690ab3b89ad4a33686337f411d Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Fri, 21 Feb 2025 16:43:43 -0500 Subject: [PATCH 5/5] remove logger --- src/shadowbox/server/manager_metrics.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/shadowbox/server/manager_metrics.ts b/src/shadowbox/server/manager_metrics.ts index 9c3c22523..7cbc90ffb 100644 --- a/src/shadowbox/server/manager_metrics.ts +++ b/src/shadowbox/server/manager_metrics.ts @@ -19,7 +19,6 @@ import { QueryResultData, } from '../infrastructure/prometheus_scraper'; import {DataUsageByUser, DataUsageTimeframe} from '../model/metrics'; -import * as logging from '../infrastructure/logging'; const PROMETHEUS_RANGE_QUERY_STEP_SECONDS = 5 * 60;