govbridge-api / middleware /memory_guard.py
harshrawat18's picture
feat(sprint-18-19): bidirectional translation + neuro-symbolic eligibility bridge
60d1026
Raw
History Blame Contribute Delete
2.07 kB
"""
GovBridge India — Memory Guard Middleware (Sprint 18)
Prevents progressive RAM bloat by:
1. Running gc.collect() after every request
2. Logging RSS memory usage for monitoring
3. Providing a /debug/memory endpoint for live inspection
Target: Hugging Face Spaces (CPU Basic: 2 vCPU, 16GB RAM)
"""
import gc
import os
import psutil
from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import Response, JSONResponse
def get_memory_mb() -> float:
"""Get current process RSS in MB."""
process = psutil.Process(os.getpid())
return process.memory_info().rss / (1024 * 1024)
class MemoryGuardMiddleware(BaseHTTPMiddleware):
"""
Post-request garbage collection middleware.
CRITICAL DESIGN: Python's default GC thresholds (700, 10, 10) are
tuned for short-lived scripts, not long-running ML servers. PyTorch
tensors create circular references that the generational GC doesn't
collect promptly. Forcing gc.collect() after every request ensures
translation tensors are freed immediately.
Performance impact: gc.collect() takes 1-5ms on this workload.
Negligible vs. the 2-4s translation + inference latency.
"""
# Threshold in MB. If exceeded, log a warning.
MEMORY_WARNING_THRESHOLD_MB = 12_000 # 12GB of 16GB
async def dispatch(self, request: Request, call_next):
response = await call_next(request)
# Force garbage collection after every request
collected = gc.collect()
# Log memory state for monitoring (only on heavy endpoints)
if request.url.path in ("/api/rag/query", "/webhook/whatsapp"):
mem_mb = get_memory_mb()
if mem_mb > self.MEMORY_WARNING_THRESHOLD_MB:
print(f"🚨 MEMORY WARNING: {mem_mb:.0f}MB RSS (threshold: {self.MEMORY_WARNING_THRESHOLD_MB}MB)")
elif collected > 0:
print(f"🧹 GC freed {collected} objects | RSS: {mem_mb:.0f}MB")
return response