Skip to content

Commit

Permalink
feat(server): stop sharing key usage with metrics server (Jigsaw-Code…
Browse files Browse the repository at this point in the history
…#1529)

* feat(server): stop sharing key usage with metrics server

* Update comment to reflect history.
  • Loading branch information
sbruens authored Apr 4, 2024
1 parent be02db8 commit a072cc4
Show file tree
Hide file tree
Showing 2 changed files with 6 additions and 78 deletions.
31 changes: 1 addition & 30 deletions src/shadowbox/server/shared_metrics.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import {
CountryUsage,
DailyFeatureMetricsReportJson,
HourlyServerMetricsReportJson,
KeyUsage,
MetricsCollectorClient,
OutlineSharedMetricsPublisher,
UsageMetrics,
Expand Down Expand Up @@ -83,11 +82,6 @@ describe('OutlineSharedMetricsPublisher', () => {
);

publisher.startSharing();
usageMetrics.keyUsage = [
{accessKeyId: 'user-0', inboundBytes: 11},
{accessKeyId: 'user-1', inboundBytes: 22},
{accessKeyId: 'user-0', inboundBytes: 33},
];
usageMetrics.countryUsage = [
{country: 'AA', inboundBytes: 11},
{country: 'BB', inboundBytes: 11},
Expand All @@ -103,9 +97,6 @@ describe('OutlineSharedMetricsPublisher', () => {
startUtcMs: startTime,
endUtcMs: clock.nowMs,
userReports: [
{userId: 'M(user-0)', bytesTransferred: 11},
{userId: 'M(user-1)', bytesTransferred: 22},
{userId: 'M(user-0)', bytesTransferred: 33},
{bytesTransferred: 11, countries: ['AA']},
{bytesTransferred: 11, countries: ['BB']},
{bytesTransferred: 22, countries: ['CC']},
Expand All @@ -115,10 +106,6 @@ describe('OutlineSharedMetricsPublisher', () => {
});

startTime = clock.nowMs;
usageMetrics.keyUsage = [
{accessKeyId: 'user-0', inboundBytes: 44},
{accessKeyId: 'user-2', inboundBytes: 55},
];
usageMetrics.countryUsage = [
{country: 'EE', inboundBytes: 44},
{country: 'FF', inboundBytes: 55},
Expand All @@ -131,8 +118,6 @@ describe('OutlineSharedMetricsPublisher', () => {
startUtcMs: startTime,
endUtcMs: clock.nowMs,
userReports: [
{userId: 'M(user-0)', bytesTransferred: 44},
{userId: 'M(user-2)', bytesTransferred: 55},
{bytesTransferred: 44, countries: ['EE']},
{bytesTransferred: 55, countries: ['FF']},
],
Expand All @@ -157,11 +142,6 @@ describe('OutlineSharedMetricsPublisher', () => {
);

publisher.startSharing();
usageMetrics.keyUsage = [
{accessKeyId: 'user-0', inboundBytes: 11},
{accessKeyId: 'user-1', inboundBytes: 22},
{accessKeyId: 'user-0', inboundBytes: 33},
];
usageMetrics.countryUsage = [
{country: 'AA', inboundBytes: 11},
{country: 'SY', inboundBytes: 11},
Expand All @@ -177,9 +157,6 @@ describe('OutlineSharedMetricsPublisher', () => {
startUtcMs: startTime,
endUtcMs: clock.nowMs,
userReports: [
{userId: 'M(user-0)', bytesTransferred: 11},
{userId: 'M(user-1)', bytesTransferred: 22},
{userId: 'M(user-0)', bytesTransferred: 33},
{bytesTransferred: 11, countries: ['AA']},
{bytesTransferred: 22, countries: ['CC']},
{bytesTransferred: 33, countries: ['AA']},
Expand Down Expand Up @@ -289,19 +266,13 @@ class FakeMetricsCollector implements MetricsCollectorClient {
}

class ManualUsageMetrics implements UsageMetrics {
public keyUsage = [] as KeyUsage[];
public countryUsage = [] as CountryUsage[];

getKeyUsage(): Promise<KeyUsage[]> {
return Promise.resolve(this.keyUsage);
}

getCountryUsage(): Promise<CountryUsage[]> {
return Promise.resolve(this.countryUsage)
return Promise.resolve(this.countryUsage);
}

reset() {
this.keyUsage = [] as KeyUsage[];
this.countryUsage = [] as CountryUsage[];
}
}
53 changes: 5 additions & 48 deletions src/shadowbox/server/shared_metrics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,6 @@ const MS_PER_HOUR = 60 * 60 * 1000;
const MS_PER_DAY = 24 * MS_PER_HOUR;
const SANCTIONED_COUNTRIES = new Set(['CU', 'KP', 'SY']);

// Used internally to track key usage.
export interface KeyUsage {
accessKeyId: string;
inboundBytes: number;
}

export interface CountryUsage {
country: string;
inboundBytes: number;
Expand All @@ -50,8 +44,7 @@ export interface HourlyServerMetricsReportJson {
// JSON format for the published report.
// Field renames will break backwards-compatibility.
export interface HourlyUserMetricsReportJson {
userId?: string;
countries?: string[];
countries: string[];
bytesTransferred: number;
}

Expand All @@ -78,7 +71,6 @@ export interface SharedMetricsPublisher {
}

export interface UsageMetrics {
getKeyUsage(): Promise<KeyUsage[]>;
getCountryUsage(): Promise<CountryUsage[]>;
reset();
}
Expand All @@ -89,23 +81,6 @@ export class PrometheusUsageMetrics implements UsageMetrics {

constructor(private prometheusClient: PrometheusClient) {}

async getKeyUsage(): Promise<KeyUsage[]> {
const timeDeltaSecs = Math.round((Date.now() - this.resetTimeMs) / 1000);
// We measure the traffic to and from the target, since that's what we are protecting.
const result = await this.prometheusClient.query(
`sum(increase(shadowsocks_data_bytes{dir=~"p>t|p<t"}[${timeDeltaSecs}s])) by (access_key)`
);
const usage = [] as KeyUsage[];
for (const entry of result.result) {
const accessKeyId = entry.metric['access_key'] || '';
const inboundBytes = Math.round(parseFloat(entry.value[1]));
if (inboundBytes > 0) {
usage.push({accessKeyId, inboundBytes});
}
}
return usage;
}

async getCountryUsage(): Promise<CountryUsage[]> {
const timeDeltaSecs = Math.round((Date.now() - this.resetTimeMs) / 1000);
// We measure the traffic to and from the target, since that's what we are protecting.
Expand Down Expand Up @@ -191,9 +166,7 @@ export class OutlineSharedMetricsPublisher implements SharedMetricsPublisher {
return;
}
try {
const keyUsagePromise = usageMetrics.getKeyUsage()
const countryUsagePromise = usageMetrics.getCountryUsage()
await this.reportServerUsageMetrics(await keyUsagePromise, await countryUsagePromise);
await this.reportServerUsageMetrics(await usageMetrics.getCountryUsage());
usageMetrics.reset();
} catch (err) {
logging.error(`Failed to report server usage metrics: ${err}`);
Expand Down Expand Up @@ -227,35 +200,19 @@ export class OutlineSharedMetricsPublisher implements SharedMetricsPublisher {
return this.serverConfig.data().metricsEnabled || false;
}

private async reportServerUsageMetrics(keyUsageMetrics: KeyUsage[], countryUsageMetrics: CountryUsage[]): Promise<void> {
private async reportServerUsageMetrics(countryUsageMetrics: CountryUsage[]): Promise<void> {
const reportEndTimestampMs = this.clock.now();

const userReports = [] as HourlyUserMetricsReportJson[];
// HACK! We use the same backend reporting endpoint for key and country usage.
// A row with empty country is for key usage, a row with empty userId is for country usage.
// Note that this reports usage twice. If you want the total, filter to rows with non empty countries.
for (const keyUsage of keyUsageMetrics) {
if (keyUsage.inboundBytes === 0) {
continue;
}
const userId = this.toMetricsId(keyUsage.accessKeyId);
if (!userId) {
continue;
}
userReports.push({
userId,
bytesTransferred: keyUsage.inboundBytes,
});
}
for (const countryUsage of countryUsageMetrics) {
if (countryUsage.inboundBytes === 0) {
continue;
}
if (isSanctionedCountry(countryUsage.country)) {
continue;
}
// Make sure to always set the country to differentiate the row
// from key usage rows.
// Make sure to always set a country, which is required by the metrics server validation.
// It's used to differentiate the row from the legacy key usage rows.
const country = countryUsage.country || 'ZZ';
userReports.push({
bytesTransferred: countryUsage.inboundBytes,
Expand Down

0 comments on commit a072cc4

Please sign in to comment.