Sizing and Capacity Planning
This guide helps you estimate memory requirements, plan for growth, and understand when to scale your Swytch deployment.
Every key stored in Swytch has fixed overhead beyond the key and value bytes:
| Component | Bytes | Notes |
|---|---|---|
| Key storage | len(key) | Your key string |
| Value storage | len(value) | Your value bytes |
| Entry metadata | ~64 | Timestamps, flags, hash, etc. |
| Index entry | ~24 | In-memory hash table slot |
Rule of thumb: Each key-value pair uses len(key) + len(value) + 90 bytes of memory.
Total Memory = (num_keys × 90) + total_key_bytes + total_value_bytes
Example: 10 million keys with average 20-byte keys and 200-byte values:
Memory = (10,000,000 × 90) + (10,000,000 × 20) + (10,000,000 × 200)
= 900MB + 200MB + 2000MB
= 3.1GB
Add 20% headroom for runtime overhead: ~3.7GB recommended maxmemory
Different Redis data types have additional per-element overhead:
| Type | Per-element overhead | Notes |
|---|---|---|
| String | 0 | Stored as raw bytes |
| List | ~16 bytes | Doubly-linked list nodes |
| Hash | ~24 bytes per field | Field name + value + metadata |
| Set | ~16 bytes per member | Hash set entry |
| Sorted Set | ~32 bytes per member | Score + skip list nodes |
| Stream | ~48 bytes per entry | Entry ID + field overhead |
Example: A hash with 100 fields averaging 10-byte names and 50-byte values:
Hash overhead = 100 × (24 + 10 + 50) = 8,400 bytes per hash
Your working set is the subset of data actively accessed within a time window. In tiered storage mode, the L1 cache holds your working set while L2 holds everything.
Use these metrics to understand your working set:
| Metric | Meaning |
|---|---|
swytch_redis_cache_hits_total | L1 (memory) cache hits |
swytch_redis_l2_hits_total | L2 (disk) hits (working set exceeded L1) |
swytch_redis_evictions_total | Keys evicted from L1 |
Working set fits in memory when:
- L2 hit rate is low (<5%)
- Evictions are infrequent
- p99 latency is stable
Working set exceeds memory when:
- L2 hit rate is high (>20%)
- Consistent eviction pressure
- Latency spikes during cache misses
L1 Hit Rate = cache_hits / (cache_hits + l2_hits + l2_misses)
L2 Hit Rate = l2_hits / (l2_hits + l2_misses)
Overall Hit Rate = (cache_hits + l2_hits) / (cache_hits + l2_hits + l2_misses)
Target: L1 hit rate > 90% for latency-sensitive workloads.
| Signal | Action |
|---|---|
| L1 hit rate < 80% | Increase --maxmemory |
| Evictions > 1000/sec sustained | Increase --maxmemory |
| p99 latency increasing | Check L2 hit rate; may need more memory |
| OOM kills | Increase container memory limit |
| Signal | Action |
|---|---|
| CPU utilization > 80% sustained | Add cores or optimize queries |
| Command queue backing up | Check INFO for blocked clients |
| Lua script timeouts | Optimize scripts or add CPU |
| Signal | Action |
|---|---|
| Disk usage > 70% | Add storage or run defragmentation |
| Write latency spikes | Move to faster storage (NVMe) |
| Recovery time too long | Consider data lifecycle policies |
Use this worksheet to plan your deployment:
Current keys: ____________
Average key size: ____________ bytes
Average value size: ____________ bytes
Growth rate (monthly): ____________ %
Current data size = keys × (key_size + value_size + 90)
= ____________ GB
6-month projection = current × (1 + growth_rate)^6
= ____________ GB
| Question | If Yes | If No |
|---|---|---|
| Must data survive restarts? | Persistent mode | In-memory mode |
| Is all data equally important? | Persistent mode | Consider TTLs |
| Can data be regenerated from source? | In-memory mode | Persistent mode |
In-memory mode:
maxmemory = data_size × 1.2 (20% headroom)
Persistent mode:
maxmemory = working_set_size × 1.2
If unknown, start with:
maxmemory = data_size × 0.3 (assume 30% is hot)
disk_size = data_size × 2.5 (growth + defrag headroom)
container_memory = maxmemory + max(512MB, maxmemory × 0.2)
container_cpu = based on expected ops/sec (see Deployment guide)
Set up alerts for proactive scaling:
# Prometheus alerting rules
groups:
- name: swytch-capacity
rules:
- alert: SwytchMemoryPressure
expr: swytch_redis_memory_bytes / swytch_redis_memory_max_bytes > 0.9
for: 5m
labels:
severity: warning
annotations:
summary: "Swytch memory usage above 90%"
- alert: SwytchHighEvictionRate
expr: rate(swytch_redis_evictions_total[5m]) > 1000
for: 10m
labels:
severity: warning
annotations:
summary: "High eviction rate indicates memory pressure"
- alert: SwytchLowHitRate
expr: |
rate(swytch_redis_cache_hits_total[5m]) /
(rate(swytch_redis_cache_hits_total[5m]) + rate(swytch_redis_l2_hits_total[5m]) + rate(swytch_redis_l2_misses_total[5m]) + 0.001) < 0.8
for: 15m
labels:
severity: warning
annotations:
summary: "L1 cache hit rate below 80%"
Note: Monitor disk usage at the OS level using standard tools like df or node_exporter metrics, as Swytch does not
currently expose disk usage metrics.
| Scenario | Recommended maxmemory | Notes |
|---|---|---|
| Small app (<1M keys) | 256MB - 1GB | Start small, scale up |
| Medium app (1-10M keys) | 1GB - 8GB | Monitor working set |
| Large app (10-100M keys) | 8GB - 64GB | Profile access patterns |
| Very large (>100M keys) | 64GB+ | Consider app-level sharding |