Openenv / app /session_store.py
vishaldhakad's picture
intial push
eda351c
"""
app/session_store.py β€” Redis abstraction with in-memory fallback.
V2 Fix: V1 used a plain dict β€” sessions lost on restart.
V2 uses Upstash Redis (free tier). If Redis is unavailable, falls back to
an in-memory dict so the episode never crashes. Worst case: sessions are
process-local again, same as V1.
The rest of the codebase never touches Redis directly β€” only load/save/delete.
"""
import os
import pickle
from typing import Optional
# ── Lazy Redis client ────────────────────────────────────────────────────────
_redis_client = None
_local_cache: dict = {} # In-memory fallback β€” activated when Redis is down
REDIS_URL = os.getenv("REDIS_URL", "")
SESSION_TTL = 3600 # 1 hour β€” episodes expire after inactivity
def _get_redis():
"""Lazy singleton. Returns Redis client or None if unavailable."""
global _redis_client
if _redis_client is not None:
return _redis_client
if not REDIS_URL:
return None
try:
import redis as redis_lib
_redis_client = redis_lib.from_url(REDIS_URL, decode_responses=False, socket_timeout=2)
_redis_client.ping() # Fail fast if connection is broken
return _redis_client
except Exception:
return None
def load(session_id: str):
"""Fetch EpisodeState from Redis, fall back to local cache."""
key = f"session:{session_id}"
r = _get_redis()
if r:
try:
data = r.get(key)
return pickle.loads(data) if data else None
except Exception:
pass
# Fallback: local memory
return _local_cache.get(session_id)
def save(session_id: str, state) -> None:
"""Persist EpisodeState to Redis + local cache (dual write for resilience)."""
key = f"session:{session_id}"
_local_cache[session_id] = state # Always write locally
r = _get_redis()
if r:
try:
r.setex(key, SESSION_TTL, pickle.dumps(state))
except Exception:
pass # Redis outage β€” local cache is the fallback
def delete(session_id: str) -> None:
"""Remove session after episode completes."""
_local_cache.pop(session_id, None)
r = _get_redis()
if r:
try:
r.delete(f"session:{session_id}")
except Exception:
pass