Skip to content

Commit a8ddac5

Browse files
[plugin/prometheus] Fix duplicated metrics (#3251)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent d318fd9 commit a8ddac5

File tree

7 files changed

+333
-42
lines changed

7 files changed

+333
-42
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@graphql-yoga/plugin-prometheus': patch
3+
---
4+
dependencies updates:
5+
- Updated dependency [`@envelop/prometheus@^10.0.0`
6+
↗︎](https://www.npmjs.com/package/@envelop/prometheus/v/10.0.0) (from `^9.4.0`, in
7+
`dependencies`)

.changeset/big-students-grin.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
---
2+
'@graphql-yoga/plugin-prometheus': major
3+
---
4+
5+
Adds a cache for metrics definition (Summary, Histogram and Counter).
6+
7+
Fixes an issue preventing this plugin to be initialized multiple times, leading to metrics
8+
duplication error (https://github.com/ardatan/graphql-mesh/issues/6545).
9+
10+
## Behavior Breaking Change:
11+
12+
Due to Prometheus client API limitations, a metric is only defined once for a given registry. This
13+
means that if the configuration of the metrics, it will be silently ignored on plugin
14+
re-initialization.
15+
16+
This is to avoid potential loss of metrics data produced between the plugin re-initialization and
17+
the last pull by the prometheus agent.
18+
19+
If you need to be sure metrics configuration is up to date after a plugin re-initialization, you can
20+
either:
21+
22+
- restart the whole node process instead of just recreating a graphql server at runtime
23+
- clear the registry using `registry.clear()` before plugin re-initialization:
24+
```ts
25+
function usePrometheusWithReset() {
26+
registry.clear()
27+
return usePrometheus({ ... })
28+
}
29+
```
30+
- use a new registry for each plugin instance:
31+
```ts
32+
function usePrometheusWithRegistry() {
33+
const registry = new Registry()
34+
return usePrometheus({
35+
registry,
36+
...
37+
})
38+
}
39+
```
40+
41+
Keep in mind that this implies potential data loss in pull mode.
42+
43+
## API Breaking Change:
44+
45+
To ensure metrics from being registered multiple times on the same registry, the signature of
46+
`createHistogram`, `createSummary` and `createCounter` have been changed to now include the registry
47+
as a mandatory parameter.
48+
49+
If you were customizing metrics parameters, you will need to update the metric definitions
50+
51+
```diff
52+
usePrometheus({
53+
execute: createHistogram({
54+
+ registry: registry
55+
histogram: new Histogram({
56+
name: 'my_custom_name',
57+
help: 'HELP ME',
58+
labelNames: ['opText'] as const,
59+
- registers: [registry],
60+
}),
61+
fillLabelsFn: () => {}
62+
}),
63+
requestCount: createCounter({
64+
+ registry: registry
65+
histogram: new Histogram({
66+
name: 'my_custom_name',
67+
help: 'HELP ME',
68+
labelNames: ['opText'] as const,
69+
- registers: [registry],
70+
}),
71+
fillLabelsFn: () => {}
72+
}),
73+
requestSummary: createSummary({
74+
+ registry: registry
75+
histogram: new Histogram({
76+
name: 'my_custom_name',
77+
help: 'HELP ME',
78+
labelNames: ['opText'] as const,
79+
- registers: [registry],
80+
}),
81+
fillLabelsFn: () => {}
82+
}),
83+
})
84+
```
85+

packages/plugins/prometheus/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
"prom-client": "^15.0.0"
4343
},
4444
"dependencies": {
45-
"@envelop/prometheus": "^9.4.0"
45+
"@envelop/prometheus": "^10.0.0"
4646
},
4747
"devDependencies": {
4848
"prom-client": "15.0.0"

packages/plugins/prometheus/src/index.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
11
import { getOperationAST } from 'graphql';
22
import { Plugin } from 'graphql-yoga';
3-
import { register as defaultRegistry, Histogram } from 'prom-client';
3+
import { register as defaultRegistry } from 'prom-client';
44
import {
5+
CounterAndLabels,
56
createCounter,
67
createHistogram,
78
createSummary,
89
PrometheusTracingPluginConfig as EnvelopPrometheusTracingPluginConfig,
910
FillLabelsFnParams,
11+
HistogramAndLabels,
12+
SummaryAndLabels,
1013
usePrometheus as useEnvelopPrometheus,
1114
} from '@envelop/prometheus';
1215

13-
export { createCounter, createHistogram, createSummary, FillLabelsFnParams };
16+
export {
17+
CounterAndLabels,
18+
FillLabelsFnParams,
19+
HistogramAndLabels,
20+
SummaryAndLabels,
21+
createCounter,
22+
createHistogram,
23+
createSummary,
24+
};
1425

1526
export interface PrometheusTracingPluginConfig extends EnvelopPrometheusTracingPluginConfig {
1627
http?: boolean | string | ReturnType<typeof createHistogram>;
@@ -46,12 +57,12 @@ export function usePrometheus(options: PrometheusTracingPluginConfig): Plugin {
4657
typeof options.http === 'object'
4758
? options.http
4859
: createHistogram({
49-
histogram: new Histogram({
60+
registry,
61+
histogram: {
5062
name: typeof options.http === 'string' ? options.http : 'graphql_yoga_http_duration',
5163
help: 'Time spent on HTTP connection',
5264
labelNames,
53-
registers: [registry],
54-
}),
65+
},
5566
fillLabelsFn(params, { request, response }) {
5667
const labels: Record<string, string> = {
5768
method: request.method,

packages/plugins/prometheus/tests/prometheus.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,4 +122,15 @@ describe('Prometheus', () => {
122122
expect(metrics).toContain('method="POST"');
123123
expect(metrics).toContain('statusCode="200"');
124124
});
125+
126+
it('should be able to register the same histogram for multiple different registries', async () => {
127+
usePrometheus({
128+
http: true,
129+
registry,
130+
});
131+
usePrometheus({
132+
http: true,
133+
registry,
134+
});
135+
});
125136
});

0 commit comments

Comments
 (0)