Skip to content

Commit e766f32

Browse files
authored
fix: Compute user last seen timestamp from last seen devices (#18948)
## Fix last seen timestamp in `/_synapse/admin/v2/users` response Fixes #18955 The last seen timestamps contained in `/_synapse/admin/v2/users` responses were computed as follows: ```sql [...] LEFT JOIN ( SELECT user_id, MAX(last_seen) AS last_seen_ts FROM user_ips GROUP BY user_id ) ls ON u.name = ls.user_id [...] ``` https://github.com/element-hq/synapse/blob/4367fb2d078c52959aeca0fe6874539c53e8360d/synapse/storage/databases/main/__init__.py#L302C1-L305C44 This leads to empty timestamps (as in: user was never seen) if users are inactive for longer than [`user_ips_max_age`](https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html#user_ips_max_age). The fix is quite trivial: Use the `devices` table, as this one also contains last seen timestamps but is *not* periodically purged. We are using this for automatic user account deletion (via [synadm](https://codeberg.org/synadm/synadm)) and the patched code works as intended, whereas the unpatched version wants to delete users during long vacations. 🫣
1 parent 512b3f5 commit e766f32

2 files changed

Lines changed: 8 additions & 2 deletions

File tree

changelog.d/18948.bugfix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Compute a user's last seen timestamp from their devices' last seen timestamps instead of IPs, because the latter are automatically cleared according to `user_ips_max_age`.

synapse/storage/databases/main/__init__.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -299,10 +299,14 @@ def get_users_paginate_txn(
299299
FROM users as u
300300
LEFT JOIN profiles AS p ON u.name = p.full_user_id
301301
LEFT JOIN erased_users AS eu ON u.name = eu.user_id
302+
LEFT JOIN (
303+
SELECT user_id, MAX(last_seen) AS last_seen_ts
304+
FROM devices GROUP BY user_id
305+
) lsd ON u.name = lsd.user_id
302306
LEFT JOIN (
303307
SELECT user_id, MAX(last_seen) AS last_seen_ts
304308
FROM user_ips GROUP BY user_id
305-
) ls ON u.name = ls.user_id
309+
) lsi ON u.name = lsi.user_id
306310
{where_clause}
307311
"""
308312
sql = "SELECT COUNT(*) as total_users " + sql_base
@@ -312,7 +316,8 @@ def get_users_paginate_txn(
312316
sql = f"""
313317
SELECT name, user_type, is_guest, admin, deactivated, shadow_banned,
314318
displayname, avatar_url, creation_ts * 1000 as creation_ts, approved,
315-
eu.user_id is not null as erased, last_seen_ts, locked
319+
eu.user_id is not null as erased,
320+
COALESCE(lsd.last_seen_ts, lsi.last_seen_ts) as last_seen_ts, locked
316321
{sql_base}
317322
ORDER BY {order_by_column} {order}, u.name ASC
318323
LIMIT ? OFFSET ?

0 commit comments

Comments
 (0)