diff --git a/changelog.d/16124.bugfix b/changelog.d/16124.bugfix
new file mode 100644
index 0000000000..fb1d501a2f
--- /dev/null
+++ b/changelog.d/16124.bugfix
@@ -0,0 +1 @@
+Filter out user agent references to the sliding sync proxy and rust-sdk from the user_daily_visits table to ensure that Element X can be represented fully.
diff --git a/synapse/storage/databases/main/client_ips.py b/synapse/storage/databases/main/client_ips.py
index 0df160d2b0..d8d333e11d 100644
--- a/synapse/storage/databases/main/client_ips.py
+++ b/synapse/storage/databases/main/client_ips.py
@@ -579,6 +579,11 @@ class ClientIpWorkerStore(ClientIpBackgroundUpdateStore, MonthlyActiveUsersWorke
device_id: Optional[str],
now: Optional[int] = None,
) -> None:
+ # The sync proxy continuously triggers /sync even if the user is not
+ # present so should be excluded from user_ips entries.
+ if user_agent == "sync-v3-proxy-":
+ return
+
if not now:
now = int(self._clock.time_msec())
key = (user_id, access_token, ip)
diff --git a/tests/storage/test_client_ips.py b/tests/storage/test_client_ips.py
index cd0079871c..209d68b40b 100644
--- a/tests/storage/test_client_ips.py
+++ b/tests/storage/test_client_ips.py
@@ -654,6 +654,71 @@ class ClientIpStoreTestCase(unittest.HomeserverTestCase):
r,
)
+ def test_invalid_user_agents_are_ignored(self) -> None:
+ # First make sure we have completed all updates.
+ self.wait_for_background_updates()
+
+ user_id1 = "@user1:id"
+ user_id2 = "@user2:id"
+ device_id1 = "MY_DEVICE1"
+ device_id2 = "MY_DEVICE2"
+ access_token1 = "access_token1"
+ access_token2 = "access_token2"
+
+ # Insert a user IP 1
+ self.get_success(
+ self.store.store_device(
+ user_id1,
+ device_id1,
+ "display name1",
+ )
+ )
+ # Insert a user IP 2
+ self.get_success(
+ self.store.store_device(
+ user_id2,
+ device_id2,
+ "display name2",
+ )
+ )
+
+ self.get_success(
+ self.store.insert_client_ip(
+ user_id1, access_token1, "ip", "sync-v3-proxy-", device_id1
+ )
+ )
+ self.get_success(
+ self.store.insert_client_ip(
+ user_id2, access_token2, "ip", "user_agent", device_id2
+ )
+ )
+ # Force persisting to disk
+ self.reactor.advance(200)
+
+ # We should see that in the DB
+ result = self.get_success(
+ self.store.db_pool.simple_select_list(
+ table="user_ips",
+ keyvalues={},
+ retcols=["access_token", "ip", "user_agent", "device_id", "last_seen"],
+ desc="get_user_ip_and_agents",
+ )
+ )
+
+ # ensure user1 is filtered out
+ self.assertEqual(
+ result,
+ [
+ {
+ "access_token": access_token2,
+ "ip": "ip",
+ "user_agent": "user_agent",
+ "device_id": device_id2,
+ "last_seen": 0,
+ }
+ ],
+ )
+
class ClientIpAuthTestCase(unittest.HomeserverTestCase):
servlets = [
|