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

Commit

Permalink
Only delete N devices at a time
Browse files Browse the repository at this point in the history
  • Loading branch information
erikjohnston committed Dec 2, 2022
1 parent 67f36d5 commit 707535e
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 3 deletions.
20 changes: 19 additions & 1 deletion synapse/handlers/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
from synapse.util.async_helpers import Linearizer
from synapse.util.caches.expiringcache import ExpiringCache
from synapse.util.cancellation import cancellable
from synapse.util.iterutils import batch_iter
from synapse.util.metrics import measure_func
from synapse.util.retryutils import NotRetryingDestination

Expand Down Expand Up @@ -461,7 +462,24 @@ async def _prune_too_many_devices(self, user_id: str) -> None:
if not device_ids:
return

await self.delete_devices(user_id, device_ids)
# We don't want to block and try and delete tonnes of devices at once,
# so we cap the number of devices we delete synchronously.
first_batch, remaining_device_ids = device_ids[:10], device_ids[10:]
await self.delete_devices(user_id, first_batch)

if not remaining_device_ids:
return

# Now spawn a background loop that deletes the rest.
async def _prune_too_many_devices_loop() -> None:
for batch in batch_iter(remaining_device_ids, 10):
await self.delete_devices(user_id, batch)

await self.clock.sleep(1)

run_as_background_process(
"_prune_too_many_devices_loop", _prune_too_many_devices_loop
)

async def _delete_stale_devices(self) -> None:
"""Background task that deletes devices which haven't been accessed for more than
Expand Down
5 changes: 3 additions & 2 deletions synapse/storage/databases/main/devices.py
Original file line number Diff line number Diff line change
Expand Up @@ -1533,7 +1533,7 @@ def _txn(txn: LoggingTransaction) -> int:

return rows

async def check_too_many_devices_for_user(self, user_id: str) -> Collection[str]:
async def check_too_many_devices_for_user(self, user_id: str) -> List[str]:
"""Check if the user has a lot of devices, and if so return the set of
devices we can prune.
Expand Down Expand Up @@ -1585,13 +1585,14 @@ async def check_too_many_devices_for_user(self, user_id: str) -> Collection[str]
AND NOT hidden
AND last_seen < ?
AND key_json IS NULL
ORDER BY last_seen
"""

def check_too_many_devices_for_user_txn(
txn: LoggingTransaction,
) -> Collection[str]:
txn.execute(sql, (user_id, max_last_seen))
return {device_id for device_id, in txn}
return [device_id for device_id, in txn]

return await self.db_pool.runInteraction(
"check_too_many_devices_for_user",
Expand Down

0 comments on commit 707535e

Please sign in to comment.