123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208 |
- # Copyright 2018 New Vector Ltd
- # Copyright 2019 Matrix.org Foundation C.I.C.
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- from importlib import metadata
- from typing import Dict, Tuple
- from unittest.mock import patch
- from pkg_resources import parse_version
- from prometheus_client.core import Sample
- from typing_extensions import Protocol
- from synapse.app._base import _set_prometheus_client_use_created_metrics
- from synapse.metrics import REGISTRY, InFlightGauge, generate_latest
- from synapse.util.caches.deferred_cache import DeferredCache
- from tests import unittest
- def get_sample_labels_value(sample: Sample) -> Tuple[Dict[str, str], float]:
- """Extract the labels and values of a sample.
- prometheus_client 0.5 changed the sample type to a named tuple with more
- members than the plain tuple had in 0.4 and earlier. This function can
- extract the labels and value from the sample for both sample types.
- Args:
- sample: The sample to get the labels and value from.
- Returns:
- A tuple of (labels, value) from the sample.
- """
- # If the sample has a labels and value attribute, use those.
- if hasattr(sample, "labels") and hasattr(sample, "value"):
- return sample.labels, sample.value
- # Otherwise fall back to treating it as a plain 3 tuple.
- else:
- # In older versions of prometheus_client Sample was a 3-tuple.
- labels: Dict[str, str]
- value: float
- _, labels, value = sample # type: ignore[misc]
- return labels, value
- class TestMauLimit(unittest.TestCase):
- def test_basic(self) -> None:
- class MetricEntry(Protocol):
- foo: int
- bar: int
- gauge: InFlightGauge[MetricEntry] = InFlightGauge(
- "test1", "", labels=["test_label"], sub_metrics=["foo", "bar"]
- )
- def handle1(metrics: MetricEntry) -> None:
- metrics.foo += 2
- metrics.bar = max(metrics.bar, 5)
- def handle2(metrics: MetricEntry) -> None:
- metrics.foo += 3
- metrics.bar = max(metrics.bar, 7)
- gauge.register(("key1",), handle1)
- self.assert_dict(
- {
- "test1_total": {("key1",): 1},
- "test1_foo": {("key1",): 2},
- "test1_bar": {("key1",): 5},
- },
- self.get_metrics_from_gauge(gauge),
- )
- gauge.unregister(("key1",), handle1)
- self.assert_dict(
- {
- "test1_total": {("key1",): 0},
- "test1_foo": {("key1",): 0},
- "test1_bar": {("key1",): 0},
- },
- self.get_metrics_from_gauge(gauge),
- )
- gauge.register(("key1",), handle1)
- gauge.register(("key2",), handle2)
- self.assert_dict(
- {
- "test1_total": {("key1",): 1, ("key2",): 1},
- "test1_foo": {("key1",): 2, ("key2",): 3},
- "test1_bar": {("key1",): 5, ("key2",): 7},
- },
- self.get_metrics_from_gauge(gauge),
- )
- gauge.unregister(("key2",), handle2)
- gauge.register(("key1",), handle2)
- self.assert_dict(
- {
- "test1_total": {("key1",): 2, ("key2",): 0},
- "test1_foo": {("key1",): 5, ("key2",): 0},
- "test1_bar": {("key1",): 7, ("key2",): 0},
- },
- self.get_metrics_from_gauge(gauge),
- )
- def get_metrics_from_gauge(
- self, gauge: InFlightGauge
- ) -> Dict[str, Dict[Tuple[str, ...], float]]:
- results = {}
- for r in gauge.collect():
- results[r.name] = {
- tuple(labels[x] for x in gauge.labels): value
- for labels, value in map(get_sample_labels_value, r.samples)
- }
- return results
- class BuildInfoTests(unittest.TestCase):
- def test_get_build(self) -> None:
- """
- The synapse_build_info metric reports the OS version, Python version,
- and Synapse version.
- """
- items = list(
- filter(
- lambda x: b"synapse_build_info{" in x,
- generate_latest(REGISTRY).split(b"\n"),
- )
- )
- self.assertEqual(len(items), 1)
- self.assertTrue(b"osversion=" in items[0])
- self.assertTrue(b"pythonversion=" in items[0])
- self.assertTrue(b"version=" in items[0])
- class CacheMetricsTests(unittest.HomeserverTestCase):
- def test_cache_metric(self) -> None:
- """
- Caches produce metrics reflecting their state when scraped.
- """
- CACHE_NAME = "cache_metrics_test_fgjkbdfg"
- cache: DeferredCache[str, str] = DeferredCache(CACHE_NAME, max_entries=777)
- items = {
- x.split(b"{")[0].decode("ascii"): x.split(b" ")[1].decode("ascii")
- for x in filter(
- lambda x: b"cache_metrics_test_fgjkbdfg" in x,
- generate_latest(REGISTRY).split(b"\n"),
- )
- }
- self.assertEqual(items["synapse_util_caches_cache_size"], "0.0")
- self.assertEqual(items["synapse_util_caches_cache_max_size"], "777.0")
- cache.prefill("1", "hi")
- items = {
- x.split(b"{")[0].decode("ascii"): x.split(b" ")[1].decode("ascii")
- for x in filter(
- lambda x: b"cache_metrics_test_fgjkbdfg" in x,
- generate_latest(REGISTRY).split(b"\n"),
- )
- }
- self.assertEqual(items["synapse_util_caches_cache_size"], "1.0")
- self.assertEqual(items["synapse_util_caches_cache_max_size"], "777.0")
- class PrometheusMetricsHackTestCase(unittest.HomeserverTestCase):
- if parse_version(metadata.version("prometheus_client")) < parse_version("0.14.0"):
- skip = "prometheus-client too old"
- def test_created_metrics_disabled(self) -> None:
- """
- Tests that a brittle hack, to disable `_created` metrics, works.
- This involves poking at the internals of prometheus-client.
- It's not the end of the world if this doesn't work.
- This test gives us a way to notice if prometheus-client changes
- their internals.
- """
- import prometheus_client.metrics
- PRIVATE_FLAG_NAME = "_use_created"
- # By default, the pesky `_created` metrics are enabled.
- # Check this assumption is still valid.
- self.assertTrue(getattr(prometheus_client.metrics, PRIVATE_FLAG_NAME))
- with patch("prometheus_client.metrics") as mock:
- setattr(mock, PRIVATE_FLAG_NAME, True)
- _set_prometheus_client_use_created_metrics(False)
- self.assertFalse(getattr(mock, PRIVATE_FLAG_NAME, False))
|