Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
7023899
Add synapse_admin_user_count metric.
Half-Shot Jun 11, 2026
c89519e
typo
Half-Shot Jun 11, 2026
b924048
spacing
Half-Shot Jun 11, 2026
3c0e6f4
Potential fix for pull request finding
Half-Shot Jun 11, 2026
4335e1f
Potential fix for pull request finding
Half-Shot Jun 11, 2026
39468bc
Potential fix for pull request finding
Half-Shot Jun 11, 2026
170d120
Potential fix for pull request finding
Half-Shot Jun 11, 2026
a0dc0c3
Potential fix for pull request finding
Half-Shot Jun 11, 2026
2c1a9f3
test update
Half-Shot Jun 11, 2026
e420c3b
lint
Half-Shot Jun 11, 2026
fb2ec7d
Bump metrics dependency to support .clear()
Half-Shot Jun 11, 2026
715c253
update lock
Half-Shot Jun 11, 2026
772082e
Active is fine
Half-Shot Jun 15, 2026
904385c
Review changes
Half-Shot Jun 16, 2026
1b9ce6c
update tests
Half-Shot Jun 16, 2026
3ae1139
Update tests
Half-Shot Jun 16, 2026
742d92f
Merge remote-tracking branch 'origin/develop' into hs/metric-for-all-…
Half-Shot Jun 16, 2026
d821bbe
update lock
Half-Shot Jun 16, 2026
b240e9c
More tidyup
Half-Shot Jun 18, 2026
a9b58dd
Add a comment
Half-Shot Jun 23, 2026
33b10c6
Update synapse/app/phone_stats_home.py
Half-Shot Jun 25, 2026
4176100
Update tests/metrics/test_phone_home_stats.py
Half-Shot Jun 25, 2026
7892274
Apply suggestions from code review
Half-Shot Jun 25, 2026
539a0c9
comment COUNT_USERS_INTERVAL value source.
Half-Shot Jun 25, 2026
54d6b58
Merge remote-tracking branch 'origin/develop' into hs/metric-for-all-…
Half-Shot Jun 25, 2026
49ed33e
use poetry 2.4.1 for lock
Half-Shot Jun 25, 2026
4f40339
lint
Half-Shot Jun 25, 2026
c3003e1
Use more standard import
MadLittleMods Jun 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/19848.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add new metric `synapse_admin_user_count` which tracks the number of non-deactivated users in the database, split by `app_service`.
63 changes: 33 additions & 30 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,8 @@ dependencies = [
"msgpack>=0.5.2",
"phonenumbers>=8.2.0",
# we use GaugeHistogramMetric, which was added in prom-client 0.4.0.
# `prometheus_client.metrics` was added in 0.5.0, so we require that too.
# We chose 0.6.0 as that is the current version in Debian Buster (oldstable).
"prometheus-client>=0.6.0",
# `Gauge.clear()` was added in 0.10.0.
"prometheus-client>=0.10.0",
# we use `order`, which arrived in attrs 19.2.0.
# Note: 21.1.0 broke `/sync`, see https://github.com/matrix-org/synapse/issues/9936
"attrs>=19.2.0,!=21.1.0",
Expand Down
27 changes: 27 additions & 0 deletions synapse/app/phone_stats_home.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@
"Registered users with reserved threepids",
labelnames=[SERVER_NAME_LABEL],
)
user_count_gauge = Gauge(
Comment thread
Half-Shot marked this conversation as resolved.
"synapse_admin_user_count",
Comment thread
Half-Shot marked this conversation as resolved.
Outdated
Comment thread
Half-Shot marked this conversation as resolved.
Outdated
"Total active (non-deactivated) user count within the Synapse database, split by appservice",
Comment thread
MadLittleMods marked this conversation as resolved.
Outdated
labelnames=["app_service", SERVER_NAME_LABEL],
)
Comment thread
Copilot marked this conversation as resolved.
Comment thread
Half-Shot marked this conversation as resolved.


def phone_stats_home(
Expand Down Expand Up @@ -261,6 +266,28 @@ async def _generate_monthly_active_users() -> None:
_generate_monthly_active_users,
)

def generate_total_users() -> "defer.Deferred[None]":
async def _generate_total_users() -> None:
store = hs.get_datastores().main

result = await store.get_user_count_by_service()

user_count_gauge.clear()
Comment thread
Half-Shot marked this conversation as resolved.

for app_service, count in result:
user_count_gauge.labels(
app_service=app_service, **{SERVER_NAME_LABEL: server_name}
).set(float(count))

Comment thread
Copilot marked this conversation as resolved.
return hs.run_as_background_process(
"generate_total_users",
_generate_total_users,
)

if hs.config.metrics.enable_metrics:
generate_total_users()
clock.looping_call(generate_total_users, Duration(minutes=5))

if hs.config.server.limit_usage_by_mau or hs.config.server.mau_stats_only:
generate_monthly_active_users()
clock.looping_call(generate_monthly_active_users, Duration(minutes=5))
Expand Down
27 changes: 27 additions & 0 deletions synapse/storage/databases/main/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -1133,6 +1133,33 @@ def _count_users(txn: LoggingTransaction) -> int:

return await self.db_pool.runInteraction("count_real_users", _count_users)

async def get_user_count_by_service(self) -> list[tuple[str, int]]:
"""Counts users grouped by their appservice.

Returns:
A list of tuples (appservice_id, count). "native" is emitted as the
appservice for users that don't come from appservices (i.e. native Matrix
users).
Comment thread
MadLittleMods marked this conversation as resolved.

"""

def _get_user_count_by_service(
txn: LoggingTransaction,
) -> list[tuple[str, int]]:
sql = """
SELECT COALESCE(NULLIF(appservice_id, ''), 'native') AS app_service, COUNT(*) AS count
FROM users
WHERE deactivated = 0
GROUP BY COALESCE(NULLIF(appservice_id, ''), 'native')
Comment thread
MadLittleMods marked this conversation as resolved.
Comment thread
Half-Shot marked this conversation as resolved.
"""

txn.execute(sql)
return cast(list[tuple[str, int]], txn.fetchall())

return await self.db_pool.runInteraction(
"get_user_count_by_service", _get_user_count_by_service
)

async def generate_user_id(self) -> str:
"""Generate a suitable localpart for a guest user

Expand Down
Loading
Loading