Skip to content

Commit 596b2a2

Browse files
committed
feat: add room count metrics
1 parent 2c6da01 commit 596b2a2

4 files changed

Lines changed: 149 additions & 4 deletions

File tree

synapse/handlers/stats.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,12 @@
3232
)
3333

3434
from synapse.api.constants import EventContentFields, EventTypes, Membership
35-
from synapse.metrics import SERVER_NAME_LABEL, event_processing_positions
35+
from synapse.metrics import (
36+
SERVER_NAME_LABEL,
37+
event_processing_positions,
38+
known_rooms_gauge,
39+
locally_joined_rooms_gauge,
40+
)
3641
from synapse.metrics.background_process_metrics import run_as_background_process
3742
from synapse.storage.databases.main.state_deltas import StateDelta
3843
from synapse.types import JsonDict
@@ -153,6 +158,19 @@ async def _unsafe_process(self) -> None:
153158

154159
self.pos = max_pos
155160

161+
(
162+
known_room_count,
163+
locally_joined_room_count,
164+
) = await self.store.get_room_stats()
165+
166+
# Update room count metrics
167+
known_rooms_gauge.labels(**{SERVER_NAME_LABEL: self.server_name}).set(
168+
known_room_count
169+
)
170+
locally_joined_rooms_gauge.labels(
171+
**{SERVER_NAME_LABEL: self.server_name}
172+
).set(locally_joined_room_count)
173+
156174
async def _handle_deltas(
157175
self, deltas: Iterable[StateDelta]
158176
) -> Tuple[Dict[str, CounterType[str]], Dict[str, CounterType[str]]]:

synapse/metrics/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,19 @@ def collect(self) -> Iterable[Metric]:
645645
labelnames=["name", SERVER_NAME_LABEL],
646646
)
647647

648+
# Gauges for room counts
649+
known_rooms_gauge = Gauge(
650+
"synapse_known_rooms_total",
651+
"Total number of rooms",
652+
labelnames=[SERVER_NAME_LABEL],
653+
)
654+
655+
locally_joined_rooms_gauge = Gauge(
656+
"synapse_locally_joined_rooms_total",
657+
"Total number of locally joined rooms",
658+
labelnames=[SERVER_NAME_LABEL],
659+
)
660+
648661

649662
def register_threadpool(*, name: str, server_name: str, threadpool: ThreadPool) -> None:
650663
"""

synapse/storage/databases/main/stats.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,6 @@ async def bulk_update_stats_delta(
324324
) -> None:
325325
"""Bulk update stats tables for a given stream_id and updates the stats
326326
incremental position.
327-
328327
Args:
329328
ts: Current timestamp in ms
330329
updates: The updates to commit as a mapping of
@@ -358,6 +357,24 @@ def _bulk_update_stats_delta_txn(txn: LoggingTransaction) -> None:
358357
"bulk_update_stats_delta", _bulk_update_stats_delta_txn
359358
)
360359

360+
async def get_room_stats(self) -> Tuple[int, int]:
361+
"""
362+
Retrieve the total number of rooms and locally joined rooms.
363+
"""
364+
365+
def _get_room_stats_txn(txn: LoggingTransaction) -> Tuple[int, int]:
366+
sql = """
367+
SELECT
368+
count(*) AS total,
369+
count(CASE WHEN local_users_in_room > 0 THEN 1 END) AS locally_joined
370+
FROM room_stats_current;
371+
"""
372+
txn.execute(sql)
373+
row = cast(Tuple[int, int], txn.fetchone())
374+
return row[0], row[1]
375+
376+
return await self.db_pool.runInteraction("get_room_stats", _get_room_stats_txn)
377+
361378
async def update_stats_delta(
362379
self,
363380
ts: int,

tests/handlers/test_stats.py

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
from typing import Any, Dict, List, Optional, Tuple, cast
2222

23+
from prometheus_client import REGISTRY, Gauge
24+
2325
from twisted.internet.testing import MemoryReactor
2426

2527
from synapse.rest import admin
@@ -46,6 +48,21 @@ class StatsRoomTests(unittest.HomeserverTestCase):
4648
def prepare(self, reactor: MemoryReactor, clock: Clock, hs: HomeServer) -> None:
4749
self.store = hs.get_datastores().main
4850
self.handler = self.hs.get_stats_handler()
51+
self._set_metrics_to_zero()
52+
53+
def _set_metrics_to_zero(self) -> None:
54+
"""
55+
There is an issue when the tests are run in parallel where the metrics
56+
are not reset between tests, leading to incorrect values.
57+
This method resets the metrics to zero before each test to ensure
58+
that each test starts with a clean slate.
59+
"""
60+
metrics = ["synapse_known_rooms_total", "synapse_locally_joined_rooms_total"]
61+
for metric_name in metrics:
62+
gauge = REGISTRY._names_to_collectors.get(metric_name)
63+
if gauge is not None and isinstance(gauge, Gauge):
64+
for labels in gauge._metrics:
65+
gauge.labels(*labels).set(0)
4966

5067
def _add_background_updates(self) -> None:
5168
"""
@@ -162,9 +179,20 @@ def test_create_room(self) -> None:
162179
"""
163180
When we create a room, it should have statistics already ready.
164181
"""
165-
166182
self._perform_background_initial_update()
167-
183+
self.assertEqual(
184+
REGISTRY.get_sample_value(
185+
"synapse_known_rooms_total", labels={"server_name": self.hs.hostname}
186+
),
187+
0.0,
188+
)
189+
self.assertEqual(
190+
REGISTRY.get_sample_value(
191+
"synapse_locally_joined_rooms_total",
192+
labels={"server_name": self.hs.hostname},
193+
),
194+
0.0,
195+
)
168196
u1 = self.register_user("u1", "pass")
169197
u1token = self.login("u1", "pass")
170198
r1 = self.helper.create_room_as(u1, tok=u1token)
@@ -190,6 +218,21 @@ def test_create_room(self) -> None:
190218
self.assertEqual(r2stats["invited_members"], 0)
191219
self.assertEqual(r2stats["banned_members"], 0)
192220

221+
# There are 2 rooms created. Check the room metrics were udpated.
222+
self.assertEqual(
223+
REGISTRY.get_sample_value(
224+
"synapse_known_rooms_total", labels={"server_name": self.hs.hostname}
225+
),
226+
2,
227+
)
228+
self.assertEqual(
229+
REGISTRY.get_sample_value(
230+
"synapse_locally_joined_rooms_total",
231+
labels={"server_name": self.hs.hostname},
232+
),
233+
2,
234+
)
235+
193236
def test_updating_profile_information_does_not_increase_joined_members_count(
194237
self,
195238
) -> None:
@@ -592,3 +635,57 @@ def test_incomplete_stats(self) -> None:
592635

593636
self.assertEqual(u1stats_complete["joined_rooms"], 1)
594637
self.assertEqual(u2stats_complete["joined_rooms"], 1)
638+
639+
def test_room_metrics(self) -> None:
640+
"""
641+
Test that the `synapse_locally_joined_rooms_total` and
642+
`synapse_known_rooms_total` metrics are updated correctly.
643+
"""
644+
645+
self._perform_background_initial_update()
646+
self.assertEqual(
647+
REGISTRY.get_sample_value(
648+
"synapse_known_rooms_total", labels={"server_name": self.hs.hostname}
649+
),
650+
0.0,
651+
)
652+
self.assertEqual(
653+
REGISTRY.get_sample_value(
654+
"synapse_locally_joined_rooms_total",
655+
labels={"server_name": self.hs.hostname},
656+
),
657+
0.0,
658+
)
659+
660+
u1 = self.register_user("u1", "pass")
661+
u1token = self.login("u1", "pass")
662+
663+
# create 2 rooms, one with a local user and one without it.
664+
r1 = self.helper.create_room_as(u1, tok=u1token)
665+
r2 = self.helper.create_room_as(u1, tok=u1token)
666+
self.helper.leave(r2, u1, tok=u1token)
667+
668+
# Check the locally joined rooms metric after creating rooms
669+
self.assertEqual(
670+
REGISTRY.get_sample_value(
671+
"synapse_locally_joined_rooms_total",
672+
labels={"server_name": self.hs.hostname},
673+
),
674+
1,
675+
)
676+
self.assertEqual(
677+
REGISTRY.get_sample_value(
678+
"synapse_known_rooms_total", labels={"server_name": self.hs.hostname}
679+
),
680+
2,
681+
)
682+
683+
# Check the stats for both rooms
684+
r1stats = self._get_current_stats("room", r1)
685+
r2stats = self._get_current_stats("room", r2)
686+
687+
assert r1stats is not None
688+
assert r2stats is not None
689+
690+
self.assertEqual(r1stats["joined_members"], 1)
691+
self.assertEqual(r2stats["joined_members"], 0)

0 commit comments

Comments
 (0)