mirror of
https://git.victorphan.net/basketballcantho/ten-project.git
synced 2026-08-05 09:53:10 +07:00
76 lines
2.2 KiB
Python
76 lines
2.2 KiB
Python
"""
|
|
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}:*"
|