Skip to content

Commit

Permalink
feat: cache recent prometheus queries in memory (#1643)
Browse files Browse the repository at this point in the history
* [DO NOT MERGE] prometheus query timer

* add cache layer

* add query result data

* ready for a look

* remove logger
  • Loading branch information
daniellacosse authored Feb 21, 2025
1 parent 15a9e54 commit d262f52
Showing 1 changed file with 46 additions and 8 deletions.
54 changes: 46 additions & 8 deletions src/shadowbox/server/manager_metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
PrometheusClient,
PrometheusMetric,
PrometheusValue,
QueryResultData,
} from '../infrastructure/prometheus_scraper';
import {DataUsageByUser, DataUsageTimeframe} from '../model/metrics';

Expand Down Expand Up @@ -111,6 +112,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,
Expand All @@ -121,34 +124,34 @@ export class PrometheusManagerMetrics implements ManagerMetrics {
dataTransferredByAccessKeyRange,
tunnelTimeByAccessKeyRange,
] = await Promise.all([
this.prometheusClient.query(
this.cachedPrometheusClient.query(
`sum(rate(shadowsocks_data_bytes_per_location{dir=~"c<p|p>t"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]))`
),
this.prometheusClient.queryRange(
this.cachedPrometheusClient.queryRange(
`sum(rate(shadowsocks_data_bytes_per_location{dir=~"c<p|p>t"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]))`,
start,
end,
`${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s`
),
this.prometheusClient.query(
this.cachedPrometheusClient.query(
`sum(increase(shadowsocks_data_bytes_per_location{dir=~"c<p|p>t"}[${timeframe.seconds}s])) by (location, asn, asorg)`
),
this.prometheusClient.query(
this.cachedPrometheusClient.query(
`sum(increase(shadowsocks_tunnel_time_seconds_per_location[${timeframe.seconds}s])) by (location, asn, asorg)`
),
this.prometheusClient.query(
this.cachedPrometheusClient.query(
`sum(increase(shadowsocks_data_bytes{dir=~"c<p|p>t"}[${timeframe.seconds}s])) by (access_key)`
),
this.prometheusClient.query(
this.cachedPrometheusClient.query(
`sum(increase(shadowsocks_tunnel_time_seconds[${timeframe.seconds}s])) by (access_key)`
),
this.prometheusClient.queryRange(
this.cachedPrometheusClient.queryRange(
`sum(increase(shadowsocks_data_bytes{dir=~"c<p|p>t"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s])) by (access_key)`,
start,
end,
`${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s`
),
this.prometheusClient.queryRange(
this.cachedPrometheusClient.queryRange(
`sum(increase(shadowsocks_tunnel_time_seconds[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s])) by (access_key)`,
start,
end,
Expand Down Expand Up @@ -231,6 +234,41 @@ export class PrometheusManagerMetrics implements ManagerMetrics {
accessKeys: Array.from(accessKeyMap.values()),
};
}

private prometheusCache = new Map<string, {timestamp: number; result: QueryResultData}>();

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(
Expand Down

0 comments on commit d262f52

Please sign in to comment.