Amazon ElastiCache

Updated 5 min read index source
On this page13
  1. What ElastiCache gives you
  2. Deployment topologies
  3. Endpoints
  4. Versioning and feature flags
  5. Sizing
  6. Persistence options
  7. Connection patterns for Python
  8. Auth and encryption
  9. Cache-aside is the dominant pattern
  10. Common gotchas
  11. Cost optimization
  12. Common interview pattern: sessions / rate limit
  13. Interview angle

Amazon ElastiCache

Engine-level Redis depth (data structures, persistence, clustering internals, distributed locks, cache-stampede mitigation) lives in Redis. Operational depth (failover, Serverless, parameter groups) is in ElastiCache — Failover, Serverless, and Operations. This file is the AWS-managed-service overview.

Managed Redis / Memcached. Saves you from operating a cache cluster. Two engines:

  • ElastiCache for Redis / Valkey — feature-rich; pub/sub, sorted sets, persistence, streams.
  • ElastiCache for Memcached — multi-threaded simple KV; rarely the right choice in 2024+.

Almost always: Redis (or Valkey, the open-source AWS-backed fork after Redis Inc.’s license change).

What ElastiCache gives you

  • Provisioning, patching, monitoring.
  • Multi-AZ replication with automatic failover.
  • Backup/restore (RDB snapshots).
  • Encryption in transit (TLS) and at rest.
  • IAM-based authentication.

For Redis fundamentals — data structures, eviction, caching patterns — see Redis.

Deployment topologies

Single node

A single primary, no replica. Cheap, no HA. Use only for dev/staging.

Replication group (cluster mode disabled)

One primary + up to 5 replicas. Automatic failover. Single primary — write throughput limited by one node. Reads can scale across replicas.

Cluster mode enabled

Sharded. Data partitioned across N “node groups” (shards), each with primary + replicas. Hash slots distribute keys. Use when you outgrow a single primary’s write throughput.

python
# Cluster mode requires a cluster-aware client
from redis.cluster import RedisCluster
r = RedisCluster(host="prod.xxx.use1.cache.amazonaws.com", port=6379, ssl=True)

Trade-off: cluster mode complicates multi-key operations. MGET / pipelines across multiple slots need extra logic; transactions can’t span slots.

Endpoints

Endpoint Purpose
Primary writes
Reader round-robin across replicas (cluster-mode-disabled)
Configuration cluster topology discovery (cluster-mode-enabled)
Node direct to a specific node
python
# cluster-mode-disabled — separate read/write endpoints
write = redis.Redis(host="prod.xxx.use1.cache.amazonaws.com", port=6379, ssl=True)
read  = redis.Redis(host="prod-ro.xxx.use1.cache.amazonaws.com", port=6379, ssl=True)

Versioning and feature flags

ElastiCache lags upstream Redis by months. Check the version map before relying on a specific feature (Redis Streams, JSON, RedisGraph, etc.).

Newer choice: ElastiCache Serverless (2023+). No node sizing — usage-based pricing per byte stored + per request. Good for unpredictable load.

Sizing

Memory is king. Watch:

  • BytesUsedForCache — actual data size.
  • DatabaseMemoryUsagePercentage — running close to 100% leads to evictions.
  • Evictions — keys being dropped because the cache is full.
  • CurrConnections — Redis has a default limit of 65k; each app pod opens N.

CPU rarely matters except for Lua scripts or KEYS * in production (don’t).

Persistence options

  • RDB snapshots — point-in-time dump. Daily by default; restore creates a fresh cluster from a snapshot.
  • AOF — append-only log of every write. ElastiCache exposes limited AOF — usually you’d use snapshots.

If your cache is only a cache (rebuildable from source of truth), don’t persist. If it’s a primary store (rate-limit counters, sessions), persist + replicate + back up.

Connection patterns for Python

python
import redis
from redis.connection import SSLConnection

# Cluster-mode-disabled
pool = redis.ConnectionPool(
    host="prod.xxx.use1.cache.amazonaws.com",
    port=6379,
    connection_class=SSLConnection,
    max_connections=50,
    decode_responses=True,
)
r = redis.Redis(connection_pool=pool)

For cluster mode, use redis.cluster.RedisCluster (sync) or redis.asyncio.cluster.RedisCluster (async, redis-py 5+).

Pool sizing on k8s

N pods × max_connections per pod = total. ElastiCache caps at 65k. With 100 pods at 100 connections each you’re at 10k — fine. With 10000 lambdas? You need an in-front proxy or fewer connections per worker.

Auth and encryption

bash
aws elasticache create-replication-group \
  --replication-group-id orders \
  --engine redis \
  --transit-encryption-enabled \
  --at-rest-encryption-enabled \
  --auth-token "long-random-string" \
  ...

Client supplies the auth token as password. IAM auth (newer) replaces shared tokens with short-lived IAM-derived tokens — same model as RDS IAM auth.

Cache-aside is the dominant pattern

python
def get_user(user_id: int) -> User:
    key = f"user:{user_id}"
    raw = r.get(key)
    if raw:
        return User.parse_raw(raw)
    user = db.query(User).get(user_id)
    if user:
        r.setex(key, 3600, user.json())  # 1h TTL
    return user

Plus invalidation on write:

python
def update_user(user_id: int, **fields):
    db.update_user(user_id, **fields)
    r.delete(f"user:{user_id}")  # invalidate

Caveats:

  • Cache stampede when a hot key expires — see Redis.
  • Stale on race — concurrent update + read can re-cache stale data. Mitigate with write-through or short TTLs.

Common gotchas

  • KEYS * in production. Blocks the single-threaded server. Use SCAN.
  • Hot key (sharded mode). All traffic for one key hits one shard. Replicate the value to N suffixed keys.
  • TLS without ssl=True in client. Connections refused. Easy to miss in dev → prod transition.
  • Memcached vs Redis interchangeability. Memcached is just a key/value cache — no pub/sub, no sorted sets, no streams, no persistence. Don’t pick it unless you know why.
  • Default maxmemory-policy: noeviction in ElastiCache. Hitting the limit returns errors instead of evicting. Set to allkeys-lru for a generic cache.
  • Snapshot restore creates a NEW cluster. No in-place rollback.

Cost optimization

  • Reserved nodes for steady workloads (~50% off).
  • Right-size memorycache.r6g.large (13GB) costs 4× cache.t4g.small (1.5GB); a too-large instance with 10% memory used is waste.
  • Serverless for spiky / dev / staging — pay per byte + request.
  • Move to Valkey — same engine post-fork, AWS bills it slightly differently; some price savings appearing.

Common interview pattern: sessions / rate limit

python
# Rate limit: max 100 requests per minute per user
def check_rate(user_id: int) -> bool:
    key = f"rate:{user_id}:{int(time.time() // 60)}"
    count = r.incr(key)
    if count == 1:
        r.expire(key, 60)
    return count <= 100
python
# Session token
r.setex(f"sess:{token}", 3600, json.dumps(session_data))

Interview angle 6

  • “ElastiCache Redis vs Memcached?” — Memcached is a simple multi-threaded KV; no persistence, no replication, no rich types. Redis is feature-rich (sorted sets, streams, pub/sub, persistence, replication). Pick Redis unless you know your workload is hot-CPU-bound on simple KV with no need for richness.
  • “Cluster mode enabled vs disabled?” — disabled: single primary + replicas; reads scale, writes don’t. Enabled: sharded across N node groups; writes scale; multi-key ops constrained to same hash slot.
  • “How do you handle a cache stampede?” — single-flight (one request rebuilds, others wait), probabilistic early expiration (refresh near TTL), or pre-warm. Avoid: thousands of requests hitting the DB simultaneously when one hot key expires.
  • “What happens if ElastiCache fails?” — depends on your app design. As a pure cache: DB takes the load, latency spikes. As a primary store (sessions, rate limits): partial outage. Plan for: short TTLs, soft-fail on cache errors, sufficient DB capacity to absorb cache loss.
  • “How do you authenticate to ElastiCache?” — AUTH token (shared password), or IAM auth (newer, short-lived tokens via SDK). Both with TLS in transit; at-rest encryption is a checkbox.
  • “Serverless ElastiCache — when?” — unpredictable / spiky workloads (you’d over-provision otherwise); dev/staging (avoid paying for idle nodes); apps that scale fast (no manual capacity changes needed).

Contents 1