mirror of
https://github.com/suitenumerique/meet.git
synced 2026-08-29 11:47:15 +00:00
76a24d4787
`cache.keys()` runs Redis `KEYS`, a full-keyspace scan on Redis's single thread that blocks everything else, including session reads in the same cache. Its cost scales with total keys, not matches, and some managed providers disable `KEYS` entirely. The trusted-lobby feature made this urgent: the waiting-list endpoint scanned on every poll, and its polling audience grows from a few admins to potentially every authenticated participant. Switch to cursor-based `SCAN` via two `core.utils` helpers, deleting in bounded batches so cleanup of a large room cannot block either. A single seam also lets us forbid raw `cache.keys()` going forward. `SCAN` still iterates the keyspace incrementally on the polled path. If monitoring flags it, the follow-up is a per-room set index — out of scope here since it changes the lobby storage model.
53 lines
2.0 KiB
Python
53 lines
2.0 KiB
Python
"""Presence cache.
|
|
|
|
Redis-backed memo of "this identity is currently connected to this room".
|
|
|
|
This module is intentionally a *pure cache store* with no dependency on other
|
|
services, so that `participants_management` (which talks to LiveKit) can
|
|
import it without creating an import cycle. The composition of "check cache,
|
|
fall back to LiveKit" lives in
|
|
`ParticipantsManagement.check_if_in_meeting_cached`.
|
|
|
|
Only positive answers are stored: a sticky negative would lock out someone
|
|
who joins right after a miss for the whole TTL. The TTL is a safety net in
|
|
case an invalidation webhook is lost.
|
|
"""
|
|
|
|
from uuid import UUID
|
|
|
|
from django.conf import settings
|
|
from django.core.cache import cache
|
|
|
|
from core.utils import CACHE_SCAN_ITERSIZE
|
|
|
|
|
|
class PresenceCache:
|
|
"""Store and invalidate (room, identity) presence entries."""
|
|
|
|
@staticmethod
|
|
def _get_cache_key(room_id: UUID | str, identity: str) -> str:
|
|
"""Cache key for a (room, identity) presence entry."""
|
|
return f"{settings.PRESENCE_KEY_PREFIX}_{room_id!s}_{identity}"
|
|
|
|
def is_marked_present(self, room_id: UUID | str, identity: str) -> bool:
|
|
"""Return True if a positive presence entry exists in cache."""
|
|
return bool(cache.get(self._get_cache_key(room_id, identity)))
|
|
|
|
def mark_present(self, room_id: UUID | str, identity: str) -> None:
|
|
"""Record that `identity` is in `room_id`."""
|
|
cache.set(
|
|
self._get_cache_key(room_id, identity),
|
|
True,
|
|
timeout=settings.PRESENCE_CACHE_TIMEOUT,
|
|
)
|
|
|
|
def clear(self, room_id: UUID | str, identity: str) -> None:
|
|
"""Forget presence for one participant (e.g. on participant_left)."""
|
|
cache.delete(self._get_cache_key(room_id, identity))
|
|
|
|
def clear_room(self, room_id: UUID | str) -> None:
|
|
"""Forget presence for every participant of a room (on room_finished)."""
|
|
cache.delete_pattern(
|
|
self._get_cache_key(room_id, "*"), itersize=CACHE_SCAN_ITERSIZE
|
|
)
|