Break down monthly active users by appservice_id (#7030)
* Break down monthly active users by appservice_id and emit via prometheus. Co-authored-by: Brendan Abolivier <babolivier@matrix.org>pull/7048/head
parent
87972f07e5
commit
1d66dce83e
|
@ -0,0 +1 @@
|
||||||
|
Break down monthly active users by `appservice_id` and emit via Prometheus.
|
|
@ -298,6 +298,11 @@ class SynapseHomeServer(HomeServer):
|
||||||
|
|
||||||
# Gauges to expose monthly active user control metrics
|
# Gauges to expose monthly active user control metrics
|
||||||
current_mau_gauge = Gauge("synapse_admin_mau:current", "Current MAU")
|
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")
|
max_mau_gauge = Gauge("synapse_admin_mau:max", "MAU Limit")
|
||||||
registered_reserved_users_mau_gauge = Gauge(
|
registered_reserved_users_mau_gauge = Gauge(
|
||||||
"synapse_admin_mau:registered_reserved_users",
|
"synapse_admin_mau:registered_reserved_users",
|
||||||
|
@ -585,12 +590,20 @@ def run(hs):
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def generate_monthly_active_users():
|
def generate_monthly_active_users():
|
||||||
current_mau_count = 0
|
current_mau_count = 0
|
||||||
|
current_mau_count_by_service = {}
|
||||||
reserved_users = ()
|
reserved_users = ()
|
||||||
store = hs.get_datastore()
|
store = hs.get_datastore()
|
||||||
if hs.config.limit_usage_by_mau or hs.config.mau_stats_only:
|
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 = 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()
|
reserved_users = yield store.get_registered_reserved_users()
|
||||||
current_mau_gauge.set(float(current_mau_count))
|
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)))
|
registered_reserved_users_mau_gauge.set(float(len(reserved_users)))
|
||||||
max_mau_gauge.set(float(hs.config.max_mau_value))
|
max_mau_gauge.set(float(hs.config.max_mau_value))
|
||||||
|
|
||||||
|
|
|
@ -43,13 +43,40 @@ class MonthlyActiveUsersWorkerStore(SQLBaseStore):
|
||||||
|
|
||||||
def _count_users(txn):
|
def _count_users(txn):
|
||||||
sql = "SELECT COALESCE(count(*), 0) FROM monthly_active_users"
|
sql = "SELECT COALESCE(count(*), 0) FROM monthly_active_users"
|
||||||
|
|
||||||
txn.execute(sql)
|
txn.execute(sql)
|
||||||
(count,) = txn.fetchone()
|
(count,) = txn.fetchone()
|
||||||
return count
|
return count
|
||||||
|
|
||||||
return self.db.runInteraction("count_users", _count_users)
|
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.
|
||||||
|
A service is typically an appservice but also includes native matrix users.
|
||||||
|
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 other than native matrix users.
|
||||||
|
|
||||||
|
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)
|
||||||
|
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
|
@defer.inlineCallbacks
|
||||||
def get_registered_reserved_users(self):
|
def get_registered_reserved_users(self):
|
||||||
"""Of the reserved threepids defined in config, which are associated
|
"""Of the reserved threepids defined in config, which are associated
|
||||||
|
@ -291,6 +318,9 @@ class MonthlyActiveUsersStore(MonthlyActiveUsersWorkerStore):
|
||||||
)
|
)
|
||||||
|
|
||||||
self._invalidate_cache_and_stream(txn, self.get_monthly_active_count, ())
|
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(
|
self._invalidate_cache_and_stream(
|
||||||
txn, self.user_last_seen_monthly_active, (user_id,)
|
txn, self.user_last_seen_monthly_active, (user_id,)
|
||||||
)
|
)
|
||||||
|
|
|
@ -303,3 +303,45 @@ class MonthlyActiveUsersTestCase(unittest.HomeserverTestCase):
|
||||||
self.pump()
|
self.pump()
|
||||||
|
|
||||||
self.store.upsert_monthly_active_user.assert_not_called()
|
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])
|
||||||
|
|
Loading…
Reference in New Issue