feat(server): use rate() over increase() for bandwidth calculations (#1638)

* feat(server): use `rate()` instead of `increase()` for bandwidth calculations

* Add total data transferred a separate top-level metric so we don't have to calculate it client-side.
This commit is contained in:
Sander Bruens 2025-02-12 12:32:25 -05:00 committed by GitHub
parent 76a23c6e81
commit 2cc12e0cb3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 37 additions and 29 deletions

View file

@ -41,7 +41,7 @@ describe('PrometheusManagerMetrics', () => {
const managerMetrics = new PrometheusManagerMetrics(
new QueryMapPrometheusClient(
{
'sum(increase(shadowsocks_data_bytes_per_location{dir=~"c<p|p>t"}[300s]))': {
'sum(rate(shadowsocks_data_bytes_per_location{dir=~"c<p|p>t"}[300s]))': {
resultType: 'vector',
result: [
{
@ -106,7 +106,7 @@ describe('PrometheusManagerMetrics', () => {
},
},
{
'sum(increase(shadowsocks_data_bytes_per_location{dir=~"c<p|p>t"}[300s]))': {
'sum(rate(shadowsocks_data_bytes_per_location{dir=~"c<p|p>t"}[300s]))': {
resultType: 'matrix',
result: [
{
@ -162,11 +162,14 @@ describe('PrometheusManagerMetrics', () => {
"seconds": 1000
},
"dataTransferred": {
"total": {
"bytes": 1000
},
"bytes": 1000
},
"bandwidth": {
"current": {
"bytes": 1234
"data": {
"bytes": 1234
},
"timestamp": 1739284734
},
"peak": {
"data": {
@ -216,7 +219,7 @@ describe('PrometheusManagerMetrics', () => {
const managerMetrics = new PrometheusManagerMetrics(
new QueryMapPrometheusClient(
{
'sum(increase(shadowsocks_data_bytes_per_location{dir=~"c<p|p>t"}[300s]))': {
'sum(rate(shadowsocks_data_bytes_per_location{dir=~"c<p|p>t"}[300s]))': {
resultType: 'vector',
result: [
{
@ -279,7 +282,7 @@ describe('PrometheusManagerMetrics', () => {
},
},
{
'sum(increase(shadowsocks_data_bytes_per_location{dir=~"c<p|p>t"}[300s]))': {
'sum(rate(shadowsocks_data_bytes_per_location{dir=~"c<p|p>t"}[300s]))': {
resultType: 'matrix',
result: [
{
@ -335,11 +338,14 @@ describe('PrometheusManagerMetrics', () => {
"seconds": 1000
},
"dataTransferred": {
"total": {
"bytes": 1000
},
"bytes": 1000
},
"bandwidth": {
"current": {
"bytes": 1234
"data": {
"bytes": 1234
},
"timestamp": 1739284734
},
"peak": {
"data": {

View file

@ -41,14 +41,14 @@ interface ConnectionStats {
}
interface BandwidthStats {
total: Data;
current: Data;
current: TimedData<Data>;
peak: TimedData<Data>;
}
interface ServerMetricsServerEntry {
tunnelTime: Duration;
dataTransferred: BandwidthStats;
dataTransferred: Data;
bandwidth: BandwidthStats;
locations: ServerMetricsLocationEntry[];
}
@ -113,8 +113,8 @@ export class PrometheusManagerMetrics implements ManagerMetrics {
const start = end - timeframe.seconds;
const [
totalDataTransferred,
totalDataTransferredRange,
bandwidth,
bandwidthRange,
dataTransferredByLocation,
tunnelTimeByLocation,
dataTransferredByAccessKey,
@ -123,10 +123,10 @@ export class PrometheusManagerMetrics implements ManagerMetrics {
tunnelTimeByAccessKeyRange,
] = await Promise.all([
this.prometheusClient.query(
`sum(increase(shadowsocks_data_bytes_per_location{dir=~"c<p|p>t"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]))`
`sum(rate(shadowsocks_data_bytes_per_location{dir=~"c<p|p>t"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]))`
),
this.prometheusClient.queryRange(
`sum(increase(shadowsocks_data_bytes_per_location{dir=~"c<p|p>t"}[${PROMETHEUS_RANGE_QUERY_STEP_SECONDS}s]))`,
`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`
@ -159,25 +159,27 @@ export class PrometheusManagerMetrics implements ManagerMetrics {
const serverMetrics: ServerMetricsServerEntry = {
tunnelTime: {seconds: 0},
dataTransferred: {
total: {bytes: 0},
current: {bytes: 0},
dataTransferred: {bytes: 0},
bandwidth: {
current: {data: {bytes: 0}, timestamp: null},
peak: {data: {bytes: 0}, timestamp: null},
},
locations: [],
};
for (const result of totalDataTransferred.result) {
const bytes = result.value ? parseFloat(result.value[1]) : 0;
serverMetrics.dataTransferred.current.bytes = bytes;
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 totalDataTransferredRange.result) {
for (const result of bandwidthRange.result) {
const peakDataTransferred = findPeak(result.values ?? []);
if (peakDataTransferred !== null) {
const peakValue = parseFloat(peakDataTransferred[1]);
if (peakValue > 0) {
serverMetrics.dataTransferred.peak.data.bytes = peakValue;
serverMetrics.dataTransferred.peak.timestamp = Math.min(now, peakDataTransferred[0]);
serverMetrics.bandwidth.peak.data.bytes = peakValue;
serverMetrics.bandwidth.peak.timestamp = Math.min(now, peakDataTransferred[0]);
}
}
break; // There should only be one result.
@ -194,7 +196,7 @@ export class PrometheusManagerMetrics implements ManagerMetrics {
const entry = getServerMetricsLocationEntry(locationMap, result.metric);
const bytes = result.value ? parseFloat(result.value[1]) : 0;
entry.dataTransferred.bytes = bytes;
serverMetrics.dataTransferred.total.bytes += bytes;
serverMetrics.dataTransferred.bytes += bytes;
}
serverMetrics.locations = Array.from(locationMap.values());