-
Notifications
You must be signed in to change notification settings - Fork 2
Redis Integration
Value Proposition Implement distributed rate limiting to manage multi-agent load. Integrating Redis provides resilient high availability across the ecosystem. Read the full value proposition.
Understand the Redis architecture to ensure robust system design. Redis serves a single, focused purpose in the mysql-mcp architecture: distributed fixed-window rate limiting for Code Mode (mysql_execute_code) and the HTTP Transport layer. It establishes a reliable, highly available operational boundary, ensuring consistent performance even under heavy multi-agent workloads. It is an entirely optional dependency — the server operates identically without it, falling back to a local in-memory rate limiter.
| Aspect | Detail |
|---|---|
| Client library | node-redis client |
| Connection model | Single client, disableOfflineQueue: true
|
| Key pattern | codemode:rl:${clientId} |
| Data structures | Simple strings holding integer counters |
| Pub/Sub / Streams | Not used |
Note
Redis is not used for schema caching. The schema metadata cache (SchemaManager) is implemented as an in-memory Map within each server process.
The CodeModeSecurityManager establishes a single Redis connection on startup if REDIS_URL is provided. The connection is configured with disableOfflineQueue: true, which means commands issued while disconnected are immediately rejected rather than buffered — this is critical for the fallback mechanism to engage instantly.
┌──────────────────────────────────────────────────────┐
│ mysql-mcp Server │
│ │
│ ┌─────────────────────┐ ┌─────────────────────┐ │
│ │ CodeModeSecurityMgr │───▶│ Redis (optional) │ │
│ │ │ │ Port: 6379 │ │
│ │ REDIS_URL set? │ └─────────────────────┘ │
│ │ ├─ Yes: Redis RL │ │
│ │ └─ No: Memory RL │ │
│ └─────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────┐ │
│ │ In-Memory Map │ ◀── Fallback (always │
│ │ (rate limiter) │ available) │
│ └─────────────────────┘ │
└──────────────────────────────────────────────────────┘
If the Redis client throws an error during a rate limit check (network timeout, connection refused, etc.), the system:
- Logs the failure as a structured warning
-
Falls through to the in-memory
Map-based rate limiter - Continues enforcing limits locally without interruption
-
Emits a
mysql_mcp_redis_fallback_to_memory_totalmetric for monitoring
Recovery is automatic — subsequent calls will attempt Redis again if the client reconnects.
The rate limiter uses a fixed window algorithm implemented as an atomic Lua script executed via EVAL:
local current = redis.call('INCR', KEYS[1])
if current == 1 then
redis.call('PEXPIRE', KEYS[1], ARGV[1])
end
return currentHow it works:
-
INCRatomically increments the counter for the client's key - On the first increment (counter == 1), a TTL is set via
PEXPIRE(millisecond precision) - The returned count is compared against configurable limits (e.g., 60 executions per minute)
- When the TTL expires, Redis automatically evicts the key, resetting the window
This approach is race-condition-free because the entire script executes atomically within Redis.
Code Mode Rate Limiting:
codemode:rl:${clientId}
-
Prefix:
codemode:rl:— identifies this as a Code Mode rate limit key -
Suffix:
${clientId}— unique per MCP client connection -
TTL: Configurable via
windowMs(default: 60,000ms / 1 minute)
HTTP Transport Rate Limiting:
http:rl:${ipAddress}
-
Prefix:
http:rl:— identifies this as an HTTP transport rate limit key -
Suffix:
${ipAddress}— client IP address -
TTL: Configurable via
windowMs(default: 60,000ms / 1 minute)
Each client gets an independent counter. Under MCP streamable HTTP transport (NodeStreamableHTTPServerTransport) with multiple agents, Code Mode execution limits (CODEMODE_RATE_LIMIT_MAX) are enforced globally per client session (clientId) via atomic Redis Lua scripts. In contrast, HTTP transport rate limits (MCP_RATE_LIMIT_MAX) are strictly enforced globally per IP address. An agent cannot circumvent limits by reconnecting.
| Variable | Default | Description |
|---|---|---|
REDIS_URL |
(none) | Redis connection URL (e.g., redis://localhost:6379). If unset, rate limiting uses in-memory only. |
CODEMODE_RATE_LIMIT_MAX |
60 |
Maximum Code Mode executions per client per window |
MCP_RATE_LIMIT_MAX |
100 |
Maximum HTTP requests per IP address per window (HTTP Transport only) |
Important
The stdio transport is not subject to HTTP transport rate limits (MCP_RATE_LIMIT_MAX). However, Code Mode execution (mysql_execute_code) is always subject to CODEMODE_RATE_LIMIT_MAX regardless of transport — enforced globally via Redis or locally in-memory.
The Redis client is configured with disableOfflineQueue: true. This means:
- If the connection drops, commands are not queued — they throw immediately
- The catch block in
checkRateLimit()traps the error and falls through to the in-memory path
| Scenario | Latency | Notes |
|---|---|---|
| Redis healthy | Sub-millisecond | Local network Lua EVAL |
| Redis unreachable | ~0ms penalty | Immediate throw due to disableOfflineQueue
|
| In-memory fallback | Sub-millisecond | Simple Map lookup |
The node-redis client includes automatic reconnection. When the connection is restored:
- The
readyevent fires - Subsequent
checkRateLimit()calls will use Redis again - No manual intervention is required
Redis integration tests use a carefully designed isolation pattern to prevent cross-test contamination.
Each test generates a unique namespace:
const prefix = getRedisTestPrefix(suiteName);This ensures parallel test runs never interfere with each other.
The test utilities explicitly prohibit FLUSHDB and FLUSHALL to prevent wiping real data. Instead, cleanup uses targeted key deletion:
// Scan for keys matching the test prefix
const keys = await redis.keys(`${prefix}*`);
if (keys.length > 0) {
const multi = redis.multi();
for (const key of keys) {
multi.del(key);
}
await multi.exec();
}A waitForKeyExpiry polling utility validates the Lua script's timeout logic in integration tests by repeatedly checking EXISTS until the key expires.
Tip
See src/__tests__/helpers/redis-test-utils.ts for the complete test harness implementation. The test ecosystem comprehensively benchmarks code mode VM sandbox performance against these rate limiting capabilities.
The following application-level Redis metrics are exported via the /metrics endpoint:
Note
Audit Trail: Rate-limiting rejection events are also captured in the file-based audit trail at AUDIT_LOG_PATH for comprehensive security analysis.
| Metric | Type | Description |
|---|---|---|
mysql_mcp_redis_rate_limit_exceeded_total |
counter | Code Mode rate limit rejections |
mysql_mcp_redis_fallback_to_memory_total |
counter | Fallback events to in-memory rate limiter |
mysql_mcp_redis_connected |
gauge | Whether Redis is currently connected (0/1) |
mysql_mcp_redis_lua_eval_latency_p95_ms |
gauge | P95 latency of the rate limit Lua EVAL |
Sync the following JSON Dashboards via your Datadog provisioning tool (e.g., pup, Terraform, or API) to ensure complete visibility:
- AI Efficiency
- Token & Tool Metrics
- MySQL Cluster Telemetry
- Redis Telemetry
- Agent Execution Telemetry
The Redis Telemetry dashboard provides infrastructure-level monitoring including:
- Memory usage and fragmentation ratio
- Keyspace metrics (total keys, expires, evictions)
- Client connections (connected, blocked, rejected)
- Commands per second and slowlog
- Cache hit/miss rate
- Network I/O throughput
- Replication health
OpenTelemetry Tracing Rules: To integrate successfully, ensure your OpenTelemetry configuration adheres to the following rules:
- Utilize
gen_ai.*semantic telemetry conventions. - Configure the OTLP exporter securely.
- Ensure standard
traceparentheaders are propagated across all service boundaries. - Utilize a batch span processor for optimal export performance.
See Observability for dashboard setup instructions.
| Metric | Why | Alert Threshold |
|---|---|---|
mysql_mcp_redis_fallback_to_memory_total |
Indicates Redis connectivity issues | Any increment |
redis.mem.fragmentation_ratio |
High fragmentation wastes memory | > 1.5 |
redis.keys.evicted |
Evictions mean memory pressure | Any non-zero |
redis.clients.blocked |
Blocked clients indicate contention | > 0 sustained |
redis.net.rejected |
Connection limit reached | Any non-zero |
Symptom: Warning logs: Redis rate limit error, falling back to memory
Resolution:
- Verify Redis is running:
docker ps | grep redis - Test connectivity:
redis-cli -h <host> -p 6379 ping - Check
REDIS_URLenvironment variable - The server continues to function via in-memory fallback — no action is strictly required
Symptom: redis.keys.evicted metric is non-zero
Resolution:
- Check
maxmemoryconfiguration - Rate limit keys are tiny (integer counters) and short-lived (60s TTL) — evictions should not occur under normal load
- If evictions persist, another workload may be consuming Redis memory
Symptom: Different agents can exceed the global rate limit
Resolution:
- Verify all server instances use the same
REDIS_URL - Check that
CODEMODE_RATE_LIMIT_MAXis consistent across instances - Confirm Redis is not in cluster mode (the Lua script requires all keys on one node)
- Code Mode - Code Mode API documentation
- Performance Tuning - Connection pooling and rate limiting guidance
- Configuration - Environment variable reference
- Observability - Telemetry and monitoring setup
- Test Ecosystem - Infrastructure setup and testing
Unlock autonomous database orchestration with an enterprise-grade MySQL MCP server. Featuring blazing-fast sandboxed Code Mode, uncompromising schema enforcement, and seamless ecosystem integrations to power secure, intelligent AI workflows.
- Installation
- Configuration
- Architecture
- HTTP Transport
- Tool Filtering
- Code Mode
- Tools
- Prompts
- Resources
- Observability & Telemetry