Skip to content

Commit d91ae66

Browse files
authored
fix: metrics should persist beyond node restarts (#3159)
Partial revert of #3154 - metrics should persist when a node is stopped and then started again and re-registering the same metric should be a no-op.
1 parent 3528df8 commit d91ae66

File tree

15 files changed

+129
-342
lines changed

15 files changed

+129
-342
lines changed

packages/metrics-opentelemetry/src/index.ts

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ class OpenTelemetryMetrics implements Metrics {
118118
}
119119

120120
// reset counts for next time
121-
this.transferStats = new Map()
121+
this.transferStats.clear()
122122

123123
return output
124124
}
@@ -139,8 +139,6 @@ class OpenTelemetryMetrics implements Metrics {
139139

140140
stop (): void {
141141
this.transferStats.clear()
142-
this.metrics.clear()
143-
this.observables.clear()
144142
}
145143

146144
/**
@@ -195,7 +193,13 @@ class OpenTelemetryMetrics implements Metrics {
195193
}
196194

197195
if (isCalculatedMetricOptions<CalculatedMetricOptions>(opts)) {
198-
const gauge = this.observables.get(name) ?? this.meter.createObservableGauge(name, {
196+
let gauge = this.observables.get(name)
197+
198+
if (gauge != null) {
199+
return
200+
}
201+
202+
gauge = this.meter.createObservableGauge(name, {
199203
description: opts?.help ?? name
200204
})
201205

@@ -232,7 +236,13 @@ class OpenTelemetryMetrics implements Metrics {
232236
const label = opts?.label ?? name
233237

234238
if (isCalculatedMetricOptions<CalculatedMetricOptions<Record<string, number>>>(opts)) {
235-
const gauge = this.observables.get(name) ?? this.meter.createObservableGauge(name, {
239+
let gauge = this.observables.get(name)
240+
241+
if (gauge != null) {
242+
return
243+
}
244+
245+
gauge = this.meter.createObservableGauge(name, {
236246
description: opts?.help ?? name
237247
})
238248

@@ -273,7 +283,13 @@ class OpenTelemetryMetrics implements Metrics {
273283
}
274284

275285
if (isCalculatedMetricOptions<CalculatedMetricOptions>(opts)) {
276-
const counter = this.observables.get(name) ?? this.meter.createObservableCounter(name, {
286+
let counter = this.observables.get(name)
287+
288+
if (counter != null) {
289+
return
290+
}
291+
292+
counter = this.meter.createObservableCounter(name, {
277293
description: opts?.help ?? name
278294
})
279295

@@ -310,7 +326,13 @@ class OpenTelemetryMetrics implements Metrics {
310326
const label = opts?.label ?? name
311327

312328
if (isCalculatedMetricOptions<CalculatedMetricOptions<Record<string, number>>>(opts)) {
313-
const counter = this.observables.get(name) ?? this.meter.createObservableCounter(name, {
329+
let counter = this.observables.get(name)
330+
331+
if (counter != null) {
332+
return
333+
}
334+
335+
counter = this.meter.createObservableCounter(name, {
314336
description: opts?.help ?? name
315337
})
316338

packages/metrics-opentelemetry/test/index.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ describe('opentelemetry-metrics', () => {
2727
expect(wrapped).to.not.equal(target.wrapped)
2828
})
2929

30-
it('should not retain metrics after stop', async () => {
30+
it('should retain metrics after stop', async () => {
3131
const metrics = openTelemetryMetrics()({
3232
nodeInfo: {
3333
name: 'test',
@@ -50,6 +50,6 @@ describe('opentelemetry-metrics', () => {
5050

5151
const m3 = metrics.registerCounterGroup('test_metric')
5252

53-
expect(m3).to.not.equal(m1, 're-used metric')
53+
expect(m3).to.equal(m1, 'did not re-use metric')
5454
})
5555
})

packages/metrics-prometheus/src/counter-group.ts

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,27 @@
11
import { Counter as PromCounter } from 'prom-client'
22
import { normalizeString } from './utils.js'
33
import type { PrometheusCalculatedMetricOptions } from './index.js'
4-
import type { CalculatedMetric } from './utils.js'
5-
import type { CounterGroup, CalculateMetric } from '@libp2p/interface'
4+
import type { CounterGroup } from '@libp2p/interface'
65
import type { CollectFunction } from 'prom-client'
76

8-
export class PrometheusCounterGroup implements CounterGroup, CalculatedMetric<Record<string, number>> {
7+
export class PrometheusCounterGroup implements CounterGroup {
98
private readonly counter: PromCounter
109
private readonly label: string
11-
private readonly calculators: Array<CalculateMetric<Record<string, number>>>
1210

1311
constructor (name: string, opts: PrometheusCalculatedMetricOptions<Record<string, number>>) {
1412
name = normalizeString(name)
1513
const help = normalizeString(opts.help ?? name)
1614
const label = this.label = normalizeString(opts.label ?? name)
1715
let collect: CollectFunction<PromCounter<any>> | undefined
18-
this.calculators = []
1916

2017
// calculated metric
2118
if (opts?.calculate != null) {
22-
this.calculators.push(opts.calculate)
23-
const self = this
24-
2519
collect = async function () {
26-
await Promise.all(self.calculators.map(async calculate => {
27-
const values = await calculate()
20+
const values = await opts.calculate()
2821

29-
Object.entries(values).forEach(([key, value]) => {
30-
this.inc({ [label]: key }, value)
31-
})
32-
}))
22+
Object.entries(values).forEach(([key, value]) => {
23+
this.inc({ [label]: key }, value)
24+
})
3325
}
3426
}
3527

@@ -42,10 +34,6 @@ export class PrometheusCounterGroup implements CounterGroup, CalculatedMetric<Re
4234
})
4335
}
4436

45-
addCalculator (calculator: CalculateMetric<Record<string, number>>): void {
46-
this.calculators.push(calculator)
47-
}
48-
4937
increment (values: Record<string, number | unknown>): void {
5038
Object.entries(values).forEach(([key, value]) => {
5139
const inc = typeof value === 'number' ? value : 1

packages/metrics-prometheus/src/counter.ts

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,22 @@
11
import { Counter as PromCounter } from 'prom-client'
22
import { normalizeString } from './utils.js'
33
import type { PrometheusCalculatedMetricOptions } from './index.js'
4-
import type { CalculatedMetric } from './utils.js'
5-
import type { CalculateMetric, Counter } from '@libp2p/interface'
4+
import type { Counter } from '@libp2p/interface'
65
import type { CollectFunction } from 'prom-client'
76

8-
export class PrometheusCounter implements Counter, CalculatedMetric {
7+
export class PrometheusCounter implements Counter {
98
private readonly counter: PromCounter
10-
private readonly calculators: CalculateMetric[]
119

1210
constructor (name: string, opts: PrometheusCalculatedMetricOptions) {
1311
name = normalizeString(name)
1412
const help = normalizeString(opts.help ?? name)
1513
const labels = opts.label != null ? [normalizeString(opts.label)] : []
1614
let collect: CollectFunction<PromCounter<any>> | undefined
17-
this.calculators = []
1815

1916
// calculated metric
2017
if (opts?.calculate != null) {
21-
this.calculators.push(opts.calculate)
22-
const self = this
23-
2418
collect = async function () {
25-
const values = await Promise.all(self.calculators.map(async calculate => calculate()))
26-
const sum = values.reduce((acc, curr) => acc + curr, 0)
27-
28-
this.inc(sum)
19+
this.inc(await opts.calculate())
2920
}
3021
}
3122

@@ -38,10 +29,6 @@ export class PrometheusCounter implements Counter, CalculatedMetric {
3829
})
3930
}
4031

41-
addCalculator (calculator: CalculateMetric): void {
42-
this.calculators.push(calculator)
43-
}
44-
4532
increment (value: number = 1): void {
4633
this.counter.inc(value)
4734
}

packages/metrics-prometheus/src/histogram-group.ts

Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,27 @@
11
import { Histogram as PromHistogram } from 'prom-client'
22
import { normalizeString } from './utils.js'
33
import type { PrometheusCalculatedHistogramOptions } from './index.js'
4-
import type { CalculatedMetric } from './utils.js'
5-
import type { CalculateMetric, HistogramGroup, StopTimer } from '@libp2p/interface'
4+
import type { HistogramGroup, StopTimer } from '@libp2p/interface'
65
import type { CollectFunction } from 'prom-client'
76

8-
export class PrometheusHistogramGroup implements HistogramGroup, CalculatedMetric<Record<string, number>> {
7+
export class PrometheusHistogramGroup implements HistogramGroup {
98
private readonly histogram: PromHistogram
109
private readonly label: string
11-
private readonly calculators: Array<CalculateMetric<Record<string, number>>>
1210

1311
constructor (name: string, opts: PrometheusCalculatedHistogramOptions<Record<string, number>>) {
1412
name = normalizeString(name)
1513
const help = normalizeString(opts.help ?? name)
1614
const label = this.label = normalizeString(opts.label ?? name)
1715
let collect: CollectFunction<PromHistogram<any>> | undefined
18-
this.calculators = []
1916

2017
// calculated metric
2118
if (opts?.calculate != null) {
22-
this.calculators.push(opts.calculate)
23-
const self = this
24-
2519
collect = async function () {
26-
await Promise.all(self.calculators.map(async calculate => {
27-
const values = await calculate()
20+
const values = await opts.calculate()
2821

29-
Object.entries(values).forEach(([key, value]) => {
30-
this.observe({ [label]: key }, value)
31-
})
32-
}))
22+
Object.entries(values).forEach(([key, value]) => {
23+
this.observe({ [label]: key }, value)
24+
})
3325
}
3426
}
3527

@@ -43,10 +35,6 @@ export class PrometheusHistogramGroup implements HistogramGroup, CalculatedMetri
4335
})
4436
}
4537

46-
addCalculator (calculator: CalculateMetric<Record<string, number>>): void {
47-
this.calculators.push(calculator)
48-
}
49-
5038
observe (values: Record<string, number>): void {
5139
Object.entries(values).forEach(([key, value]) => {
5240
this.histogram.observe({ [this.label]: key }, value)

packages/metrics-prometheus/src/histogram.ts

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,22 @@
11
import { Histogram as PromHistogram } from 'prom-client'
22
import { normalizeString } from './utils.js'
33
import type { PrometheusCalculatedHistogramOptions } from './index.js'
4-
import type { StopTimer, CalculateMetric, Histogram } from '@libp2p/interface'
4+
import type { StopTimer, Histogram } from '@libp2p/interface'
55
import type { CollectFunction } from 'prom-client'
66

77
export class PrometheusHistogram implements Histogram {
88
private readonly histogram: PromHistogram
9-
private readonly calculators: CalculateMetric[]
109

1110
constructor (name: string, opts: PrometheusCalculatedHistogramOptions) {
1211
name = normalizeString(name)
1312
const help = normalizeString(opts.help ?? name)
1413
const labels = opts.label != null ? [normalizeString(opts.label)] : []
1514
let collect: CollectFunction<PromHistogram<any>> | undefined
16-
this.calculators = []
1715

1816
// calculated metric
1917
if (opts?.calculate != null) {
20-
this.calculators.push(opts.calculate)
21-
const self = this
22-
2318
collect = async function () {
24-
const values = await Promise.all(self.calculators.map(async calculate => calculate()))
25-
for (const value of values) {
26-
this.observe(value)
27-
}
19+
this.observe(await opts.calculate())
2820
}
2921
}
3022

@@ -38,10 +30,6 @@ export class PrometheusHistogram implements Histogram {
3830
})
3931
}
4032

41-
addCalculator (calculator: CalculateMetric): void {
42-
this.calculators.push(calculator)
43-
}
44-
4533
observe (value: number): void {
4634
this.histogram.observe(value)
4735
}

0 commit comments

Comments
 (0)