Skip to content

Commit

Permalink
feat(server): use rate() over increase() for bandwidth calculatio…
Browse files Browse the repository at this point in the history
…ns (#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.
  • Loading branch information
sbruens authored Feb 12, 2025
1 parent 76a23c6 commit 2cc12e0
Show file tree
Hide file tree
Showing 2 changed files with 37 additions and 29 deletions.
30 changes: 18 additions & 12 deletions src/shadowbox/server/manager_metrics.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: [
{
Expand Down Expand Up @@ -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: [
{
Expand Down Expand Up @@ -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": {
Expand Down Expand Up @@ -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: [
{
Expand Down Expand Up @@ -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: [
{
Expand Down Expand Up @@ -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": {
Expand Down
36 changes: 19 additions & 17 deletions src/shadowbox/server/manager_metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
}

Expand Down Expand Up @@ -113,8 +113,8 @@ export class PrometheusManagerMetrics implements ManagerMetrics {
const start = end - timeframe.seconds;

const [
totalDataTransferred,
totalDataTransferredRange,
bandwidth,
bandwidthRange,
dataTransferredByLocation,
tunnelTimeByLocation,
dataTransferredByAccessKey,
Expand All @@ -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`
Expand Down Expand Up @@ -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.
Expand All @@ -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());

Expand Down

0 comments on commit 2cc12e0

Please sign in to comment.