- Introduction of dynamic Processors - Introduction of caching system - Introduction of a better template manager - Adaptation of ModelVariables to support dynamic Processors / Retrievers / Specialists - Start adaptation of chat client
89 lines
2.7 KiB
Python
89 lines
2.7 KiB
Python
# common/utils/cache/base.py
|
|
|
|
from typing import Any, Dict, List, Optional, TypeVar, Generic, Type
|
|
from dataclasses import dataclass
|
|
from flask import Flask
|
|
from dogpile.cache import CacheRegion
|
|
|
|
T = TypeVar('T')
|
|
|
|
|
|
@dataclass
|
|
class CacheKey:
|
|
"""Represents a cache key with multiple components"""
|
|
components: Dict[str, Any]
|
|
|
|
def __str__(self) -> str:
|
|
return ":".join(f"{k}={v}" for k, v in sorted(self.components.items()))
|
|
|
|
|
|
class CacheInvalidationManager:
|
|
"""Manages cache invalidation subscriptions"""
|
|
|
|
def __init__(self):
|
|
self._subscribers = {}
|
|
|
|
def subscribe(self, model: str, handler: 'CacheHandler', key_fields: List[str]):
|
|
if model not in self._subscribers:
|
|
self._subscribers[model] = []
|
|
self._subscribers[model].append((handler, key_fields))
|
|
|
|
def notify_change(self, model: str, **identifiers):
|
|
if model in self._subscribers:
|
|
for handler, key_fields in self._subscribers[model]:
|
|
if all(field in identifiers for field in key_fields):
|
|
handler.invalidate_by_model(model, **identifiers)
|
|
|
|
|
|
class CacheHandler(Generic[T]):
|
|
"""Base cache handler implementation"""
|
|
|
|
def __init__(self, region: CacheRegion, prefix: str):
|
|
self.region = region
|
|
self.prefix = prefix
|
|
self._key_components = []
|
|
|
|
def configure_keys(self, *components: str):
|
|
self._key_components = components
|
|
return self
|
|
|
|
def subscribe_to_model(self, model: str, key_fields: List[str]):
|
|
invalidation_manager.subscribe(model, self, key_fields)
|
|
return self
|
|
|
|
def generate_key(self, **identifiers) -> str:
|
|
missing = set(self._key_components) - set(identifiers.keys())
|
|
if missing:
|
|
raise ValueError(f"Missing key components: {missing}")
|
|
|
|
key = CacheKey({k: identifiers[k] for k in self._key_components})
|
|
return f"{self.prefix}:{str(key)}"
|
|
|
|
def get(self, creator_func, **identifiers) -> T:
|
|
cache_key = self.generate_key(**identifiers)
|
|
|
|
def creator():
|
|
instance = creator_func(**identifiers)
|
|
return self.to_cache_data(instance)
|
|
|
|
cached_data = self.region.get_or_create(
|
|
cache_key,
|
|
creator,
|
|
should_cache_fn=self.should_cache
|
|
)
|
|
|
|
return self.from_cache_data(cached_data, **identifiers)
|
|
|
|
def invalidate(self, **identifiers):
|
|
cache_key = self.generate_key(**identifiers)
|
|
self.region.delete(cache_key)
|
|
|
|
def invalidate_by_model(self, model: str, **identifiers):
|
|
try:
|
|
self.invalidate(**identifiers)
|
|
except ValueError:
|
|
pass
|
|
|
|
|
|
# Create global invalidation manager
|
|
invalidation_manager = CacheInvalidationManager() |