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

Break down monthly active users by appservice_id #7030

Merged
merged 3 commits into from
Mar 6, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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/7030.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Break down monthly active users by appservice_id and emit via prometheus.
neilisfragile marked this conversation as resolved.
Show resolved Hide resolved
13 changes: 13 additions & 0 deletions synapse/app/homeserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,11 @@ def start_listening(self, listeners):

# Gauges to expose monthly active user control metrics
current_mau_gauge = Gauge("synapse_admin_mau:current", "Current MAU")
current_mau_by_service_gauge = Gauge(
"synapse_admin_mau_current_mau_by_service",
"Current MAU by service",
["app_service"],
)
max_mau_gauge = Gauge("synapse_admin_mau:max", "MAU Limit")
registered_reserved_users_mau_gauge = Gauge(
"synapse_admin_mau:registered_reserved_users",
Expand Down Expand Up @@ -585,12 +590,20 @@ def reap_monthly_active_users():
@defer.inlineCallbacks
def generate_monthly_active_users():
current_mau_count = 0
current_mau_count_by_service = {}
reserved_users = ()
store = hs.get_datastore()
if hs.config.limit_usage_by_mau or hs.config.mau_stats_only:
current_mau_count = yield store.get_monthly_active_count()
current_mau_count_by_service = (
yield store.get_monthly_active_count_by_service()
)
reserved_users = yield store.get_registered_reserved_users()
current_mau_gauge.set(float(current_mau_count))

for app_service, count in current_mau_count_by_service.items():
current_mau_by_service_gauge.labels(app_service).set(float(count))

registered_reserved_users_mau_gauge.set(float(len(reserved_users)))
max_mau_gauge.set(float(hs.config.max_mau_value))

Expand Down
31 changes: 30 additions & 1 deletion synapse/storage/data_stores/main/monthly_active_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,39 @@ def get_monthly_active_count(self):

def _count_users(txn):
sql = "SELECT COALESCE(count(*), 0) FROM monthly_active_users"

txn.execute(sql)
(count,) = txn.fetchone()
return count

return self.db.runInteraction("count_users", _count_users)

@cached(num_args=0)
def get_monthly_active_count_by_service(self):
"""Generates current count of monthly active users broken down by service.
Since the `monthly_active_users` table is populated from the `user_ips` table
`config.track_appservice_user_ips` must be set to `true` for this
method to return anything meaningful.

Returns:
Deferred[dict]: dict that includes a mapping between app_service_id
and the number of occurrences.

"""

def _count_users_by_service(txn):
sql = """
SELECT COALESCE(appservice_id, 'native'), COALESCE(count(*),0)
neilisfragile marked this conversation as resolved.
Show resolved Hide resolved
FROM monthly_active_users
LEFT JOIN users ON monthly_active_users.user_id=users.name
GROUP BY appservice_id;
"""

txn.execute(sql)
result = txn.fetchall()
return dict(result)

return self.db.runInteraction("count_users_by_service", _count_users_by_service)

@defer.inlineCallbacks
def get_registered_reserved_users(self):
"""Of the reserved threepids defined in config, which are associated
Expand Down Expand Up @@ -291,6 +317,9 @@ def upsert_monthly_active_user_txn(self, txn, user_id):
)

self._invalidate_cache_and_stream(txn, self.get_monthly_active_count, ())
self._invalidate_cache_and_stream(
txn, self.get_monthly_active_count_by_service, ()
)
self._invalidate_cache_and_stream(
txn, self.user_last_seen_monthly_active, (user_id,)
)
Expand Down
42 changes: 42 additions & 0 deletions tests/storage/test_monthly_active_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,3 +303,45 @@ def test_no_users_when_not_tracking(self):
self.pump()

self.store.upsert_monthly_active_user.assert_not_called()

def test_get_monthly_active_count_by_service(self):
appservice1_user1 = "@appservice1_user1:example.com"
appservice1_user2 = "@appservice1_user2:example.com"

appservice2_user1 = "@appservice2_user1:example.com"
native_user1 = "@native_user1:example.com"

service1 = "service1"
service2 = "service2"
native = "native"

self.store.register_user(
user_id=appservice1_user1, password_hash=None, appservice_id=service1
)
self.store.register_user(
user_id=appservice1_user2, password_hash=None, appservice_id=service1
)
self.store.register_user(
user_id=appservice2_user1, password_hash=None, appservice_id=service2
)
self.store.register_user(user_id=native_user1, password_hash=None)
self.pump()

count = self.store.get_monthly_active_count_by_service()
self.assertEqual({}, self.get_success(count))

self.store.upsert_monthly_active_user(native_user1)
self.store.upsert_monthly_active_user(appservice1_user1)
self.store.upsert_monthly_active_user(appservice1_user2)
self.store.upsert_monthly_active_user(appservice2_user1)
self.pump()

count = self.store.get_monthly_active_count()
self.assertEqual(4, self.get_success(count))

count = self.store.get_monthly_active_count_by_service()
result = self.get_success(count)

self.assertEqual(2, result[service1])
self.assertEqual(1, result[service2])
self.assertEqual(1, result[native])