Skip to content

Commit 8b36740

Browse files
authored
Fix InFlightGauge typing to allow upgrading to prometheus_client 0.24 (#19379)
Fixes #19375 `prometheus_client` 0.24 makes `Collector` a generic type. Previously, `InFlightGauge` inherited from both `Generic[MetricsEntry]` and `Collector`, resulting in the error `TypeError: cannot create a consistent MRO` when using `prometheus_client` >= 0.24. This behaviour of disallowing multiple `Generic` inheritance is more strictly enforced starting with python 3.14, but can still lead to issues with earlier versions of python. This PR separates runtime and typing inheritance for `InFlightGauge`: - Runtime: `InFlightGauge` inherits only from `Collector` - Typing: `InFlightGauge` is generic This preserves static typing, avoids MRO conflicts, and supports both `prometheus_client` <0.24 and >=0.24. I have tested these changes out locally with `prometheus_client` 0.23.1 & 0.24 on python 3.14 while sending a bunch of messages over federation and watching a grafana dashboard configured to show `synapse_util_metrics_block_in_flight_total` & `synapse_util_metrics_block_in_flight_real_time_sum` (the only metric setup to use `InFlightGauge`) and things are working in each case. https://github.com/element-hq/synapse/blob/a1e9abc7df3e6c43a95cba059348546a4c9d4491/synapse/util/metrics.py#L112-L119 ### Pull Request Checklist <!-- Please read https://element-hq.github.io/synapse/latest/development/contributing_guide.html before submitting your pull request --> * [X] Pull request is based on the develop branch * [X] Pull request includes a [changelog file](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#changelog). The entry should: - Be a short description of your change which makes sense to users. "Fixed a bug that prevented receiving messages from other servers." instead of "Moved X method from `EventStore` to `EventWorkerStore`.". - Use markdown where necessary, mostly for `code blocks`. - End with either a period (.) or an exclamation mark (!). - Start with a capital letter. - Feel free to credit yourself, by adding a sentence "Contributed by @github_username." or "Contributed by [Your Name]." to the end of the entry. * [X] [Code style](https://element-hq.github.io/synapse/latest/code_style.html) is correct (run the [linters](https://element-hq.github.io/synapse/latest/development/contributing_guide.html#run-the-linters))
1 parent cb376ee commit 8b36740

File tree

5 files changed

+44
-6
lines changed

5 files changed

+44
-6
lines changed

changelog.d/19379.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix `InFlightGauge` typing to allow upgrading to `prometheus_client` 0.24.

scripts-dev/mypy_synapse_plugin.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ class ArgLocation:
133133
"prometheus_client.metrics.Info": ArgLocation("labelnames", 2),
134134
"prometheus_client.metrics.Enum": ArgLocation("labelnames", 2),
135135
"synapse.metrics.LaterGauge": ArgLocation("labelnames", 2),
136+
"synapse.metrics._InFlightGaugeRuntime": ArgLocation("labels", 2),
136137
"synapse.metrics.InFlightGauge": ArgLocation("labels", 2),
137138
"synapse.metrics.GaugeBucketCollector": ArgLocation("labelnames", 2),
138139
"prometheus_client.registry.Collector": None,

synapse/metrics/__init__.py

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import threading
2828
from importlib import metadata
2929
from typing import (
30+
TYPE_CHECKING,
31+
Any,
3032
Callable,
3133
Generic,
3234
Iterable,
@@ -262,8 +264,12 @@ def __attrs_post_init__(self) -> None:
262264
MetricsEntry = TypeVar("MetricsEntry")
263265

264266

265-
class InFlightGauge(Generic[MetricsEntry], Collector):
266-
"""Tracks number of things (e.g. requests, Measure blocks, etc) in flight
267+
class _InFlightGaugeRuntime(Collector):
268+
"""
269+
Runtime class for InFlightGauge. Contains all actual logic.
270+
Does not inherit from Generic to avoid method resolution order (MRO) conflicts.
271+
272+
Tracks number of things (e.g. requests, Measure blocks, etc) in flight
267273
at any given time.
268274
269275
Each InFlightGauge will create a metric called `<name>_total` that counts
@@ -292,16 +298,20 @@ def __init__(
292298

293299
# Create a class which have the sub_metrics values as attributes, which
294300
# default to 0 on initialization. Used to pass to registered callbacks.
295-
self._metrics_class: type[MetricsEntry] = attr.make_class(
301+
self._metrics_class = attr.make_class(
296302
"_MetricsEntry",
297303
attrs={x: attr.ib(default=0) for x in sub_metrics},
298304
slots=True,
299305
)
300306

301307
# Counts number of in flight blocks for a given set of label values
302-
self._registrations: dict[
303-
tuple[str, ...], set[Callable[[MetricsEntry], None]]
304-
] = {}
308+
# `Callable` should be of type `Callable[[MetricsEntry], None]`, but
309+
# `MetricsEntry` has no meaning in this context without the higher level
310+
# `InFlightGauge` typing information.
311+
# Instead, the typing is enforced by having `_registrations` be private and all
312+
# accessor functions have proper `Callable[[MetricsEntry], None]` type
313+
# annotations.
314+
self._registrations: dict[tuple[str, ...], set[Callable[[Any], None]]] = {}
305315

306316
# Protects access to _registrations
307317
self._lock = threading.Lock()
@@ -398,6 +408,17 @@ def collect(self) -> Iterable[Metric]:
398408
yield gauge
399409

400410

411+
if TYPE_CHECKING:
412+
413+
class InFlightGauge(_InFlightGaugeRuntime, Generic[MetricsEntry]):
414+
"""
415+
Typing-only generic wrapper.
416+
Provides InFlightGauge[T] support to type checkers.
417+
"""
418+
else:
419+
InFlightGauge = _InFlightGaugeRuntime
420+
421+
401422
class GaugeHistogramMetricFamilyWithLabels(GaugeHistogramMetricFamily):
402423
"""
403424
Custom version of `GaugeHistogramMetricFamily` from `prometheus_client` that allows

synapse/util/metrics.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,13 @@
1919
#
2020
#
2121

22+
# These imports are necessary for python <= 3.13 in order for the `InFlightGauge` type
23+
# annotations not to be evaluated at runtime.
24+
# Starting with python 3.14, annotations are lazily evaluated by default, which is the
25+
# behaviour we desire.
26+
# More info here: https://docs.python.org/3/reference/compound_stmts.html#annotations
27+
from __future__ import annotations
28+
2229
import logging
2330
from functools import wraps
2431
from types import TracebackType

tests/metrics/test_metrics.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@
1818
# [This file includes modifications made by New Vector Limited]
1919
#
2020
#
21+
22+
# These imports are necessary for python <= 3.13 in order for the `InFlightGauge` type
23+
# annotations not to be evaluated at runtime.
24+
# Starting with python 3.14, annotations are lazily evaluated by default, which is the
25+
# behaviour we desire.
26+
# More info here: https://docs.python.org/3/reference/compound_stmts.html#annotations
27+
from __future__ import annotations
28+
2129
from typing import NoReturn, Protocol
2230

2331
from prometheus_client.core import Sample

0 commit comments

Comments
 (0)