Skip to content

Commit 301e77b

Browse files
committed
fix(sdk-metrics-base): misbehaving aggregation temporality selector tolerance
1 parent 1ee1b28 commit 301e77b

3 files changed

Lines changed: 70 additions & 11 deletions

File tree

experimental/packages/opentelemetry-sdk-metrics-base/src/state/TemporalMetricProcessor.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ interface LastReportedHistory<T> {
3535
* The timestamp the data was reported.
3636
*/
3737
collectionTime: HrTime;
38+
/**
39+
* The AggregationTemporality used to aggregate reports.
40+
*/
41+
aggregationTemporality: AggregationTemporality;
3842
}
3943

4044
/**
@@ -69,19 +73,20 @@ export class TemporalMetricProcessor<T> {
6973
sdkStartTime: HrTime,
7074
collectionTime: HrTime,
7175
): Maybe<MetricData> {
72-
const aggregationTemporality = collector.selectAggregationTemporality(instrumentDescriptor.type);
7376
// In case it's our first collection, default to start timestamp (see below for explanation).
7477
let lastCollectionTime = sdkStartTime;
7578

7679
this._stashAccumulations(collectors, currentAccumulations);
7780
const unreportedAccumulations = this._getMergedUnreportedAccumulations(collector);
7881

7982
let result = unreportedAccumulations;
83+
let aggregationTemporality: AggregationTemporality;
8084
// Check our last report time.
8185
if (this._reportHistory.has(collector)) {
8286
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
8387
const last = this._reportHistory.get(collector)!;
8488
lastCollectionTime = last.collectionTime;
89+
aggregationTemporality = last.aggregationTemporality;
8590

8691
// Use aggregation temporality + instrument to determine if we do a merge or a diff of
8792
// previous. We have the following four scenarios:
@@ -95,12 +100,16 @@ export class TemporalMetricProcessor<T> {
95100
// for the next cumulative measurement.
96101
result = TemporalMetricProcessor.merge(last.accumulations, unreportedAccumulations, this._aggregator);
97102
}
103+
} else {
104+
// Call into user code to select aggregation temporality for the instrument.
105+
aggregationTemporality = collector.selectAggregationTemporality(instrumentDescriptor.type);
98106
}
99107

100108
// Update last reported (cumulative) accumulation.
101109
this._reportHistory.set(collector, {
102110
accumulations: result,
103111
collectionTime,
112+
aggregationTemporality,
104113
});
105114

106115
// Metric data time span is determined as:

experimental/packages/opentelemetry-sdk-metrics-base/test/state/TemporalMetricProcessor.test.ts

Lines changed: 55 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import * as api from '@opentelemetry/api';
1818
import { hrTime } from '@opentelemetry/core';
1919
import * as assert from 'assert';
20+
import * as sinon from 'sinon';
2021
import { SumAggregator } from '../../src/aggregator';
2122
import { AggregationTemporality } from '../../src/export/AggregationTemporality';
2223
import { DataPointType } from '../../src/export/MetricData';
@@ -40,11 +41,17 @@ const cumulativeCollector1: MetricCollectorHandle = {
4041
const sdkStartTime = hrTime();
4142

4243
describe('TemporalMetricProcessor', () => {
44+
afterEach(() => {
45+
sinon.restore();
46+
});
47+
4348
describe('buildMetrics', () => {
4449
describe('single delta collector', () => {
4550
const collectors = [ deltaCollector1 ];
4651

4752
it('should build metrics', () => {
53+
const spy = sinon.spy(deltaCollector1, 'selectAggregationTemporality');
54+
4855
const aggregator = new SumAggregator();
4956
const deltaMetricStorage = new DeltaMetricProcessor(aggregator);
5057
const temporalMetricStorage = new TemporalMetricProcessor(aggregator);
@@ -59,7 +66,10 @@ describe('TemporalMetricProcessor', () => {
5966
sdkStartTime,
6067
hrTime());
6168

62-
assertMetricData(metric, DataPointType.SINGULAR);
69+
assertMetricData(metric,
70+
DataPointType.SINGULAR,
71+
defaultInstrumentDescriptor,
72+
AggregationTemporality.DELTA);
6373
assert.strictEqual(metric.dataPoints.length, 1);
6474
assertDataPoint(metric.dataPoints[0], {}, 1);
6575
}
@@ -74,7 +84,10 @@ describe('TemporalMetricProcessor', () => {
7484
sdkStartTime,
7585
hrTime());
7686

77-
assertMetricData(metric, DataPointType.SINGULAR);
87+
assertMetricData(metric,
88+
DataPointType.SINGULAR,
89+
defaultInstrumentDescriptor,
90+
AggregationTemporality.DELTA);
7891
assert.strictEqual(metric.dataPoints.length, 1);
7992
assertDataPoint(metric.dataPoints[0], {}, 2);
8093
}
@@ -88,9 +101,15 @@ describe('TemporalMetricProcessor', () => {
88101
sdkStartTime,
89102
hrTime());
90103

91-
assertMetricData(metric, DataPointType.SINGULAR);
104+
assertMetricData(metric,
105+
DataPointType.SINGULAR,
106+
defaultInstrumentDescriptor,
107+
AggregationTemporality.DELTA);
92108
assert.strictEqual(metric.dataPoints.length, 0);
93109
}
110+
111+
// selectAggregationTemporality should be called only once.
112+
assert.strictEqual(spy.callCount, 1);
94113
});
95114
});
96115

@@ -112,7 +131,10 @@ describe('TemporalMetricProcessor', () => {
112131
sdkStartTime,
113132
hrTime());
114133

115-
assertMetricData(metric, DataPointType.SINGULAR);
134+
assertMetricData(metric,
135+
DataPointType.SINGULAR,
136+
defaultInstrumentDescriptor,
137+
AggregationTemporality.DELTA);
116138
assert.strictEqual(metric.dataPoints.length, 1);
117139
assertDataPoint(metric.dataPoints[0], {}, 1);
118140
}
@@ -126,7 +148,10 @@ describe('TemporalMetricProcessor', () => {
126148
sdkStartTime,
127149
hrTime());
128150

129-
assertMetricData(metric, DataPointType.SINGULAR);
151+
assertMetricData(metric,
152+
DataPointType.SINGULAR,
153+
defaultInstrumentDescriptor,
154+
AggregationTemporality.DELTA);
130155
assert.strictEqual(metric.dataPoints.length, 1);
131156
assertDataPoint(metric.dataPoints[0], {}, 1);
132157
}
@@ -136,6 +161,8 @@ describe('TemporalMetricProcessor', () => {
136161
describe('single cumulative collector', () => {
137162
const collectors = [ cumulativeCollector1 ];
138163
it('should build metrics', () => {
164+
const spy = sinon.spy(cumulativeCollector1, 'selectAggregationTemporality');
165+
139166
const aggregator = new SumAggregator();
140167
const deltaMetricStorage = new DeltaMetricProcessor(aggregator);
141168
const temporalMetricStorage = new TemporalMetricProcessor(aggregator);
@@ -150,7 +177,10 @@ describe('TemporalMetricProcessor', () => {
150177
sdkStartTime,
151178
hrTime());
152179

153-
assertMetricData(metric, DataPointType.SINGULAR);
180+
assertMetricData(metric,
181+
DataPointType.SINGULAR,
182+
defaultInstrumentDescriptor,
183+
AggregationTemporality.CUMULATIVE);
154184
assert.strictEqual(metric.dataPoints.length, 1);
155185
assertDataPoint(metric.dataPoints[0], {}, 1);
156186
}
@@ -165,10 +195,16 @@ describe('TemporalMetricProcessor', () => {
165195
sdkStartTime,
166196
hrTime());
167197

168-
assertMetricData(metric, DataPointType.SINGULAR);
198+
assertMetricData(metric,
199+
DataPointType.SINGULAR,
200+
defaultInstrumentDescriptor,
201+
AggregationTemporality.CUMULATIVE);
169202
assert.strictEqual(metric.dataPoints.length, 1);
170203
assertDataPoint(metric.dataPoints[0], {}, 3);
171204
}
205+
206+
// selectAggregationTemporality should be called only once.
207+
assert.strictEqual(spy.callCount, 1);
172208
});
173209
});
174210

@@ -189,7 +225,10 @@ describe('TemporalMetricProcessor', () => {
189225
sdkStartTime,
190226
hrTime());
191227

192-
assertMetricData(metric, DataPointType.SINGULAR);
228+
assertMetricData(metric,
229+
DataPointType.SINGULAR,
230+
defaultInstrumentDescriptor,
231+
AggregationTemporality.CUMULATIVE);
193232
assert.strictEqual(metric.dataPoints.length, 1);
194233
assertDataPoint(metric.dataPoints[0], {}, 1);
195234
}
@@ -204,7 +243,10 @@ describe('TemporalMetricProcessor', () => {
204243
sdkStartTime,
205244
hrTime());
206245

207-
assertMetricData(metric, DataPointType.SINGULAR);
246+
assertMetricData(metric,
247+
DataPointType.SINGULAR,
248+
defaultInstrumentDescriptor,
249+
AggregationTemporality.DELTA);
208250
assert.strictEqual(metric.dataPoints.length, 1);
209251
assertDataPoint(metric.dataPoints[0], {}, 3);
210252
}
@@ -217,7 +259,10 @@ describe('TemporalMetricProcessor', () => {
217259
sdkStartTime,
218260
hrTime());
219261

220-
assertMetricData(metric, DataPointType.SINGULAR);
262+
assertMetricData(metric,
263+
DataPointType.SINGULAR,
264+
defaultInstrumentDescriptor,
265+
AggregationTemporality.CUMULATIVE);
221266
assert.strictEqual(metric.dataPoints.length, 1);
222267
assertDataPoint(metric.dataPoints[0], {}, 3);
223268
}

experimental/packages/opentelemetry-sdk-metrics-base/test/util.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import { Measurement } from '../src/Measurement';
2929
import { isNotNullish } from '../src/utils';
3030
import { HrTime } from '@opentelemetry/api';
3131
import { Histogram } from '../src/aggregator/types';
32+
import { AggregationTemporality } from '../src/export/AggregationTemporality';
3233

3334
export const defaultResource = new Resource({
3435
resourceKey: 'my-resource',
@@ -71,6 +72,7 @@ export function assertMetricData(
7172
actual: unknown,
7273
dataPointType?: DataPointType,
7374
instrumentDescriptor: Partial<InstrumentDescriptor> | null = defaultInstrumentDescriptor,
75+
aggregationTemporality?: AggregationTemporality,
7476
): asserts actual is MetricData {
7577
const it = actual as MetricData;
7678
if (instrumentDescriptor != null) {
@@ -81,6 +83,9 @@ export function assertMetricData(
8183
} else {
8284
assert(isNotNullish(DataPointType[it.dataPointType]));
8385
}
86+
if (aggregationTemporality != null) {
87+
assert.strictEqual(aggregationTemporality, it.aggregationTemporality);
88+
}
8489
assert(Array.isArray(it.dataPoints));
8590
}
8691

0 commit comments

Comments
 (0)