mirror of
https://git.victorphan.net/basketballcantho/ten-project.git
synced 2026-08-05 14:33:10 +07:00
colaborative tích hợp redis để đồng bộ toàn bộ annotation
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
"""
|
||||
Redis helper — returns a connected client or None when Redis is unavailable.
|
||||
|
||||
Key namespace design (prevents cross-file / cross-user data leakage):
|
||||
|
||||
ann:{pdf_id}:{page_number}:{user_id}
|
||||
|
||||
Every segment is mandatory, so:
|
||||
- User A opening file 1 never touches User A's data on file 2
|
||||
- User A opening file 1 never touches User B's data on file 1
|
||||
- Data for page 3 never affects page 7
|
||||
|
||||
TTL: 24 h — temp entry expires automatically if the user never returns.
|
||||
On permanent save the entry is deleted immediately.
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
from typing import Optional
|
||||
|
||||
import redis as redis_lib
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_client: Optional[redis_lib.Redis] = None
|
||||
_warned = False # log the "unavailable" warning only once
|
||||
|
||||
|
||||
def get_redis() -> Optional[redis_lib.Redis]:
|
||||
"""Return a live Redis client, or None if Redis is unreachable."""
|
||||
global _client, _warned
|
||||
|
||||
if _client is not None:
|
||||
try:
|
||||
_client.ping()
|
||||
return _client
|
||||
except Exception:
|
||||
_client = None # connection dropped — try to reconnect below
|
||||
|
||||
url = os.getenv("REDIS_URL", "redis://localhost:6379/0")
|
||||
try:
|
||||
r: redis_lib.Redis = redis_lib.from_url(
|
||||
url,
|
||||
decode_responses=True,
|
||||
socket_connect_timeout=2,
|
||||
socket_timeout=2,
|
||||
)
|
||||
r.ping()
|
||||
_client = r
|
||||
_warned = False
|
||||
logger.info("Redis connected: %s", url)
|
||||
return _client
|
||||
except Exception as exc:
|
||||
if not _warned:
|
||||
logger.warning(
|
||||
"Redis unavailable (%s) — temp annotation cache disabled. "
|
||||
"Set REDIS_URL or start a local Redis instance to enable it.",
|
||||
exc,
|
||||
)
|
||||
_warned = True
|
||||
return None
|
||||
|
||||
|
||||
# Key helpers — centralised so there's one place to change the format.
|
||||
ANN_TTL = 86_400 # 24 hours
|
||||
|
||||
|
||||
def ann_temp_key(pdf_id: int, page_number: int, user_id: int) -> str:
|
||||
"""Fully-namespaced Redis key for one user's unsaved canvas on one PDF page."""
|
||||
return f"ann:{pdf_id}:{page_number}:{user_id}"
|
||||
|
||||
|
||||
def ann_temp_page_pattern(pdf_id: int, page_number: int) -> str:
|
||||
"""Glob pattern to list all users' temp entries for a given page."""
|
||||
return f"ann:{pdf_id}:{page_number}:*"
|
||||
Reference in New Issue
Block a user