From a6562b708da93af9ae27c67fd46971f665ec64b7 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Tue, 25 Feb 2025 13:58:02 -0500 Subject: [PATCH 01/13] refactor(server): combine prometheus queries --- src/shadowbox/server/manager_metrics.ts | 135 ++++++++++++------------ 1 file changed, 70 insertions(+), 65 deletions(-) diff --git a/src/shadowbox/server/manager_metrics.ts b/src/shadowbox/server/manager_metrics.ts index 7cbc90ffb..7cef9505e 100644 --- a/src/shadowbox/server/manager_metrics.ts +++ b/src/shadowbox/server/manager_metrics.ts @@ -115,48 +115,32 @@ export class PrometheusManagerMetrics implements ManagerMetrics { this.prunePrometheusCache(); const [ - bandwidth, - bandwidthRange, - dataTransferredByLocation, - tunnelTimeByLocation, - dataTransferredByAccessKey, - tunnelTimeByAccessKey, dataTransferredByAccessKeyRange, + dataTransferredByLocationRange, tunnelTimeByAccessKeyRange, + tunnelTimeByLocation, ] = await Promise.all([ - this.cachedPrometheusClient.query( - `sum(rate(shadowsocks_data_bytes_per_location{dir=~"ct"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]))` - ), this.cachedPrometheusClient.queryRange( - `sum(rate(shadowsocks_data_bytes_per_location{dir=~"ct"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]))`, + `increase(shadowsocks_data_bytes{dir=~"ct"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]) by (access_key)`, start, end, `${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s` ), - this.cachedPrometheusClient.query( - `sum(increase(shadowsocks_data_bytes_per_location{dir=~"ct"}[${timeframe.seconds}s])) by (location, asn, asorg)` - ), - this.cachedPrometheusClient.query( - `sum(increase(shadowsocks_tunnel_time_seconds_per_location[${timeframe.seconds}s])) by (location, asn, asorg)` - ), - this.cachedPrometheusClient.query( - `sum(increase(shadowsocks_data_bytes{dir=~"ct"}[${timeframe.seconds}s])) by (access_key)` - ), - this.cachedPrometheusClient.query( - `sum(increase(shadowsocks_tunnel_time_seconds[${timeframe.seconds}s])) by (access_key)` - ), this.cachedPrometheusClient.queryRange( - `sum(increase(shadowsocks_data_bytes{dir=~"ct"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s])) by (access_key)`, + `increase(shadowsocks_data_bytes_per_location{dir=~"ct"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]) by (location, asn, asorg)`, start, end, `${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s` ), this.cachedPrometheusClient.queryRange( - `sum(increase(shadowsocks_tunnel_time_seconds[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s])) by (access_key)`, + `increase(shadowsocks_tunnel_time_seconds[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]) by (access_key)`, start, end, `${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s` ), + this.cachedPrometheusClient.query( + `sum(increase(shadowsocks_tunnel_time_seconds_per_location[${timeframe.seconds}s])) by (location, asn, asorg)` + ), ]); const serverMetrics: ServerMetricsServerEntry = { @@ -168,51 +152,45 @@ export class PrometheusManagerMetrics implements ManagerMetrics { }, locations: [], }; - for (const result of bandwidth.result) { - if (result.value) { - serverMetrics.bandwidth.current.data.bytes = parseFloat(result.value[1]); - serverMetrics.bandwidth.current.timestamp = result.value[0]; - } - break; // There should only be one result. - } - for (const result of bandwidthRange.result) { - const peakDataTransferred = findPeak(result.values ?? []); - if (peakDataTransferred !== null) { - const peakValue = parseFloat(peakDataTransferred[1]); - if (peakValue > 0) { - serverMetrics.bandwidth.peak.data.bytes = peakValue; - serverMetrics.bandwidth.peak.timestamp = Math.min(now, peakDataTransferred[0]); - } - } - break; // There should only be one result. - } - const locationMap = new Map(); - for (const result of tunnelTimeByLocation.result) { - const entry = getServerMetricsLocationEntry(locationMap, result.metric); - const tunnelTime = result.value ? parseFloat(result.value[1]) : 0; - entry.tunnelTime.seconds = tunnelTime; - serverMetrics.tunnelTime.seconds += tunnelTime; - } - for (const result of dataTransferredByLocation.result) { - const entry = getServerMetricsLocationEntry(locationMap, result.metric); - const bytes = result.value ? parseFloat(result.value[1]) : 0; - entry.dataTransferred.bytes = bytes; - serverMetrics.dataTransferred.bytes += bytes; - } - serverMetrics.locations = Array.from(locationMap.values()); + const bandwidthRangeValues = []; const accessKeyMap = new Map(); - for (const result of tunnelTimeByAccessKey.result) { + for (const result of dataTransferredByAccessKeyRange.result) { const entry = getServerMetricsAccessKeyEntry(accessKeyMap, result.metric); - entry.tunnelTime.seconds = result.value ? parseFloat(result.value[1]) : 0; + const lastTrafficSeen = findLastNonZero(result.values ?? []); + + entry.connection.lastTrafficSeen = lastTrafficSeen ? Math.min(now, lastTrafficSeen[0]) : null; + entry.dataTransferred.bytes = findSum(result.values ?? []); + + // does this... actually work? can we assume the every row has the same amound of data and similar enough timestamps? + for (const entryIndex in result.values) { + const [currentTimestamp, nextValue] = result.values[entryIndex]; + const [previousTimestamp, currentValue] = bandwidthRangeValues[entryIndex] ?? [0, 0]; + + bandwidthRangeValues[entryIndex] = [ + Math.min(previousTimestamp, currentTimestamp), + currentValue + (nextValue ? parseFloat(nextValue) : 0), + ]; + } } - for (const result of dataTransferredByAccessKey.result) { - const entry = getServerMetricsAccessKeyEntry(accessKeyMap, result.metric); - entry.dataTransferred.bytes = result.value ? parseFloat(result.value[1]) : 0; + + [serverMetrics.bandwidth.current.timestamp, serverMetrics.bandwidth.current.data.bytes] = + bandwidthRangeValues[bandwidthRangeValues.length - 1]; + + const peakDataTransferred = findPeak(bandwidthRangeValues); + if (peakDataTransferred !== null) { + const peakValue = parseFloat(peakDataTransferred[1]); + + if (peakValue > 0) { + serverMetrics.bandwidth.peak.data.bytes = peakValue; + serverMetrics.bandwidth.peak.timestamp = Math.min(now, peakDataTransferred[0]); + } } + for (const result of tunnelTimeByAccessKeyRange.result) { const entry = getServerMetricsAccessKeyEntry(accessKeyMap, result.metric); + const peakTunnelTimeSec = findPeak(result.values ?? []); if (peakTunnelTimeSec !== null) { const peakValue = parseFloat(peakTunnelTimeSec[1]); @@ -222,13 +200,29 @@ export class PrometheusManagerMetrics implements ManagerMetrics { entry.connection.peakDeviceCount.timestamp = Math.min(now, peakTunnelTimeSec[0]); } } + + entry.tunnelTime.seconds = findSum(result.values ?? []); } - for (const result of dataTransferredByAccessKeyRange.result) { - const entry = getServerMetricsAccessKeyEntry(accessKeyMap, result.metric); - const lastTrafficSeen = findLastNonZero(result.values ?? []); - entry.connection.lastTrafficSeen = lastTrafficSeen ? Math.min(now, lastTrafficSeen[0]) : null; + + const locationMap = new Map(); + for (const result of dataTransferredByLocationRange.result) { + const entry = getServerMetricsLocationEntry(locationMap, result.metric); + const bytes = findSum(result.values ?? []); + + entry.dataTransferred.bytes += bytes; + serverMetrics.dataTransferred.bytes += bytes; + } + + for (const result of tunnelTimeByLocation.result) { + const entry = getServerMetricsLocationEntry(locationMap, result.metric); + const tunnelTime = result.value ? parseFloat(result.value[1]) : 0; + + entry.tunnelTime.seconds = tunnelTime; + serverMetrics.tunnelTime.seconds += tunnelTime; } + serverMetrics.locations = Array.from(locationMap.values()); + return { server: serverMetrics, accessKeys: Array.from(accessKeyMap.values()), @@ -350,3 +344,14 @@ function findLastNonZero(values: PrometheusValue[]): PrometheusValue | null { } return null; } + +/** + * + */ +function findSum(values: PrometheusValue[]): number { + let sum = 0; + for (const value of values) { + sum += parseFloat(value[1]); + } + return sum; +} From e3612521e47a6753599b3408793b5b5cf94e521a Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Thu, 27 Feb 2025 11:46:39 -0500 Subject: [PATCH 02/13] ok now we must try with real data --- src/shadowbox/server/manager_metrics.ts | 25 +++++++++++++------------ src/shadowbox/server/manager_service.ts | 2 +- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/shadowbox/server/manager_metrics.ts b/src/shadowbox/server/manager_metrics.ts index 7cef9505e..4e43a1e71 100644 --- a/src/shadowbox/server/manager_metrics.ts +++ b/src/shadowbox/server/manager_metrics.ts @@ -120,25 +120,25 @@ export class PrometheusManagerMetrics implements ManagerMetrics { tunnelTimeByAccessKeyRange, tunnelTimeByLocation, ] = await Promise.all([ - this.cachedPrometheusClient.queryRange( - `increase(shadowsocks_data_bytes{dir=~"ct"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]) by (access_key)`, + this.prometheusClient.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.cachedPrometheusClient.queryRange( - `increase(shadowsocks_data_bytes_per_location{dir=~"ct"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]) by (location, asn, asorg)`, + this.prometheusClient.queryRange( + `sum(increase(shadowsocks_data_bytes_per_location{dir=~"ct"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s])) by (location, asn, asorg)`, start, end, `${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s` ), - this.cachedPrometheusClient.queryRange( - `increase(shadowsocks_tunnel_time_seconds[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]) by (access_key)`, + this.prometheusClient.queryRange( + `sum(increase(shadowsocks_tunnel_time_seconds[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s])) by (access_key)`, start, end, `${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s` ), - this.cachedPrometheusClient.query( + this.prometheusClient.query( `sum(increase(shadowsocks_tunnel_time_seconds_per_location[${timeframe.seconds}s])) by (location, asn, asorg)` ), ]); @@ -153,7 +153,7 @@ export class PrometheusManagerMetrics implements ManagerMetrics { locations: [], }; - const bandwidthRangeValues = []; + const bandwidthRangeValues: [number, string][] = []; const accessKeyMap = new Map(); for (const result of dataTransferredByAccessKeyRange.result) { @@ -163,20 +163,21 @@ export class PrometheusManagerMetrics implements ManagerMetrics { entry.connection.lastTrafficSeen = lastTrafficSeen ? Math.min(now, lastTrafficSeen[0]) : null; entry.dataTransferred.bytes = findSum(result.values ?? []); - // does this... actually work? can we assume the every row has the same amound of data and similar enough timestamps? for (const entryIndex in result.values) { const [currentTimestamp, nextValue] = result.values[entryIndex]; const [previousTimestamp, currentValue] = bandwidthRangeValues[entryIndex] ?? [0, 0]; bandwidthRangeValues[entryIndex] = [ Math.min(previousTimestamp, currentTimestamp), - currentValue + (nextValue ? parseFloat(nextValue) : 0), + String(parseFloat(currentValue as string) + (nextValue ? parseFloat(nextValue) : 0)), ]; } } - [serverMetrics.bandwidth.current.timestamp, serverMetrics.bandwidth.current.data.bytes] = - bandwidthRangeValues[bandwidthRangeValues.length - 1]; + const currentBandwidth = bandwidthRangeValues[bandwidthRangeValues.length - 1] ?? []; + + serverMetrics.bandwidth.current.timestamp = currentBandwidth[0]; + serverMetrics.bandwidth.current.data.bytes = parseFloat(currentBandwidth[1]); const peakDataTransferred = findPeak(bandwidthRangeValues); if (peakDataTransferred !== null) { diff --git a/src/shadowbox/server/manager_service.ts b/src/shadowbox/server/manager_service.ts index 91bd25f4b..a246ef791 100644 --- a/src/shadowbox/server/manager_service.ts +++ b/src/shadowbox/server/manager_service.ts @@ -628,7 +628,7 @@ export class ShadowsocksManagerService { } async getServerMetrics(req: RequestType, res: ResponseType, next: restify.Next) { - logging.debug(`getServerMetrics request ${JSON.stringify(req.params)}`); + logging.debug(`getServerMetrics request ${JSON.stringify(req.query)}`); let seconds; try { From 488bc72951718ccbfe3f907f0c73b956566b2148 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Thu, 27 Feb 2025 14:58:25 -0500 Subject: [PATCH 03/13] ok! they match, mostly --- src/shadowbox/server/main.ts | 2 +- src/shadowbox/server/manager_metrics.ts | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/shadowbox/server/main.ts b/src/shadowbox/server/main.ts index 8f52c18d0..76963c4c1 100644 --- a/src/shadowbox/server/main.ts +++ b/src/shadowbox/server/main.ts @@ -174,7 +174,7 @@ async function main() { // Start Prometheus subprocess and wait for it to be up and running. const prometheusConfigFilename = getPersistentFilename('prometheus/config.yml'); const prometheusTsdbFilename = getPersistentFilename('prometheus/data'); - const prometheusEndpoint = `http://${prometheusLocation}`; + const prometheusEndpoint = `http://localhost:9095`; const prometheusBinary = getBinaryFilename('prometheus'); const prometheusArgs = [ '--config.file', diff --git a/src/shadowbox/server/manager_metrics.ts b/src/shadowbox/server/manager_metrics.ts index 4e43a1e71..8d4442206 100644 --- a/src/shadowbox/server/manager_metrics.ts +++ b/src/shadowbox/server/manager_metrics.ts @@ -165,7 +165,7 @@ export class PrometheusManagerMetrics implements ManagerMetrics { for (const entryIndex in result.values) { const [currentTimestamp, nextValue] = result.values[entryIndex]; - const [previousTimestamp, currentValue] = bandwidthRangeValues[entryIndex] ?? [0, 0]; + const [previousTimestamp, currentValue] = bandwidthRangeValues[entryIndex] ?? [Infinity, 0]; bandwidthRangeValues[entryIndex] = [ Math.min(previousTimestamp, currentTimestamp), @@ -176,15 +176,18 @@ export class PrometheusManagerMetrics implements ManagerMetrics { const currentBandwidth = bandwidthRangeValues[bandwidthRangeValues.length - 1] ?? []; + // convert increase() into rate() + serverMetrics.bandwidth.current.data.bytes = + parseFloat(currentBandwidth[1]) / PROMETHEUS_RANGE_QUERY_STEP_SECONDS; serverMetrics.bandwidth.current.timestamp = currentBandwidth[0]; - serverMetrics.bandwidth.current.data.bytes = parseFloat(currentBandwidth[1]); const peakDataTransferred = findPeak(bandwidthRangeValues); if (peakDataTransferred !== null) { const peakValue = parseFloat(peakDataTransferred[1]); if (peakValue > 0) { - serverMetrics.bandwidth.peak.data.bytes = peakValue; + // convert increase() into rate() + serverMetrics.bandwidth.peak.data.bytes = peakValue / PROMETHEUS_RANGE_QUERY_STEP_SECONDS; serverMetrics.bandwidth.peak.timestamp = Math.min(now, peakDataTransferred[0]); } } From b61aca169979f5342da4a7d3fb554c0d2ca8102a Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Thu, 27 Feb 2025 14:59:58 -0500 Subject: [PATCH 04/13] oops, revert --- src/shadowbox/server/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shadowbox/server/main.ts b/src/shadowbox/server/main.ts index 76963c4c1..8f52c18d0 100644 --- a/src/shadowbox/server/main.ts +++ b/src/shadowbox/server/main.ts @@ -174,7 +174,7 @@ async function main() { // Start Prometheus subprocess and wait for it to be up and running. const prometheusConfigFilename = getPersistentFilename('prometheus/config.yml'); const prometheusTsdbFilename = getPersistentFilename('prometheus/data'); - const prometheusEndpoint = `http://localhost:9095`; + const prometheusEndpoint = `http://${prometheusLocation}`; const prometheusBinary = getBinaryFilename('prometheus'); const prometheusArgs = [ '--config.file', From a98bcf4f192a1f1a555f7ae0119551cf2e07d6a2 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Thu, 27 Feb 2025 15:01:23 -0500 Subject: [PATCH 05/13] add description --- src/shadowbox/server/manager_metrics.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shadowbox/server/manager_metrics.ts b/src/shadowbox/server/manager_metrics.ts index 8d4442206..cc6db3f75 100644 --- a/src/shadowbox/server/manager_metrics.ts +++ b/src/shadowbox/server/manager_metrics.ts @@ -350,7 +350,7 @@ function findLastNonZero(values: PrometheusValue[]): PrometheusValue | null { } /** - * + * Finds the sum of the values in an array of PrometheusValues. */ function findSum(values: PrometheusValue[]): number { let sum = 0; From 938590fcb5acf80d911eb02db80d78d205d80a64 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Thu, 27 Feb 2025 15:02:01 -0500 Subject: [PATCH 06/13] switch back to cached --- src/shadowbox/server/manager_metrics.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/shadowbox/server/manager_metrics.ts b/src/shadowbox/server/manager_metrics.ts index cc6db3f75..eadc882e1 100644 --- a/src/shadowbox/server/manager_metrics.ts +++ b/src/shadowbox/server/manager_metrics.ts @@ -120,25 +120,25 @@ export class PrometheusManagerMetrics implements ManagerMetrics { tunnelTimeByAccessKeyRange, tunnelTimeByLocation, ] = await Promise.all([ - this.prometheusClient.queryRange( + 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.prometheusClient.queryRange( + this.cachedPrometheusClient.queryRange( `sum(increase(shadowsocks_data_bytes_per_location{dir=~"ct"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s])) by (location, asn, asorg)`, 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, `${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s` ), - this.prometheusClient.query( + this.cachedPrometheusClient.query( `sum(increase(shadowsocks_tunnel_time_seconds_per_location[${timeframe.seconds}s])) by (location, asn, asorg)` ), ]); From 27cab96cdcb47ebdf8f3f38e6d491494b4363406 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Thu, 27 Feb 2025 15:03:05 -0500 Subject: [PATCH 07/13] add zero vals --- src/shadowbox/server/manager_metrics.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shadowbox/server/manager_metrics.ts b/src/shadowbox/server/manager_metrics.ts index eadc882e1..1fceebb50 100644 --- a/src/shadowbox/server/manager_metrics.ts +++ b/src/shadowbox/server/manager_metrics.ts @@ -174,7 +174,7 @@ export class PrometheusManagerMetrics implements ManagerMetrics { } } - const currentBandwidth = bandwidthRangeValues[bandwidthRangeValues.length - 1] ?? []; + const currentBandwidth = bandwidthRangeValues[bandwidthRangeValues.length - 1] ?? [0, '0']; // convert increase() into rate() serverMetrics.bandwidth.current.data.bytes = From 3e53c7f1c14ff22e3ed0f7de447dba720e2ef7c3 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Thu, 27 Feb 2025 15:05:16 -0500 Subject: [PATCH 08/13] formatting, type fix --- src/shadowbox/server/manager_metrics.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/shadowbox/server/manager_metrics.ts b/src/shadowbox/server/manager_metrics.ts index 1fceebb50..ba29d9244 100644 --- a/src/shadowbox/server/manager_metrics.ts +++ b/src/shadowbox/server/manager_metrics.ts @@ -165,7 +165,10 @@ export class PrometheusManagerMetrics implements ManagerMetrics { for (const entryIndex in result.values) { const [currentTimestamp, nextValue] = result.values[entryIndex]; - const [previousTimestamp, currentValue] = bandwidthRangeValues[entryIndex] ?? [Infinity, 0]; + const [previousTimestamp, currentValue] = bandwidthRangeValues[entryIndex] ?? [ + Infinity, + '0', + ]; bandwidthRangeValues[entryIndex] = [ Math.min(previousTimestamp, currentTimestamp), From f885e500b8652f37903b18083c21a7136f831942 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Thu, 27 Feb 2025 16:30:46 -0500 Subject: [PATCH 09/13] tests "work" - what did the 2nd test test? --- src/shadowbox/Taskfile.yml | 1 + src/shadowbox/server/manager_metrics.spec.ts | 199 ++++--------------- 2 files changed, 40 insertions(+), 160 deletions(-) diff --git a/src/shadowbox/Taskfile.yml b/src/shadowbox/Taskfile.yml index fb72a9b87..432dc58e3 100644 --- a/src/shadowbox/Taskfile.yml +++ b/src/shadowbox/Taskfile.yml @@ -60,6 +60,7 @@ tasks: SB_PUBLIC_IP: localhost SB_CERTIFICATE_FILE: '{{joinPath .RUN_DIR "/shadowbox-selfsigned-dev.crt"}}' SB_PRIVATE_KEY_FILE: '{{joinPath .RUN_DIR "/shadowbox-selfsigned-dev.key"}}' + LOG_LEVEL: '{{.LOG_LEVEL}}' cmds: - echo Target platform is {{.TARGET_OS}}/{{.TARGET_ARCH}} - echo "Using directory {{.RUN_DIR}}" diff --git a/src/shadowbox/server/manager_metrics.spec.ts b/src/shadowbox/server/manager_metrics.spec.ts index ed3107ff7..ba102da61 100644 --- a/src/shadowbox/server/manager_metrics.spec.ts +++ b/src/shadowbox/server/manager_metrics.spec.ts @@ -41,20 +41,7 @@ describe('PrometheusManagerMetrics', () => { const managerMetrics = new PrometheusManagerMetrics( new QueryMapPrometheusClient( { - 'sum(rate(shadowsocks_data_bytes_per_location{dir=~"ct"}[300s]))': { - resultType: 'vector', - result: [ - { - metric: { - location: 'US', - asn: '49490', - asorg: 'Test AS Org', - }, - value: [1739284734, '1234'], - }, - ], - }, - 'sum(increase(shadowsocks_data_bytes_per_location{dir=~"ct"}[0s])) by (location, asn, asorg)': + 'sum(increase(shadowsocks_tunnel_time_seconds_per_location[0s])) by (location, asn, asorg)': { resultType: 'vector', result: [ @@ -68,9 +55,11 @@ describe('PrometheusManagerMetrics', () => { }, ], }, - 'sum(increase(shadowsocks_tunnel_time_seconds_per_location[0s])) by (location, asn, asorg)': + }, + { + 'sum(increase(shadowsocks_data_bytes_per_location{dir=~"ct"}[300s])) by (location, asn, asorg)': { - resultType: 'vector', + resultType: 'matrix', result: [ { metric: { @@ -78,50 +67,13 @@ describe('PrometheusManagerMetrics', () => { asn: '49490', asorg: 'Test AS Org', }, - value: [1738959398, '1000'], + values: [ + [1738959398, '9000'], + [1739284734, '3000'], + ], }, ], }, - 'sum(increase(shadowsocks_data_bytes{dir=~"ct"}[0s])) by (access_key)': { - resultType: 'vector', - result: [ - { - metric: { - access_key: '0', - }, - value: [1738959398, '1000'], - }, - ], - }, - 'sum(increase(shadowsocks_tunnel_time_seconds[0s])) by (access_key)': { - resultType: 'vector', - result: [ - { - metric: { - access_key: '0', - }, - value: [1738959398, '1000'], - }, - ], - }, - }, - { - 'sum(rate(shadowsocks_data_bytes_per_location{dir=~"ct"}[300s]))': { - resultType: 'matrix', - result: [ - { - metric: { - location: 'US', - asn: '49490', - asorg: 'Test AS Org', - }, - values: [ - [1738959398, '5678'], - [1739284734, '1234'], - ], - }, - ], - }, 'sum(increase(shadowsocks_data_bytes{dir=~"ct"}[300s])) by (access_key)': { resultType: 'matrix', result: [ @@ -130,8 +82,8 @@ describe('PrometheusManagerMetrics', () => { access_key: '0', }, values: [ - [1738959398, '1000'], - [1739284734, '2000'], + [1738959398, '9000'], + [1739284734, '3000'], ], }, ], @@ -145,7 +97,7 @@ describe('PrometheusManagerMetrics', () => { }, values: [ [1738959398, '1000'], - [1739284734, '0'], + [1738959398, '0'], ], }, ], @@ -162,18 +114,18 @@ describe('PrometheusManagerMetrics', () => { "seconds": 1000 }, "dataTransferred": { - "bytes": 1000 + "bytes": 12000 }, "bandwidth": { "current": { "data": { - "bytes": 1234 + "bytes": 10 }, "timestamp": 1739284734 }, "peak": { "data": { - "bytes": 5678 + "bytes": 30 }, "timestamp": 1738959398 } @@ -184,7 +136,7 @@ describe('PrometheusManagerMetrics', () => { "asn": 49490, "asOrg": "Test AS Org", "dataTransferred": { - "bytes": 1000 + "bytes": 12000 }, "tunnelTime": { "seconds": 1000 @@ -196,7 +148,7 @@ describe('PrometheusManagerMetrics', () => { { "accessKeyId": 0, "dataTransferred": { - "bytes": 1000 + "bytes": 12000 }, "tunnelTime": { "seconds": 1000 @@ -218,20 +170,7 @@ describe('PrometheusManagerMetrics', () => { const managerMetrics = new PrometheusManagerMetrics( new QueryMapPrometheusClient( { - 'sum(rate(shadowsocks_data_bytes_per_location{dir=~"ct"}[300s]))': { - resultType: 'vector', - result: [ - { - metric: { - location: 'US', - asn: '49490', - asorg: 'Test AS Org', - }, - value: [1739284734, '1234'], - }, - ], - }, - 'sum(increase(shadowsocks_data_bytes_per_location{dir=~"ct"}[0s])) by (location, asn, asorg)': + 'sum(increase(shadowsocks_tunnel_time_seconds_per_location[0s])) by (location, asn, asorg)': { resultType: 'vector', result: [ @@ -245,58 +184,25 @@ describe('PrometheusManagerMetrics', () => { }, ], }, - 'sum(increase(shadowsocks_tunnel_time_seconds_per_location[0s])) by (location, asn, asorg)': + }, + { + 'sum(increase(shadowsocks_data_bytes_per_location{dir=~"ct"}[300s])) by (location, asn, asorg)': { - resultType: 'vector', + resultType: 'matrix', result: [ { metric: { - location: 'CA', + location: 'US', + asn: '49490', + asorg: 'Test AS Org', }, - value: [1738959398, '1000'], + values: [ + [1738959398, '9000'], + [1739284734, '3000'], + ], }, ], }, - 'sum(increase(shadowsocks_data_bytes{dir=~"ct"}[0s])) by (access_key)': { - resultType: 'vector', - result: [ - { - metric: { - access_key: '0', - }, - value: [1738959398, '1000'], - }, - ], - }, - 'sum(increase(shadowsocks_tunnel_time_seconds[0s])) by (access_key)': { - resultType: 'vector', - result: [ - { - metric: { - access_key: '1', - }, - value: [1738959398, '1000'], - }, - ], - }, - }, - { - 'sum(rate(shadowsocks_data_bytes_per_location{dir=~"ct"}[300s]))': { - resultType: 'matrix', - result: [ - { - metric: { - location: 'US', - asn: '49490', - asorg: 'Test AS Org', - }, - values: [ - [1738959398, '5678'], - [1739284734, '1234'], - ], - }, - ], - }, 'sum(increase(shadowsocks_data_bytes{dir=~"ct"}[300s])) by (access_key)': { resultType: 'matrix', result: [ @@ -305,8 +211,8 @@ describe('PrometheusManagerMetrics', () => { access_key: '0', }, values: [ - [1738959398, '1000'], - [1738959398, '2000'], + [1738959398, '9000'], + [1739284734, '3000'], ], }, ], @@ -337,74 +243,47 @@ describe('PrometheusManagerMetrics', () => { "seconds": 1000 }, "dataTransferred": { - "bytes": 1000 + "bytes": 12000 }, "bandwidth": { "current": { "data": { - "bytes": 1234 + "bytes": 10 }, "timestamp": 1739284734 }, "peak": { "data": { - "bytes": 5678 + "bytes": 30 }, "timestamp": 1738959398 } }, "locations": [ - { - "location": "CA", - "asn": null, - "asOrg": null, - "dataTransferred": { - "bytes": 0 - }, - "tunnelTime": { - "seconds": 1000 - } - }, { "location": "US", "asn": 49490, "asOrg": "Test AS Org", "dataTransferred": { - "bytes": 1000 + "bytes": 12000 }, "tunnelTime": { - "seconds": 0 + "seconds": 1000 } } ] }, "accessKeys": [ - { - "accessKeyId": 1, - "dataTransferred": { - "bytes": 0 - }, - "tunnelTime": { - "seconds": 1000 - }, - "connection": { - "lastTrafficSeen": null, - "peakDeviceCount": { - "data": 0, - "timestamp": null - } - } - }, { "accessKeyId": 0, "dataTransferred": { - "bytes": 1000 + "bytes": 12000 }, "tunnelTime": { - "seconds": 0 + "seconds": 1000 }, "connection": { - "lastTrafficSeen": 1738959398, + "lastTrafficSeen": 1739284734, "peakDeviceCount": { "data": 4, "timestamp": 1738959398 From 7a5a96c4e556a307f7186edd2a4f250fb59b1b72 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Thu, 27 Feb 2025 16:36:58 -0500 Subject: [PATCH 10/13] i _think_ this test is no longer necessary --- src/shadowbox/server/manager_metrics.spec.ts | 129 ------------------- 1 file changed, 129 deletions(-) diff --git a/src/shadowbox/server/manager_metrics.spec.ts b/src/shadowbox/server/manager_metrics.spec.ts index ba102da61..4d49de8a4 100644 --- a/src/shadowbox/server/manager_metrics.spec.ts +++ b/src/shadowbox/server/manager_metrics.spec.ts @@ -166,135 +166,6 @@ describe('PrometheusManagerMetrics', () => { done(); }); - it('getServerMetrics - does a full outer join on metric data', async (done) => { - const managerMetrics = new PrometheusManagerMetrics( - new QueryMapPrometheusClient( - { - 'sum(increase(shadowsocks_tunnel_time_seconds_per_location[0s])) by (location, asn, asorg)': - { - resultType: 'vector', - result: [ - { - metric: { - location: 'US', - asn: '49490', - asorg: 'Test AS Org', - }, - value: [1738959398, '1000'], - }, - ], - }, - }, - { - 'sum(increase(shadowsocks_data_bytes_per_location{dir=~"ct"}[300s])) by (location, asn, asorg)': - { - resultType: 'matrix', - result: [ - { - metric: { - location: 'US', - asn: '49490', - asorg: 'Test AS Org', - }, - values: [ - [1738959398, '9000'], - [1739284734, '3000'], - ], - }, - ], - }, - 'sum(increase(shadowsocks_data_bytes{dir=~"ct"}[300s])) by (access_key)': { - resultType: 'matrix', - result: [ - { - metric: { - access_key: '0', - }, - values: [ - [1738959398, '9000'], - [1739284734, '3000'], - ], - }, - ], - }, - 'sum(increase(shadowsocks_tunnel_time_seconds[300s])) by (access_key)': { - resultType: 'matrix', - result: [ - { - metric: { - access_key: '0', - }, - values: [ - [1738959398, '1000'], - [1738959398, '0'], - ], - }, - ], - }, - } - ) - ); - - const serverMetrics = await managerMetrics.getServerMetrics({seconds: 0}); - - expect(JSON.stringify(serverMetrics, null, 2)).toEqual(`{ - "server": { - "tunnelTime": { - "seconds": 1000 - }, - "dataTransferred": { - "bytes": 12000 - }, - "bandwidth": { - "current": { - "data": { - "bytes": 10 - }, - "timestamp": 1739284734 - }, - "peak": { - "data": { - "bytes": 30 - }, - "timestamp": 1738959398 - } - }, - "locations": [ - { - "location": "US", - "asn": 49490, - "asOrg": "Test AS Org", - "dataTransferred": { - "bytes": 12000 - }, - "tunnelTime": { - "seconds": 1000 - } - } - ] - }, - "accessKeys": [ - { - "accessKeyId": 0, - "dataTransferred": { - "bytes": 12000 - }, - "tunnelTime": { - "seconds": 1000 - }, - "connection": { - "lastTrafficSeen": 1739284734, - "peakDeviceCount": { - "data": 4, - "timestamp": 1738959398 - } - } - } - ] -}`); - done(); - }); - it('getOutboundByteTransfer', async (done) => { const managerMetrics = new PrometheusManagerMetrics( new FakePrometheusClient({'access-key-1': 1000, 'access-key-2': 10000}) From eaf1cad9279cd425bf0bd716728f6a75e1f5f940 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Thu, 27 Feb 2025 16:42:27 -0500 Subject: [PATCH 11/13] ? --- src/shadowbox/server/manager_metrics.spec.ts | 156 +++++++++++++++++++ 1 file changed, 156 insertions(+) diff --git a/src/shadowbox/server/manager_metrics.spec.ts b/src/shadowbox/server/manager_metrics.spec.ts index 4d49de8a4..4113561d4 100644 --- a/src/shadowbox/server/manager_metrics.spec.ts +++ b/src/shadowbox/server/manager_metrics.spec.ts @@ -166,6 +166,162 @@ describe('PrometheusManagerMetrics', () => { done(); }); + it('getServerMetrics - combines sources', async (done) => { + const managerMetrics = new PrometheusManagerMetrics( + new QueryMapPrometheusClient( + { + 'sum(increase(shadowsocks_tunnel_time_seconds_per_location[0s])) by (location, asn, asorg)': + { + resultType: 'vector', + result: [ + { + metric: { + location: 'US', + asn: '49490', + asorg: 'Test AS Org', + }, + value: [1738959398, '1000'], + }, + ], + }, + }, + { + 'sum(increase(shadowsocks_data_bytes_per_location{dir=~"ct"}[300s])) by (location, asn, asorg)': + { + resultType: 'matrix', + result: [ + { + metric: { + location: 'CA', + }, + values: [ + [1738959398, '9000'], + [1739284734, '3000'], + ], + }, + ], + }, + 'sum(increase(shadowsocks_data_bytes{dir=~"ct"}[300s])) by (access_key)': { + resultType: 'matrix', + result: [ + { + metric: { + access_key: '0', + }, + values: [ + [1738959398, '9000'], + [1739284734, '3000'], + ], + }, + ], + }, + 'sum(increase(shadowsocks_tunnel_time_seconds[300s])) by (access_key)': { + resultType: 'matrix', + result: [ + { + metric: { + access_key: '1', + }, + values: [ + [1738959398, '1000'], + [1738959398, '0'], + ], + }, + ], + }, + } + ) + ); + + const serverMetrics = await managerMetrics.getServerMetrics({seconds: 0}); + + console.log(JSON.stringify(serverMetrics, null, 2)); + + expect(JSON.stringify(serverMetrics, null, 2)).toEqual(`{ + "server": { + "tunnelTime": { + "seconds": 1000 + }, + "dataTransferred": { + "bytes": 12000 + }, + "bandwidth": { + "current": { + "data": { + "bytes": 10 + }, + "timestamp": 1739284734 + }, + "peak": { + "data": { + "bytes": 30 + }, + "timestamp": 1738959398 + } + }, + "locations": [ + { + "location": "CA", + "asn": null, + "asOrg": null, + "dataTransferred": { + "bytes": 12000 + }, + "tunnelTime": { + "seconds": 0 + } + }, + { + "location": "US", + "asn": 49490, + "asOrg": "Test AS Org", + "dataTransferred": { + "bytes": 0 + }, + "tunnelTime": { + "seconds": 1000 + } + } + ] + }, + "accessKeys": [ + { + "accessKeyId": 0, + "dataTransferred": { + "bytes": 12000 + }, + "tunnelTime": { + "seconds": 0 + }, + "connection": { + "lastTrafficSeen": 1739284734, + "peakDeviceCount": { + "data": 0, + "timestamp": null + } + } + }, + { + "accessKeyId": 1, + "dataTransferred": { + "bytes": 0 + }, + "tunnelTime": { + "seconds": 1000 + }, + "connection": { + "lastTrafficSeen": null, + "peakDeviceCount": { + "data": 4, + "timestamp": 1738959398 + } + } + } + ] +}`); + done(); + }); + it('getOutboundByteTransfer', async (done) => { const managerMetrics = new PrometheusManagerMetrics( new FakePrometheusClient({'access-key-1': 1000, 'access-key-2': 10000}) From a255c816cb63e7ce235d644a094a4bd3cd4ee3a9 Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Thu, 27 Feb 2025 16:43:19 -0500 Subject: [PATCH 12/13] missing parent --- src/shadowbox/server/manager_metrics.spec.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/shadowbox/server/manager_metrics.spec.ts b/src/shadowbox/server/manager_metrics.spec.ts index 4113561d4..68b163c47 100644 --- a/src/shadowbox/server/manager_metrics.spec.ts +++ b/src/shadowbox/server/manager_metrics.spec.ts @@ -235,8 +235,6 @@ describe('PrometheusManagerMetrics', () => { const serverMetrics = await managerMetrics.getServerMetrics({seconds: 0}); - console.log(JSON.stringify(serverMetrics, null, 2)); - expect(JSON.stringify(serverMetrics, null, 2)).toEqual(`{ "server": { "tunnelTime": { @@ -301,7 +299,7 @@ describe('PrometheusManagerMetrics', () => { } } }, - { + { "accessKeyId": 1, "dataTransferred": { "bytes": 0 From cfe3704012d747327e08609723d07b45b05c53ac Mon Sep 17 00:00:00 2001 From: Daniel LaCosse <3759828+daniellacosse@users.noreply.github.com> Date: Mon, 3 Mar 2025 11:31:03 -0500 Subject: [PATCH 13/13] well, that fixed the peak value --- src/shadowbox/server/manager_metrics.ts | 26 ++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/shadowbox/server/manager_metrics.ts b/src/shadowbox/server/manager_metrics.ts index ba29d9244..643e910eb 100644 --- a/src/shadowbox/server/manager_metrics.ts +++ b/src/shadowbox/server/manager_metrics.ts @@ -153,7 +153,7 @@ export class PrometheusManagerMetrics implements ManagerMetrics { locations: [], }; - const bandwidthRangeValues: [number, string][] = []; + const bandwidthTimeseriesIndex = new Map(); const accessKeyMap = new Map(); for (const result of dataTransferredByAccessKeyRange.result) { @@ -164,19 +164,23 @@ export class PrometheusManagerMetrics implements ManagerMetrics { entry.dataTransferred.bytes = findSum(result.values ?? []); for (const entryIndex in result.values) { - const [currentTimestamp, nextValue] = result.values[entryIndex]; - const [previousTimestamp, currentValue] = bandwidthRangeValues[entryIndex] ?? [ - Infinity, - '0', - ]; - - bandwidthRangeValues[entryIndex] = [ - Math.min(previousTimestamp, currentTimestamp), - String(parseFloat(currentValue as string) + (nextValue ? parseFloat(nextValue) : 0)), - ]; + const [timestamp, value] = result.values[entryIndex]; + + bandwidthTimeseriesIndex.set( + timestamp, + bandwidthTimeseriesIndex.has(timestamp) + ? String( + parseFloat(bandwidthTimeseriesIndex.get(timestamp) as string) + parseFloat(value) + ) + : value + ); } } + const bandwidthRangeValues = [...bandwidthTimeseriesIndex.entries()].sort( + (a, b) => a[0] - b[0] + ); + const currentBandwidth = bandwidthRangeValues[bandwidthRangeValues.length - 1] ?? [0, '0']; // convert increase() into rate()