Skip to Content
FeaturesModulesMCP (AI Agents)Redis & Caching

Redis & Caching

django_mcp uses Django’s default cache backend (django.core.cache.cache) for three independent purposes. All three work with any Django cache backend, but Redis is assumed in production because QueryBudget depends on pipeline operations.

Cache key summary

PrefixScopeTTLPurpose
mcp:query:<hash>Global5 minutesQuery result cache
mcp:budget:{session_id}:*Per session1 hour (sliding)Resource budget tracking
mcp_chat:{user_id}:{session_id}Per user + session24 hours (sliding)Chat conversation history

1. Query cache (cache.pyQueryCache)

Caches tool query results to avoid hitting the database repeatedly when the LLM calls the same tool with identical arguments in one session.

Key format

mcp:query:<16-char-sha256-hex>

The key is a SHA-256 hash of {sql, params, tenant_id} serialized as JSON, truncated to 16 hex characters.

Behavior

  • TTL: 5 minutes (configurable per QueryCache(ttl=N) instance).
  • Stampede prevention: uses cache.add() instead of cache.set() — only writes if the key does not already exist, so concurrent requests for the same query do not trigger multiple DB hits.
  • Invalidation: cache.invalidate(sql, params, tenant_id) deletes a specific key. cache.clear_all() uses Redis KEYS mcp:query:* — avoid this in the production hot path.

Usage in tools

QueryCache is instantiated per-request inside tool implementations. It is not wired automatically — each tool that wants caching creates its own instance:

from django_cfg.modules.django_mcp.cache import QueryCache cache = QueryCache(ttl=300) result = cache.get(sql, params, tenant_id="default") if result is None: result = run_query(sql, params) cache.set(sql, params, tenant_id="default", value=result)

2. Query budget (budget.pyQueryBudget)

Tracks per-session resource consumption and enforces limits. Prevents a single agent session from hammering the database or burning excessive LLM tokens on tool calls.

Key format

mcp:budget:{session_id}:time # float — total execution seconds mcp:budget:{session_id}:rows # int — total rows fetched mcp:budget:{session_id}:cost # float — total EXPLAIN cost units mcp:budget:{session_id}:queries # int — total number of queries

All four keys share the same session scope and TTL (1 hour, sliding — reset on every record_usage() call via EXPIRE).

Default limits

MetricDefault limit
Total execution time60 seconds
Total rows fetched10,000 rows
Total EXPLAIN cost500,000 units
Total queries200 queries

Behavior

record_usage(execution_ms, rows, cost) runs a Redis pipeline:

  1. INCRBYFLOAT on time and cost.
  2. INCR on rows and queries.
  3. EXPIRE all four keys (sliding TTL).
  4. Reads results and compares against limits.
  5. Returns True (within budget) or False (exceeded).

Fail-open: if Redis is unavailable during the pipeline, record_usage() returns True and lets the query proceed. This keeps the system operational when Redis is temporarily down, but means budget tracking is silently skipped. For stricter environments, you can override this to fail-closed.

Usage

from django_cfg.modules.django_mcp.budget import QueryBudget budget = QueryBudget(session_id="agent-session-abc123") ok = budget.record_usage(execution_ms=250, rows=150, cost=1200.5) if not ok: return "Error: session resource budget exceeded. Try a more specific query."

3. Chat history (chat/store.pyRedisMCPChatStore)

Status: planned / in progress. The store class exists but is not yet wired into the streaming view. Currently all chat history is in-memory and resets on each request.

Persists the conversation history for the admin chat agent between page reloads.

Key format

mcp_chat:{user_id}:{session_id}

Stored as a JSON list of {"role": ..., "content": ...} dicts.

Behavior

  • Storage order: newest-first (prepend on write, trim to max_messages=50).
  • Read order: reversed to chronological (oldest-first) before returning to the agent.
  • TTL: 24 hours, sliding — reset on every read and write.
  • Per-user isolation: user_id is always part of the key, so different admin users have separate histories even with the same session_id.

Usage

from django_cfg.modules.django_mcp.chat.store import RedisMCPChatStore store = RedisMCPChatStore(user_id=request.user.pk, session_id="admin-chat") # Load history before running the agent history = store.get_history() # [{"role": "user", "content": "..."}, ...] # Persist messages after stream completes store.append_message("user", user_message) store.append_message("assistant", assistant_reply) # Clear the session (user requests a fresh start) store.clear()

Redis not configured?

All three features use django.core.cache.cache, so they fall back gracefully to whatever backend is configured:

BackendEffect
LocMemCache (Django default in dev)Works for single-process; resets on server restart.
DummyCacheEverything is a no-op — queries never cached, budgets never tracked, history always lost.
Redis (production default)Full functionality. Required for QueryBudget pipelines to work correctly.

The QueryBudget.record_usage() method catches all exceptions and returns True (fail-open) when Redis is unavailable, so the rest of the system continues working.

Last updated on