mirror of
https://git.anonymousland.org/anonymousland/synapse.git
synced 2025-07-26 13:15:19 -04:00
Move update_client_ip
background job from the main process to the background worker. (#12251)
This commit is contained in:
parent
319a805cd3
commit
f871222880
10 changed files with 159 additions and 152 deletions
|
@ -25,7 +25,9 @@ from synapse.storage.database import (
|
|||
LoggingTransaction,
|
||||
make_tuple_comparison_clause,
|
||||
)
|
||||
from synapse.storage.databases.main.monthly_active_users import MonthlyActiveUsersStore
|
||||
from synapse.storage.databases.main.monthly_active_users import (
|
||||
MonthlyActiveUsersWorkerStore,
|
||||
)
|
||||
from synapse.types import JsonDict, UserID
|
||||
from synapse.util.caches.lrucache import LruCache
|
||||
|
||||
|
@ -397,7 +399,7 @@ class ClientIpBackgroundUpdateStore(SQLBaseStore):
|
|||
return updated
|
||||
|
||||
|
||||
class ClientIpWorkerStore(ClientIpBackgroundUpdateStore):
|
||||
class ClientIpWorkerStore(ClientIpBackgroundUpdateStore, MonthlyActiveUsersWorkerStore):
|
||||
def __init__(
|
||||
self,
|
||||
database: DatabasePool,
|
||||
|
@ -406,11 +408,40 @@ class ClientIpWorkerStore(ClientIpBackgroundUpdateStore):
|
|||
):
|
||||
super().__init__(database, db_conn, hs)
|
||||
|
||||
if hs.config.redis.redis_enabled:
|
||||
# If we're using Redis, we can shift this update process off to
|
||||
# the background worker
|
||||
self._update_on_this_worker = hs.config.worker.run_background_tasks
|
||||
else:
|
||||
# If we're NOT using Redis, this must be handled by the master
|
||||
self._update_on_this_worker = hs.get_instance_name() == "master"
|
||||
|
||||
self.user_ips_max_age = hs.config.server.user_ips_max_age
|
||||
|
||||
# (user_id, access_token, ip,) -> last_seen
|
||||
self.client_ip_last_seen = LruCache[Tuple[str, str, str], int](
|
||||
cache_name="client_ip_last_seen", max_size=50000
|
||||
)
|
||||
|
||||
if hs.config.worker.run_background_tasks and self.user_ips_max_age:
|
||||
self._clock.looping_call(self._prune_old_user_ips, 5 * 1000)
|
||||
|
||||
if self._update_on_this_worker:
|
||||
# This is the designated worker that can write to the client IP
|
||||
# tables.
|
||||
|
||||
# (user_id, access_token, ip,) -> (user_agent, device_id, last_seen)
|
||||
self._batch_row_update: Dict[
|
||||
Tuple[str, str, str], Tuple[str, Optional[str], int]
|
||||
] = {}
|
||||
|
||||
self._client_ip_looper = self._clock.looping_call(
|
||||
self._update_client_ips_batch, 5 * 1000
|
||||
)
|
||||
self.hs.get_reactor().addSystemEventTrigger(
|
||||
"before", "shutdown", self._update_client_ips_batch
|
||||
)
|
||||
|
||||
@wrap_as_background_process("prune_old_user_ips")
|
||||
async def _prune_old_user_ips(self) -> None:
|
||||
"""Removes entries in user IPs older than the configured period."""
|
||||
|
@ -456,7 +487,7 @@ class ClientIpWorkerStore(ClientIpBackgroundUpdateStore):
|
|||
"_prune_old_user_ips", _prune_old_user_ips_txn
|
||||
)
|
||||
|
||||
async def get_last_client_ip_by_device(
|
||||
async def _get_last_client_ip_by_device_from_database(
|
||||
self, user_id: str, device_id: Optional[str]
|
||||
) -> Dict[Tuple[str, str], DeviceLastConnectionInfo]:
|
||||
"""For each device_id listed, give the user_ip it was last seen on.
|
||||
|
@ -487,7 +518,7 @@ class ClientIpWorkerStore(ClientIpBackgroundUpdateStore):
|
|||
|
||||
return {(d["user_id"], d["device_id"]): d for d in res}
|
||||
|
||||
async def get_user_ip_and_agents(
|
||||
async def _get_user_ip_and_agents_from_database(
|
||||
self, user: UserID, since_ts: int = 0
|
||||
) -> List[LastConnectionInfo]:
|
||||
"""Fetch the IPs and user agents for a user since the given timestamp.
|
||||
|
@ -539,34 +570,6 @@ class ClientIpWorkerStore(ClientIpBackgroundUpdateStore):
|
|||
for access_token, ip, user_agent, last_seen in rows
|
||||
]
|
||||
|
||||
|
||||
class ClientIpStore(ClientIpWorkerStore, MonthlyActiveUsersStore):
|
||||
def __init__(
|
||||
self,
|
||||
database: DatabasePool,
|
||||
db_conn: LoggingDatabaseConnection,
|
||||
hs: "HomeServer",
|
||||
):
|
||||
|
||||
# (user_id, access_token, ip,) -> last_seen
|
||||
self.client_ip_last_seen = LruCache[Tuple[str, str, str], int](
|
||||
cache_name="client_ip_last_seen", max_size=50000
|
||||
)
|
||||
|
||||
super().__init__(database, db_conn, hs)
|
||||
|
||||
# (user_id, access_token, ip,) -> (user_agent, device_id, last_seen)
|
||||
self._batch_row_update: Dict[
|
||||
Tuple[str, str, str], Tuple[str, Optional[str], int]
|
||||
] = {}
|
||||
|
||||
self._client_ip_looper = self._clock.looping_call(
|
||||
self._update_client_ips_batch, 5 * 1000
|
||||
)
|
||||
self.hs.get_reactor().addSystemEventTrigger(
|
||||
"before", "shutdown", self._update_client_ips_batch
|
||||
)
|
||||
|
||||
async def insert_client_ip(
|
||||
self,
|
||||
user_id: str,
|
||||
|
@ -584,17 +587,27 @@ class ClientIpStore(ClientIpWorkerStore, MonthlyActiveUsersStore):
|
|||
last_seen = self.client_ip_last_seen.get(key)
|
||||
except KeyError:
|
||||
last_seen = None
|
||||
await self.populate_monthly_active_users(user_id)
|
||||
|
||||
# Rate-limited inserts
|
||||
if last_seen is not None and (now - last_seen) < LAST_SEEN_GRANULARITY:
|
||||
return
|
||||
|
||||
self.client_ip_last_seen.set(key, now)
|
||||
|
||||
self._batch_row_update[key] = (user_agent, device_id, now)
|
||||
if self._update_on_this_worker:
|
||||
await self.populate_monthly_active_users(user_id)
|
||||
self._batch_row_update[key] = (user_agent, device_id, now)
|
||||
else:
|
||||
# We are not the designated writer-worker, so stream over replication
|
||||
self.hs.get_replication_command_handler().send_user_ip(
|
||||
user_id, access_token, ip, user_agent, device_id, now
|
||||
)
|
||||
|
||||
@wrap_as_background_process("update_client_ips")
|
||||
async def _update_client_ips_batch(self) -> None:
|
||||
assert (
|
||||
self._update_on_this_worker
|
||||
), "This worker is not designated to update client IPs"
|
||||
|
||||
# If the DB pool has already terminated, don't try updating
|
||||
if not self.db_pool.is_running():
|
||||
|
@ -612,6 +625,10 @@ class ClientIpStore(ClientIpWorkerStore, MonthlyActiveUsersStore):
|
|||
txn: LoggingTransaction,
|
||||
to_update: Mapping[Tuple[str, str, str], Tuple[str, Optional[str], int]],
|
||||
) -> None:
|
||||
assert (
|
||||
self._update_on_this_worker
|
||||
), "This worker is not designated to update client IPs"
|
||||
|
||||
if "user_ips" in self.db_pool._unsafe_to_upsert_tables or (
|
||||
not self.database_engine.can_native_upsert
|
||||
):
|
||||
|
@ -662,7 +679,12 @@ class ClientIpStore(ClientIpWorkerStore, MonthlyActiveUsersStore):
|
|||
A dictionary mapping a tuple of (user_id, device_id) to dicts, with
|
||||
keys giving the column names from the devices table.
|
||||
"""
|
||||
ret = await super().get_last_client_ip_by_device(user_id, device_id)
|
||||
ret = await self._get_last_client_ip_by_device_from_database(user_id, device_id)
|
||||
|
||||
if not self._update_on_this_worker:
|
||||
# Only the writing-worker has additional in-memory data to enhance
|
||||
# the result
|
||||
return ret
|
||||
|
||||
# Update what is retrieved from the database with data which is pending
|
||||
# insertion, as if it has already been stored in the database.
|
||||
|
@ -707,9 +729,16 @@ class ClientIpStore(ClientIpWorkerStore, MonthlyActiveUsersStore):
|
|||
Only the latest user agent for each access token and IP address combination
|
||||
is available.
|
||||
"""
|
||||
rows_from_db = await self._get_user_ip_and_agents_from_database(user, since_ts)
|
||||
|
||||
if not self._update_on_this_worker:
|
||||
# Only the writing-worker has additional in-memory data to enhance
|
||||
# the result
|
||||
return rows_from_db
|
||||
|
||||
results: Dict[Tuple[str, str], LastConnectionInfo] = {
|
||||
(connection["access_token"], connection["ip"]): connection
|
||||
for connection in await super().get_user_ip_and_agents(user, since_ts)
|
||||
for connection in rows_from_db
|
||||
}
|
||||
|
||||
# Overlay data that is pending insertion on top of the results from the
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue