Skip to content
This repository was archived by the owner on Apr 26, 2024. It is now read-only.

Commit 55d5b3a

Browse files
authored
Servers-known-about statistic (#5981)
1 parent 78801e7 commit 55d5b3a

File tree

7 files changed

+226
-60
lines changed

7 files changed

+226
-60
lines changed

changelog.d/5981.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Setting metrics_flags.known_servers to True in the configuration will publish the synapse_federation_known_servers metric over Prometheus. This represents the total number of servers your server knows about (i.e. is in rooms with), including itself.

docs/sample_config.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,16 @@ account_threepid_delegates:
958958
#sentry:
959959
# dsn: "..."
960960

961+
# Flags to enable Prometheus metrics which are not suitable to be
962+
# enabled by default, either for performance reasons or limited use.
963+
#
964+
metrics_flags:
965+
# Publish synapse_federation_known_servers, a g auge of the number of
966+
# servers this homeserver knows about, including itself. May cause
967+
# performance problems on large homeservers.
968+
#
969+
#known_servers: true
970+
961971
# Whether or not to report anonymized homeserver usage statistics.
962972
# report_stats: true|false
963973

synapse/config/metrics.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# -*- coding: utf-8 -*-
22
# Copyright 2015, 2016 OpenMarket Ltd
3+
# Copyright 2019 The Matrix.org Foundation C.I.C.
34
#
45
# Licensed under the Apache License, Version 2.0 (the "License");
56
# you may not use this file except in compliance with the License.
@@ -13,20 +14,40 @@
1314
# See the License for the specific language governing permissions and
1415
# limitations under the License.
1516

17+
import attr
18+
1619
from ._base import Config, ConfigError
1720

1821
MISSING_SENTRY = """Missing sentry-sdk library. This is required to enable sentry
1922
integration.
2023
"""
2124

2225

26+
@attr.s
27+
class MetricsFlags(object):
28+
known_servers = attr.ib(default=False, validator=attr.validators.instance_of(bool))
29+
30+
@classmethod
31+
def all_off(cls):
32+
"""
33+
Instantiate the flags with all options set to off.
34+
"""
35+
return cls(**{x.name: False for x in attr.fields(cls)})
36+
37+
2338
class MetricsConfig(Config):
2439
def read_config(self, config, **kwargs):
2540
self.enable_metrics = config.get("enable_metrics", False)
2641
self.report_stats = config.get("report_stats", None)
2742
self.metrics_port = config.get("metrics_port")
2843
self.metrics_bind_host = config.get("metrics_bind_host", "127.0.0.1")
2944

45+
if self.enable_metrics:
46+
_metrics_config = config.get("metrics_flags") or {}
47+
self.metrics_flags = MetricsFlags(**_metrics_config)
48+
else:
49+
self.metrics_flags = MetricsFlags.all_off()
50+
3051
self.sentry_enabled = "sentry" in config
3152
if self.sentry_enabled:
3253
try:
@@ -58,6 +79,16 @@ def generate_config_section(self, report_stats=None, **kwargs):
5879
#sentry:
5980
# dsn: "..."
6081
82+
# Flags to enable Prometheus metrics which are not suitable to be
83+
# enabled by default, either for performance reasons or limited use.
84+
#
85+
metrics_flags:
86+
# Publish synapse_federation_known_servers, a g auge of the number of
87+
# servers this homeserver knows about, including itself. May cause
88+
# performance problems on large homeservers.
89+
#
90+
#known_servers: true
91+
6192
# Whether or not to report anonymized homeserver usage statistics.
6293
"""
6394

synapse/storage/roommember.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@
2424
from twisted.internet import defer
2525

2626
from synapse.api.constants import EventTypes, Membership
27+
from synapse.metrics import LaterGauge
2728
from synapse.metrics.background_process_metrics import run_as_background_process
2829
from synapse.storage._base import LoggingTransaction
30+
from synapse.storage.engines import Sqlite3Engine
2931
from synapse.storage.events_worker import EventsWorkerStore
3032
from synapse.types import get_domain_from_id
3133
from synapse.util.async_helpers import Linearizer
@@ -74,6 +76,63 @@ def __init__(self, db_conn, hs):
7476
self._check_safe_current_state_events_membership_updated_txn(txn)
7577
txn.close()
7678

79+
if self.hs.config.metrics_flags.known_servers:
80+
self._known_servers_count = 1
81+
self.hs.get_clock().looping_call(
82+
run_as_background_process,
83+
60 * 1000,
84+
"_count_known_servers",
85+
self._count_known_servers,
86+
)
87+
self.hs.get_clock().call_later(
88+
1000,
89+
run_as_background_process,
90+
"_count_known_servers",
91+
self._count_known_servers,
92+
)
93+
LaterGauge(
94+
"synapse_federation_known_servers",
95+
"",
96+
[],
97+
lambda: self._known_servers_count,
98+
)
99+
100+
@defer.inlineCallbacks
101+
def _count_known_servers(self):
102+
"""
103+
Count the servers that this server knows about.
104+
105+
The statistic is stored on the class for the
106+
`synapse_federation_known_servers` LaterGauge to collect.
107+
"""
108+
109+
def _transact(txn):
110+
if isinstance(self.database_engine, Sqlite3Engine):
111+
query = """
112+
SELECT COUNT(DISTINCT substr(out.user_id, pos+1))
113+
FROM (
114+
SELECT rm.user_id as user_id, instr(rm.user_id, ':')
115+
AS pos FROM room_memberships as rm
116+
INNER JOIN current_state_events as c ON rm.event_id = c.event_id
117+
WHERE c.type = 'm.room.member'
118+
) as out
119+
"""
120+
else:
121+
query = """
122+
SELECT COUNT(DISTINCT split_part(state_key, ':', 2))
123+
FROM current_state_events
124+
WHERE type = 'm.room.member' AND membership = 'join';
125+
"""
126+
txn.execute(query)
127+
return list(txn)[0][0]
128+
129+
count = yield self.runInteraction("get_known_servers", _transact)
130+
131+
# We always know about ourselves, even if we have nothing in
132+
# room_memberships (for example, the server is new).
133+
self._known_servers_count = max([count, 1])
134+
return self._known_servers_count
135+
77136
def _check_safe_current_state_events_membership_updated_txn(self, txn):
78137
"""Checks if it is safe to assume the new current_state_events
79138
membership column is up to date

tests/config/test_generate.py

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
import re
1818
import shutil
1919
import tempfile
20+
from contextlib import redirect_stdout
21+
from io import StringIO
2022

2123
from synapse.config.homeserver import HomeServerConfig
2224

@@ -32,17 +34,18 @@ def tearDown(self):
3234
shutil.rmtree(self.dir)
3335

3436
def test_generate_config_generates_files(self):
35-
HomeServerConfig.load_or_generate_config(
36-
"",
37-
[
38-
"--generate-config",
39-
"-c",
40-
self.file,
41-
"--report-stats=yes",
42-
"-H",
43-
"lemurs.win",
44-
],
45-
)
37+
with redirect_stdout(StringIO()):
38+
HomeServerConfig.load_or_generate_config(
39+
"",
40+
[
41+
"--generate-config",
42+
"-c",
43+
self.file,
44+
"--report-stats=yes",
45+
"-H",
46+
"lemurs.win",
47+
],
48+
)
4649

4750
self.assertSetEqual(
4851
set(["homeserver.yaml", "lemurs.win.log.config", "lemurs.win.signing.key"]),

tests/config/test_load.py

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
import os.path
1616
import shutil
1717
import tempfile
18+
from contextlib import redirect_stdout
19+
from io import StringIO
1820

1921
import yaml
2022

@@ -26,7 +28,6 @@
2628
class ConfigLoadingTestCase(unittest.TestCase):
2729
def setUp(self):
2830
self.dir = tempfile.mkdtemp()
29-
print(self.dir)
3031
self.file = os.path.join(self.dir, "homeserver.yaml")
3132

3233
def tearDown(self):
@@ -94,18 +95,27 @@ def test_disable_registration(self):
9495
)
9596
self.assertTrue(config.enable_registration)
9697

98+
def test_stats_enabled(self):
99+
self.generate_config_and_remove_lines_containing("enable_metrics")
100+
self.add_lines_to_config(["enable_metrics: true"])
101+
102+
# The default Metrics Flags are off by default.
103+
config = HomeServerConfig.load_config("", ["-c", self.file])
104+
self.assertFalse(config.metrics_flags.known_servers)
105+
97106
def generate_config(self):
98-
HomeServerConfig.load_or_generate_config(
99-
"",
100-
[
101-
"--generate-config",
102-
"-c",
103-
self.file,
104-
"--report-stats=yes",
105-
"-H",
106-
"lemurs.win",
107-
],
108-
)
107+
with redirect_stdout(StringIO()):
108+
HomeServerConfig.load_or_generate_config(
109+
"",
110+
[
111+
"--generate-config",
112+
"-c",
113+
self.file,
114+
"--report-stats=yes",
115+
"-H",
116+
"lemurs.win",
117+
],
118+
)
109119

110120
def generate_config_and_remove_lines_containing(self, needle):
111121
self.generate_config()

0 commit comments

Comments
 (0)