# Redis Memory Accounting (Plan §14.7)
This document validates the Redis memory usage for the Miroir task store when running in HA mode with multiple replicas.
## Redis Keyspace Overview
The Redis backend for Miroir's task store uses the following keyspace patterns:
### Table Keys (Hash + Index Set)
Each of the 14 tables is stored as:
- `miroir:
:` → Hash containing the serialized record
- `miroir::_index` → Set containing all IDs in the table
Tables:
1. `tasks` - Task registry
2. `node_settings_version` - Per-(index, node) settings freshness
3. `aliases` - Single and multi-target aliases
4. `sessions` - Read-your-writes session pins
5. `idempotency_cache` - Write dedup cache
6. `jobs` - Background jobs
7. `leader_lease` - Coordinator lease
8. `canaries` - Canary definitions
9. `canary_runs` - Canary run history
10. `cdc_cursors` - CDC cursors
11. `tenant_map` - API key → tenant mapping
12. `rollover_policies` - ILM rollover policies
13. `search_ui_config` - Search UI configuration
14. `admin_sessions` - Admin UI sessions
### Special-Purpose Keys
#### Rate Limiting (§13.21)
- `miroir:ratelimit:searchui:` - Per-IP search UI rate limit counter (EXPIRE after window)
- `miroir:ratelimit:adminlogin:` - Admin login rate limit counter
- `miroir:ratelimit:backoff:` - Per-IP backoff flag (EXPIRE after backoff duration)
#### CDC Overflow (§13.13)
- `miroir:cdc:overflow:` - CDC overflow buffer (1 GiB per sink default)
#### Scoped Key Rotation (§13.21)
- `miroir:search_ui_scoped_key:` - Current scoped key for an index
- `miroir:search_ui_scoped_key_observed::` - Pod observation marker
#### Job Queue (§14.5)
- `miroir:jobs:enqueued` - List of enqueued job IDs
#### Admin Session Revocation (§13.19)
- `miroir:admin_session:revoked` - Pub/Sub channel for instant logout propagation
## Memory Sizing Formula
### Per-Record Overhead
Redis has the following per-key overhead:
- Key: ~100 bytes (including key length and overhead)
- Value: varies by type
- Hash entry: ~50 bytes per field
- Set entry: ~50 bytes per member
### Estimated Memory per Table
| Table | Avg Record Size | Est. Count | Memory (approx) |
|-------|----------------|------------|-----------------|
| tasks | 500 bytes | 10,000 | ~5 MB |
| node_settings_version | 100 bytes | 500 | ~50 KB |
| aliases | 200 bytes | 100 | ~20 KB |
| sessions | 150 bytes | 1,000 | ~150 KB |
| idempotency_cache | 1 KB | 10,000 | ~10 MB (TTL 1h) |
| jobs | 400 bytes | 1,000 | ~400 KB |
| leader_lease | 200 bytes | 1 | ~200 bytes |
| canaries | 300 bytes | 50 | ~15 KB |
| canary_runs | 200 bytes | 10,000 | ~2 MB |
| cdc_cursors | 100 bytes | 100 | ~10 KB |
| tenant_map | 500 bytes | 100 | ~50 KB |
| rollover_policies | 300 bytes | 50 | ~15 KB |
| search_ui_config | 2 KB | 100 | ~200 KB |
| admin_sessions | 150 bytes | 100 | ~15 KB |
| **Subtotal** | - | - | **~18 MB** |
### Rate Limiting Memory
Search UI rate limiter (§13.21):
- Per-IP bucket: ~100 bytes
- Active IPs: ~10,000 (production estimate)
- TTL: 60 seconds (configurable via `search_ui.rate_limit.redis_ttl_s`)
- **Memory: ~1 MB** (steady state, with TTL)
Admin login rate limiter:
- Per-IP bucket: ~100 bytes
- Active IPs: ~100
- TTL: 300 seconds (5 minutes)
- **Memory: ~10 KB**
Backoff markers:
- Per-IP backoff: ~100 bytes
- Active backoffs: ~100
- TTL: variable (typically 60-300 seconds)
- **Memory: ~10 KB**
### CDC Overflow Memory
- Per-sink buffer: 1 GiB (configurable via `cdc.buffer.redis_bytes`)
- Typical sinks: 1-3
- **Memory: 1-3 GiB** (only when CDC is enabled and overflow occurs)
### Scoped Key Rotation Memory
- Per-index key: ~200 bytes
- Indices: ~100
- Per-pod observation markers: 100 bytes × (pods × indices)
- Pods (HPA max): 10
- **Memory: ~20 KB + 100 KB = ~120 KB**
### Total Estimated Memory Usage
| Component | Memory |
|-----------|--------|
| Tables (steady state) | ~18 MB |
| Rate limiting | ~1 MB |
| Scoped key rotation | ~120 KB |
| **Subtotal (without CDC overflow)** | **~19 MB** |
| CDC overflow (per sink) | 1 GiB (optional) |
### Production Sizing Recommendation
For a production deployment with CDC disabled:
- **Minimum Redis memory: 64 MB** (provides headroom for bursts)
- **Recommended Redis memory: 128-256 MB** (comfortable headroom)
For a production deployment with CDC enabled:
- **Per-sink buffer: 1 GiB** (configurable)
- **Minimum Redis memory: 1 GiB + 64 MB per sink**
- **Recommended Redis memory: 2 GiB** (for single-sink deployments)
## Validation Script
The following script can be used to validate Redis memory usage in a running deployment:
```bash
# Connect to Redis and get memory info
redis-cli INFO memory
# Get memory usage for all Miroir keys
redis-cli --scan --pattern 'miroir:*' | xargs redis-cli MEMORY USAGE
# Get total memory used by Miroir
redis-cli --scan --pattern 'miroir:*' | xargs redis-cli MEMORY USAGE | awk '{sum+=$1} END {print sum}'
```
## Helm Chart Defaults
The Helm chart (see `charts/miroir/values.yaml`) sets the following defaults:
```yaml
redis:
resources:
limits:
cpu: 500m
memory: 256Mi
requests:
cpu: 100m
memory: 128Mi
```
These defaults are appropriate for deployments without CDC overflow. For CDC-enabled deployments, increase the memory limit to at least 2 GiB.
## References
- Plan §4: Task store schema
- Plan §13.13: CDC cursors and overflow
- Plan §13.19: Admin sessions and Pub/Sub revocation
- Plan §13.21: Search UI rate limiting and scoped key rotation
- Plan §14.7: Deployment sizing matrix