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

Servers-known-about statistic #5981

Merged
merged 18 commits into from
Sep 6, 2019
1 change: 1 addition & 0 deletions changelog.d/5981.feature
Original file line number Diff line number Diff line change
@@ -0,0 +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.
10 changes: 10 additions & 0 deletions docs/sample_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -926,6 +926,16 @@ uploads_path: "DATADIR/uploads"
#sentry:
# dsn: "..."

# Flags to enable Prometheus metrics which are not suitable to be
# enabled by default, either for performance reasons or limited use.
#
metrics_flags:
# Publish synapse_federation_known_servers, a g auge of the number of
# servers this homeserver knows about, including itself. May cause
# performance problems on large homeservers.
#
#known_servers: true

# Whether or not to report anonymized homeserver usage statistics.
# report_stats: true|false

Expand Down
31 changes: 31 additions & 0 deletions synapse/config/metrics.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright 2015, 2016 OpenMarket Ltd
# Copyright 2019 The 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.
Expand All @@ -13,20 +14,40 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import attr

from ._base import Config, ConfigError

MISSING_SENTRY = """Missing sentry-sdk library. This is required to enable sentry
integration.
"""


@attr.s
class MetricsFlags(object):
known_servers = attr.ib(default=False, validator=attr.validators.instance_of(bool))

@classmethod
def all_off(cls):
"""
Instantiate the flags with all options set to off.
"""
return cls(**{x.name: False for x in attr.fields(cls)})


class MetricsConfig(Config):
def read_config(self, config, **kwargs):
self.enable_metrics = config.get("enable_metrics", False)
self.report_stats = config.get("report_stats", None)
self.metrics_port = config.get("metrics_port")
self.metrics_bind_host = config.get("metrics_bind_host", "127.0.0.1")

if self.enable_metrics:
_metrics_config = config.get("metrics_flags") or {}
self.metrics_flags = MetricsFlags(**_metrics_config)
else:
self.metrics_flags = MetricsFlags.all_off()

self.sentry_enabled = "sentry" in config
if self.sentry_enabled:
try:
Expand Down Expand Up @@ -58,6 +79,16 @@ def generate_config_section(self, report_stats=None, **kwargs):
#sentry:
# dsn: "..."

# Flags to enable Prometheus metrics which are not suitable to be
# enabled by default, either for performance reasons or limited use.
#
metrics_flags:
# Publish synapse_federation_known_servers, a g auge of the number of
# servers this homeserver knows about, including itself. May cause
# performance problems on large homeservers.
#
#known_servers: true

# Whether or not to report anonymized homeserver usage statistics.
"""

Expand Down
59 changes: 59 additions & 0 deletions synapse/storage/roommember.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,10 @@
from twisted.internet import defer

from synapse.api.constants import EventTypes, Membership
from synapse.metrics import LaterGauge
from synapse.metrics.background_process_metrics import run_as_background_process
from synapse.storage._base import LoggingTransaction
from synapse.storage.engines import Sqlite3Engine
from synapse.storage.events_worker import EventsWorkerStore
from synapse.types import get_domain_from_id
from synapse.util.async_helpers import Linearizer
Expand Down Expand Up @@ -74,6 +76,63 @@ def __init__(self, db_conn, hs):
self._check_safe_current_state_events_membership_updated_txn(txn)
txn.close()

if self.hs.config.metrics_flags.known_servers:
self._known_servers_count = 1
self.hs.get_clock().looping_call(
run_as_background_process,
60 * 1000,
"_count_known_servers",
self._count_known_servers,
)
self.hs.get_clock().call_later(
1000,
run_as_background_process,
"_count_known_servers",
self._count_known_servers,
)
LaterGauge(
"synapse_federation_known_servers",
"",
[],
lambda: self._known_servers_count,
)

@defer.inlineCallbacks
def _count_known_servers(self):
"""
Count the servers that this server knows about.
hawkowl marked this conversation as resolved.
Show resolved Hide resolved

The statistic is stored on the class for the
`synapse_federation_known_servers` LaterGauge to collect.
"""

def _transact(txn):
if isinstance(self.database_engine, Sqlite3Engine):
query = """
SELECT COUNT(DISTINCT substr(out.user_id, pos+1))
FROM (
SELECT rm.user_id as user_id, instr(rm.user_id, ':')
AS pos FROM room_memberships as rm
INNER JOIN current_state_events as c ON rm.event_id = c.event_id
WHERE c.type = 'm.room.member'
) as out
"""
else:
query = """
SELECT COUNT(DISTINCT split_part(state_key, ':', 2))
FROM current_state_events
WHERE type = 'm.room.member' AND membership = 'join';
"""
txn.execute(query)
return list(txn)[0][0]

count = yield self.runInteraction("get_known_servers", _transact)

# We always know about ourselves, even if we have nothing in
# room_memberships (for example, the server is new).
self._known_servers_count = max([count, 1])
return self._known_servers_count

def _check_safe_current_state_events_membership_updated_txn(self, txn):
"""Checks if it is safe to assume the new current_state_events
membership column is up to date
Expand Down
25 changes: 14 additions & 11 deletions tests/config/test_generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
import re
import shutil
import tempfile
from contextlib import redirect_stdout
from io import StringIO

from synapse.config.homeserver import HomeServerConfig

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

def test_generate_config_generates_files(self):
HomeServerConfig.load_or_generate_config(
"",
[
"--generate-config",
"-c",
self.file,
"--report-stats=yes",
"-H",
"lemurs.win",
],
)
with redirect_stdout(StringIO()):
HomeServerConfig.load_or_generate_config(
"",
[
"--generate-config",
"-c",
self.file,
"--report-stats=yes",
"-H",
"lemurs.win",
],
)

self.assertSetEqual(
set(["homeserver.yaml", "lemurs.win.log.config", "lemurs.win.signing.key"]),
Expand Down
34 changes: 22 additions & 12 deletions tests/config/test_load.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
import os.path
import shutil
import tempfile
from contextlib import redirect_stdout
from io import StringIO

import yaml

Expand All @@ -26,7 +28,6 @@
class ConfigLoadingTestCase(unittest.TestCase):
def setUp(self):
self.dir = tempfile.mkdtemp()
print(self.dir)
self.file = os.path.join(self.dir, "homeserver.yaml")

def tearDown(self):
Expand Down Expand Up @@ -94,18 +95,27 @@ def test_disable_registration(self):
)
self.assertTrue(config.enable_registration)

def test_stats_enabled(self):
self.generate_config_and_remove_lines_containing("enable_metrics")
self.add_lines_to_config(["enable_metrics: true"])

# The default Metrics Flags are off by default.
config = HomeServerConfig.load_config("", ["-c", self.file])
self.assertFalse(config.metrics_flags.known_servers)

def generate_config(self):
HomeServerConfig.load_or_generate_config(
"",
[
"--generate-config",
"-c",
self.file,
"--report-stats=yes",
"-H",
"lemurs.win",
],
)
with redirect_stdout(StringIO()):
HomeServerConfig.load_or_generate_config(
"",
[
"--generate-config",
"-c",
self.file,
"--report-stats=yes",
"-H",
"lemurs.win",
],
)

def generate_config_and_remove_lines_containing(self, needle):
self.generate_config()
Expand Down
Loading