Версия: 1.0.0
Дата: 2025-11-10
L1: Request Cache (In-Memory) 0.1ms
↓ miss
L2: Redis (Application) 1-5ms
↓ miss
L3: Database (PostgreSQL) 10-50ms
cifra:{tenant_id}:{entity}:{id}:{version}
Examples:
- cifra:acme:Contact:uuid-123:v1
- cifra:acme:Deal:list:stage=won:page=1:v2
- cifra:global:settings:theme:v1
CACHE_TTL = {
'User': 300, # 5 min (часто меняется)
'Contact': 600, # 10 min
'Deal': 600, # 10 min
'Product': 3600, # 1 hour (редко меняется)
'Category': 86400, # 24 hours (почти не меняется)
'Settings': 86400, # 24 hours
}
from cifra.cache import cached
@cached(ttl=600, key_prefix="contact")
async def get_contact(db, contact_id):
"""Get contact with caching"""
return await db.query(Contact).get(contact_id)
# Usage
contact = await get_contact(db, uuid) # DB query
contact = await get_contact(db, uuid) # From cache!
# Automatic on update
class Contact(Base):
@event.listens_for(Contact, 'after_update')
def invalidate_cache(mapper, connection, target):
cache.delete(f"contact:*{target.id}*")
# Manual
await cache.delete("contact:uuid-123")
await cache.delete_pattern("contact:*")
async def get_user(user_id):
# Try cache
user = await cache.get(f"user:{user_id}")
if user:
return user
# Query DB
user = await db.query(User).get(user_id)
# Save to cache
await cache.set(f"user:{user_id}", user, ttl=600)
return user
async def update_user(user_id, data):
# Update DB
user = await db.query(User).get(user_id)
user.update(data)
await db.commit()
# Update cache
await cache.set(f"user:{user_id}", user, ttl=600)
return user
async def update_user(user_id, data):
# Update cache immediately
user = await cache.get(f"user:{user_id}")
user.update(data)
await cache.set(f"user:{user_id}", user)
# Schedule DB update (async)
await task_queue.enqueue('update_user_in_db', user_id, data)
return user
# At application startup
async def warm_cache():
# Load all categories
categories = await db.query(Category).all()
for cat in categories:
await cache.set(f"category:{cat.id}", cat, ttl=86400)
# Load settings
settings = await db.query(Settings).all()
await cache.set("settings:all", settings, ttl=86400)
async def get_with_lock(key):
# Try cache
value = await cache.get(key)
if value:
return value
# Try get lock
lock_key = f"{key}:lock"
acquired = await cache.set(lock_key, 1, nx=True, ex=10)
if acquired:
# First one - query DB
value = await db.query(...)
await cache.set(key, value, ex=600)
await cache.delete(lock_key)
return value
else:
# Someone else querying - wait
await asyncio.sleep(0.1)
return await get_with_lock(key)
# Cache hit rate
cache_hits = redis.get("stats:cache:hits")
cache_misses = redis.get("stats:cache:misses")
hit_rate = cache_hits / (cache_hits + cache_misses)
# Target: > 80% hit rate
Документация: https://docs.cifra.io/caching