Skip to content

Commit f5c1380

Browse files
committed
feat(opentelemetry-sampler-aws-xray): Add Rate Limiter and Sampling Targets Poller Logic
1 parent 5988c79 commit f5c1380

13 files changed

+1194
-59
lines changed

incubator/opentelemetry-sampler-aws-xray/src/aws-xray-sampling-client.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
1919
// SPDX-License-Identifier: Apache-2.0
2020

21-
import { DiagLogFunction, DiagLogger, context } from '@opentelemetry/api';
21+
import { DiagLogFunction, context, diag } from '@opentelemetry/api';
2222
import { suppressTracing } from '@opentelemetry/core';
2323
import * as http from 'http';
2424
import {
@@ -30,12 +30,10 @@ import {
3030
export class AWSXRaySamplingClient {
3131
private getSamplingRulesEndpoint: string;
3232
private samplingTargetsEndpoint: string;
33-
private samplerDiag: DiagLogger;
3433

35-
constructor(endpoint: string, samplerDiag: DiagLogger) {
34+
constructor(endpoint: string) {
3635
this.getSamplingRulesEndpoint = endpoint + '/GetSamplingRules';
3736
this.samplingTargetsEndpoint = endpoint + '/SamplingTargets';
38-
this.samplerDiag = samplerDiag;
3937
}
4038

4139
public fetchSamplingTargets(
@@ -45,7 +43,7 @@ export class AWSXRaySamplingClient {
4543
this.makeSamplingRequest<GetSamplingTargetsResponse>(
4644
this.samplingTargetsEndpoint,
4745
callback,
48-
this.samplerDiag.debug,
46+
diag.debug,
4947
JSON.stringify(requestBody)
5048
);
5149
}
@@ -56,7 +54,7 @@ export class AWSXRaySamplingClient {
5654
this.makeSamplingRequest<GetSamplingRulesResponse>(
5755
this.getSamplingRulesEndpoint,
5856
callback,
59-
this.samplerDiag.error
57+
diag.error
6058
);
6159
}
6260

@@ -98,10 +96,8 @@ export class AWSXRaySamplingClient {
9896
callback(responseObject);
9997
}
10098
} else {
101-
this.samplerDiag.debug(
102-
`${url} Response Code is: ${response.statusCode}`
103-
);
104-
this.samplerDiag.debug(`${url} responseData is: ${responseData}`);
99+
diag.debug(`${url} Response Code is: ${response.statusCode}`);
100+
diag.debug(`${url} responseData is: ${responseData}`);
105101
}
106102
});
107103
})

incubator/opentelemetry-sampler-aws-xray/src/fallback-sampler.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,20 @@
2121
import { Attributes, Context, Link, SpanKind } from '@opentelemetry/api';
2222
import {
2323
Sampler,
24+
SamplingDecision,
2425
SamplingResult,
2526
TraceIdRatioBasedSampler,
2627
} from '@opentelemetry/sdk-trace-base';
28+
import { RateLimitingSampler } from './rate-limiting-sampler';
2729

2830
// FallbackSampler samples 1 req/sec and additional 5% of requests using TraceIdRatioBasedSampler.
2931
export class FallbackSampler implements Sampler {
3032
private fixedRateSampler: TraceIdRatioBasedSampler;
33+
private rateLimitingSampler: RateLimitingSampler;
3134

3235
constructor() {
3336
this.fixedRateSampler = new TraceIdRatioBasedSampler(0.05);
37+
this.rateLimitingSampler = new RateLimitingSampler(1);
3438
}
3539

3640
shouldSample(
@@ -41,7 +45,19 @@ export class FallbackSampler implements Sampler {
4145
attributes: Attributes,
4246
links: Link[]
4347
): SamplingResult {
44-
// TODO: implement and use Rate Limiting Sampler
48+
const samplingResult: SamplingResult =
49+
this.rateLimitingSampler.shouldSample(
50+
context,
51+
traceId,
52+
spanName,
53+
spanKind,
54+
attributes,
55+
links
56+
);
57+
58+
if (samplingResult.decision !== SamplingDecision.NOT_RECORD) {
59+
return samplingResult;
60+
}
4561

4662
return this.fixedRateSampler.shouldSample(context, traceId);
4763
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
// Includes work from:
18+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
19+
// SPDX-License-Identifier: Apache-2.0
20+
21+
/*
22+
* The RateLimiter keeps track of the current reservoir quota balance available (measured via available time)
23+
* If enough time has elapsed, the RateLimiter will allow quota balance to be consumed/taken (decrease available time)
24+
* A RateLimitingSampler uses this RateLimiter to determine if it should sample or not based on the quota balance available.
25+
*/
26+
export class RateLimiter {
27+
// Quota assigned to client to dictate maximum quota balance that can be consumed per second.
28+
private quota: number;
29+
private MAX_BALANCE_MILLIS: number;
30+
// Used to measure current quota balance.
31+
private walletFloorMillis: number;
32+
33+
constructor(quota: number, maxBalanceInSeconds = 1) {
34+
this.MAX_BALANCE_MILLIS = maxBalanceInSeconds * 1000.0;
35+
this.quota = quota;
36+
this.walletFloorMillis = Date.now();
37+
// current "balance" would be `ceiling - floor`
38+
}
39+
40+
public take(cost = 1): boolean {
41+
if (this.quota === 0) {
42+
return false;
43+
}
44+
45+
const quotaPerMillis: number = this.quota / 1000.0;
46+
47+
// assume divide by zero not possible
48+
const costInMillis: number = cost / quotaPerMillis;
49+
50+
const walletCeilingMillis: number = Date.now();
51+
let currentBalanceMillis: number =
52+
walletCeilingMillis - this.walletFloorMillis;
53+
currentBalanceMillis = Math.min(
54+
currentBalanceMillis,
55+
this.MAX_BALANCE_MILLIS
56+
);
57+
const pendingRemainingBalanceMillis: number =
58+
currentBalanceMillis - costInMillis;
59+
if (pendingRemainingBalanceMillis >= 0) {
60+
this.walletFloorMillis =
61+
walletCeilingMillis - pendingRemainingBalanceMillis;
62+
return true;
63+
}
64+
// No changes to the wallet state
65+
return false;
66+
}
67+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
// Includes work from:
18+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
19+
// SPDX-License-Identifier: Apache-2.0
20+
21+
import { Attributes, Context, Link, SpanKind } from '@opentelemetry/api';
22+
import {
23+
Sampler,
24+
SamplingDecision,
25+
SamplingResult,
26+
} from '@opentelemetry/sdk-trace-base';
27+
import { RateLimiter } from './rate-limiter';
28+
29+
export class RateLimitingSampler implements Sampler {
30+
private quota: number;
31+
private reservoir: RateLimiter;
32+
33+
constructor(quota: number) {
34+
this.quota = quota;
35+
this.reservoir = new RateLimiter(quota);
36+
}
37+
38+
shouldSample(
39+
context: Context,
40+
traceId: string,
41+
spanName: string,
42+
spanKind: SpanKind,
43+
attributes: Attributes,
44+
links: Link[]
45+
): SamplingResult {
46+
if (this.reservoir.take(1)) {
47+
return {
48+
decision: SamplingDecision.RECORD_AND_SAMPLED,
49+
attributes: attributes,
50+
};
51+
}
52+
return { decision: SamplingDecision.NOT_RECORD, attributes: attributes };
53+
}
54+
55+
public toString(): string {
56+
return `RateLimitingSampler{rate limiting sampling with sampling config of ${this.quota} req/sec and 0% of additional requests}`;
57+
}
58+
}

0 commit comments

Comments
 (0)