Skip to content
This repository was archived by the owner on Oct 3, 2023. It is now read-only.

Commit 7ca6aba

Browse files
justindsmithkjin
authored andcommitted
fix: sampler now samples correctly (#100)
Sampler was not adhering to the configured propability when not ALWAYS or NEVER. Issue was that the Sampler was using Number.MAX_VALUE, which uses 1024-bits. There were two issues with this approach: 1. The MAX_VALUE is NOT considered a "safe integer" and thus there are side effects during manipulation. 2. The `shouldSample` method was only comparing the idUpperBound (based on 1024-bits of data) to the lower 16 characters (64-bits) of a trace, which was resulting in the configured probabilty not being respected. We now us a 52-bit number, which falls within the "safe integer" range (53-bits) and is able to be fully represented by a 13-diget hex value. This allows the value to: 1. Be safely algorithmically manipulated without side effects. 2. Be easily compared to the lower 52-bits (13 characters) of a traceId. The tests have been updated to perform validation of traceIds that should and should not be sampled given a probability.
1 parent ed255e6 commit 7ca6aba

File tree

4 files changed

+50
-18
lines changed

4 files changed

+50
-18
lines changed

packages/opencensus-core/src/trace/config/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export interface BufferConfig {
3131

3232
/** Defines tracer configuration parameters */
3333
export interface TracerConfig {
34-
/** Determines the samplin rate. Ranges from 0.0 to 1.0 */
34+
/** Determines the sampling rate. Ranges from 0.0 to 1.0 */
3535
samplingRate?: number;
3636
/** Determines the ignored (or blacklisted) URLs */
3737
ignoreUrls?: Array<string|RegExp>;

packages/opencensus-core/src/trace/sampler/sampler.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,11 @@
1717
import {randomSpanId} from '../../internal/util';
1818
import {Sampler} from './types';
1919

20-
21-
const MIN_NUMBER = Number.MIN_VALUE;
22-
const MAX_NUMBER = Number.MAX_VALUE;
23-
20+
// We use 52-bits as our max number because it remains a javascript "safe
21+
// integer" for arithmetic and parsing while using the full hex range for
22+
// comparison to the lower order bytes on a traceId.
23+
const MAX_NUMBER = 0xfffffffffffff;
24+
const LOWER_BYTE_COUNT = 13;
2425

2526
/** Sampler that samples every trace. */
2627
export class AlwaysSampler implements Sampler {
@@ -61,7 +62,8 @@ export class ProbabilitySampler implements Sampler {
6162
* False if the traceId is not in probability.
6263
*/
6364
shouldSample(traceId: string): boolean {
64-
const LOWER_BYTES = traceId ? traceId.substring(16) : '0';
65+
const LOWER_BYTES =
66+
traceId ? ('0000000000000' + traceId).slice(-LOWER_BYTE_COUNT) : '0';
6567
// tslint:disable-next-line:ban Needed to parse hexadecimal.
6668
const LOWER_LONG = parseInt(LOWER_BYTES, 16);
6769

packages/opencensus-core/test/test-sampler.ts

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,16 @@ describe('Sampler', () => {
3131
it('should return a always sampler for 1', () => {
3232
const root = new RootSpan(tracer);
3333
const sampler = SamplerBuilder.getSampler(1);
34-
const samplerShouldSampler = sampler.shouldSample(root.traceId);
34+
const samplerShouldSample = sampler.shouldSample(root.traceId);
3535
assert.strictEqual(sampler.description, 'always');
36-
assert.ok(samplerShouldSampler);
36+
assert.ok(samplerShouldSample);
3737
});
3838
it('should return a always sampler for >1', () => {
3939
const root = new RootSpan(tracer);
4040
const sampler = SamplerBuilder.getSampler(100);
41-
const samplerShouldSampler = sampler.shouldSample(root.traceId);
41+
const samplerShouldSample = sampler.shouldSample(root.traceId);
4242
assert.strictEqual(sampler.description, 'always');
43-
assert.ok(samplerShouldSampler);
43+
assert.ok(samplerShouldSample);
4444
});
4545
});
4646
/**
@@ -50,26 +50,56 @@ describe('Sampler', () => {
5050
it('should return a never sampler for 0', () => {
5151
const root = new RootSpan(tracer);
5252
const sampler = SamplerBuilder.getSampler(0);
53-
const samplerShouldSampler = sampler.shouldSample(root.traceId);
53+
const samplerShouldSample = sampler.shouldSample(root.traceId);
5454
assert.strictEqual(sampler.description, 'never');
55-
assert.ok(!samplerShouldSampler);
55+
assert.ok(!samplerShouldSample);
5656
});
5757
it('should return a never sampler for negative value', () => {
5858
const root = new RootSpan(tracer);
5959
const sampler = SamplerBuilder.getSampler(-1);
60-
const samplerShouldSampler = sampler.shouldSample(root.traceId);
60+
const samplerShouldSample = sampler.shouldSample(root.traceId);
6161
assert.strictEqual(sampler.description, 'never');
62-
assert.ok(!samplerShouldSampler);
62+
assert.ok(!samplerShouldSample);
6363
});
6464
});
6565

6666
describe('shouldSample() probability', () => {
6767
it('should return a probability sampler', () => {
68-
const root = new RootSpan(tracer);
6968
const sampler = SamplerBuilder.getSampler(0.7);
7069
assert.ok(sampler.description.indexOf('probability') >= 0);
71-
const samplerShouldSampler = sampler.shouldSample(root.traceId);
72-
assert.ok(samplerShouldSampler ? samplerShouldSampler : true);
7370
});
71+
it('should sample an empty traceId', () => {
72+
const sampler = SamplerBuilder.getSampler(0.5);
73+
const samplerShouldSample = sampler.shouldSample(null);
74+
assert.ok(samplerShouldSample);
75+
});
76+
it('should accept and reject traces based on last 26 bytes of traceId',
77+
() => {
78+
const sampler = SamplerBuilder.getSampler(0.5);
79+
80+
const shouldSample = [
81+
'11111111111111111110000000000000',
82+
'1111111111111111111000ffffffffff',
83+
'11111111111111111117ffffffffffff',
84+
];
85+
shouldSample.forEach(traceId => {
86+
const samplerShouldSample = sampler.shouldSample(traceId);
87+
assert.ok(
88+
samplerShouldSample,
89+
`should have sampled but didn't: ${traceId}`);
90+
});
91+
92+
const shouldNotSample = [
93+
'11111111111111111118000000000000',
94+
'11111111111111111118000fffffffff',
95+
'1111111111111111111fffffffffffff',
96+
];
97+
shouldNotSample.forEach(traceId => {
98+
const samplerShouldSample = sampler.shouldSample(traceId);
99+
assert.ok(
100+
!samplerShouldSample,
101+
`should not have sampled but did: ${traceId}`);
102+
});
103+
});
74104
});
75105
});

packages/opencensus-exporter-zpages/test/test-zpages.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ const zpagesServerUrl = 'http://localhost:' + options.port;
3636

3737
/** Default config for traces tests */
3838
const defaultConfig: TracerConfig = {
39-
samplingRate: 0.2
39+
samplingRate: 1.0
4040
};
4141

4242
/**

0 commit comments

Comments
 (0)