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.
a1e9abc7df/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))
This commit is contained in:
1
changelog.d/19379.bugfix
Normal file
1
changelog.d/19379.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
Fix `InFlightGauge` typing to allow upgrading to `prometheus_client` 0.24.
|
||||
@@ -133,6 +133,7 @@ prometheus_metric_fullname_to_label_arg_map: Mapping[str, ArgLocation | None] =
|
||||
"prometheus_client.metrics.Info": ArgLocation("labelnames", 2),
|
||||
"prometheus_client.metrics.Enum": ArgLocation("labelnames", 2),
|
||||
"synapse.metrics.LaterGauge": ArgLocation("labelnames", 2),
|
||||
"synapse.metrics._InFlightGaugeRuntime": ArgLocation("labels", 2),
|
||||
"synapse.metrics.InFlightGauge": ArgLocation("labels", 2),
|
||||
"synapse.metrics.GaugeBucketCollector": ArgLocation("labelnames", 2),
|
||||
"prometheus_client.registry.Collector": None,
|
||||
|
||||
@@ -27,6 +27,8 @@ import platform
|
||||
import threading
|
||||
from importlib import metadata
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
Callable,
|
||||
Generic,
|
||||
Iterable,
|
||||
@@ -262,8 +264,12 @@ shutdown.
|
||||
MetricsEntry = TypeVar("MetricsEntry")
|
||||
|
||||
|
||||
class InFlightGauge(Generic[MetricsEntry], Collector):
|
||||
"""Tracks number of things (e.g. requests, Measure blocks, etc) in flight
|
||||
class _InFlightGaugeRuntime(Collector):
|
||||
"""
|
||||
Runtime class for InFlightGauge. Contains all actual logic.
|
||||
Does not inherit from Generic to avoid method resolution order (MRO) conflicts.
|
||||
|
||||
Tracks number of things (e.g. requests, Measure blocks, etc) in flight
|
||||
at any given time.
|
||||
|
||||
Each InFlightGauge will create a metric called `<name>_total` that counts
|
||||
@@ -292,16 +298,20 @@ class InFlightGauge(Generic[MetricsEntry], Collector):
|
||||
|
||||
# Create a class which have the sub_metrics values as attributes, which
|
||||
# default to 0 on initialization. Used to pass to registered callbacks.
|
||||
self._metrics_class: type[MetricsEntry] = attr.make_class(
|
||||
self._metrics_class = attr.make_class(
|
||||
"_MetricsEntry",
|
||||
attrs={x: attr.ib(default=0) for x in sub_metrics},
|
||||
slots=True,
|
||||
)
|
||||
|
||||
# Counts number of in flight blocks for a given set of label values
|
||||
self._registrations: dict[
|
||||
tuple[str, ...], set[Callable[[MetricsEntry], None]]
|
||||
] = {}
|
||||
# `Callable` should be of type `Callable[[MetricsEntry], None]`, but
|
||||
# `MetricsEntry` has no meaning in this context without the higher level
|
||||
# `InFlightGauge` typing information.
|
||||
# Instead, the typing is enforced by having `_registrations` be private and all
|
||||
# accessor functions have proper `Callable[[MetricsEntry], None]` type
|
||||
# annotations.
|
||||
self._registrations: dict[tuple[str, ...], set[Callable[[Any], None]]] = {}
|
||||
|
||||
# Protects access to _registrations
|
||||
self._lock = threading.Lock()
|
||||
@@ -398,6 +408,17 @@ class InFlightGauge(Generic[MetricsEntry], Collector):
|
||||
yield gauge
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
class InFlightGauge(_InFlightGaugeRuntime, Generic[MetricsEntry]):
|
||||
"""
|
||||
Typing-only generic wrapper.
|
||||
Provides InFlightGauge[T] support to type checkers.
|
||||
"""
|
||||
else:
|
||||
InFlightGauge = _InFlightGaugeRuntime
|
||||
|
||||
|
||||
class GaugeHistogramMetricFamilyWithLabels(GaugeHistogramMetricFamily):
|
||||
"""
|
||||
Custom version of `GaugeHistogramMetricFamily` from `prometheus_client` that allows
|
||||
|
||||
@@ -19,6 +19,13 @@
|
||||
#
|
||||
#
|
||||
|
||||
# These imports are necessary for python <= 3.13 in order for the `InFlightGauge` type
|
||||
# annotations not to be evaluated at runtime.
|
||||
# Starting with python 3.14, annotations are lazily evaluated by default, which is the
|
||||
# behaviour we desire.
|
||||
# More info here: https://docs.python.org/3/reference/compound_stmts.html#annotations
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from functools import wraps
|
||||
from types import TracebackType
|
||||
|
||||
@@ -18,6 +18,14 @@
|
||||
# [This file includes modifications made by New Vector Limited]
|
||||
#
|
||||
#
|
||||
|
||||
# These imports are necessary for python <= 3.13 in order for the `InFlightGauge` type
|
||||
# annotations not to be evaluated at runtime.
|
||||
# Starting with python 3.14, annotations are lazily evaluated by default, which is the
|
||||
# behaviour we desire.
|
||||
# More info here: https://docs.python.org/3/reference/compound_stmts.html#annotations
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import NoReturn, Protocol
|
||||
|
||||
from prometheus_client.core import Sample
|
||||
|
||||
Reference in New Issue
Block a user