How to Cache a Slow Endpoint With AWS ElastiCache (Redis)
A /search endpoint that hits the database on every call costs about 1 second per request - the same query ten times is ten full DB hits. The fix is the Redis cache-aside pattern: GET the cache first, fall back to the database on a miss, and SETEX the result with a 60-second TTL. ElastiCache in AWS is that same Redis, run as a managed service. Here is how to wire it up.
What ElastiCache in AWS actually is
Amazon ElastiCache is a managed in-memory cache - you pick the Redis (or Valkey/
Memcached) engine and AWS runs the nodes, failover, backups, and patching. The key
thing for your application code: ElastiCache speaks the exact same Redis protocol as
a local Redis. The only real difference is the connection endpoint - localhost:6379
in development becomes something like
my-cache.abc123.ng.0001.use1.cache.amazonaws.com:6379 in production. The caching
logic you write does not change. So the way to learn ElastiCache is to build the
cache-aside pattern against Redis, then point it at the managed endpoint.
The problem: no cache on a hot read path
Here is a Flask /search endpoint that goes straight to a slow query on every call.
Each slow_search() costs about a second, and there is a counter so you can see the
DB hits:
@app.route("/search")
def search():
q = request.args.get("q", "")
# no cache - every call hits the slow DB
result = slow_search(q)
return jsonify(result)
Ten requests for ?q=ssd is ten one-second DB hits. Everything after the first is
wasted work, because the answer did not change.
The fix: cache-aside with Redis / ElastiCache
Cache-aside means the application manages the cache directly: read it first, and only
touch the database on a miss. Connect a Redis client (this is the same client whether
you point it at localhost or an ElastiCache endpoint), then wrap the query:
import json, time
import redis
from flask import Flask, request, jsonify
app = Flask(__name__)
_r = redis.Redis(host="localhost", port=6379, db=0, decode_responses=True)
@app.route("/search")
def search():
q = request.args.get("q", "")
key = f"search:{q}"
cached = _r.get(key) # 1. read cache first
if cached:
return jsonify(json.loads(cached)) # hit -> no DB
result = slow_search(q) # 2. miss -> hit the DB
_r.setex(key, 60, json.dumps(result)) # 3. store with a 60s TTL
return jsonify(result)
Three moves: GET search:<q> from Redis, return it on a hit, and on a miss run the
query and SETEX the JSON with a 60-second TTL. SETEX is SET plus an expiry in one
command, so the key self-expires and stale data heals on its own. After this, ten
identical /search?q=test requests cause exactly one DB hit - the other nine are
served from cache in a single Redis round-trip.
Verify the cache is working
Restart the app, fire the same query ten times, and read the DB-hit counter:
bash /workspace/run.sh
for i in $(seq 10); do curl -fsS 'http://localhost:5001/search?q=test' > /dev/null; done
curl -fsS http://localhost:5001/counter # -> {"db_hits": 1}
redis-cli keys '*' # -> search:test
If db_hits is 1, the cache-aside layer is doing its job.
Moving it to ElastiCache
To run the same code against ElastiCache in AWS, only the connection changes. Read the endpoint from the environment so dev and prod share one code path:
import os
_r = redis.Redis(
host=os.environ.get("REDIS_HOST", "localhost"),
port=int(os.environ.get("REDIS_PORT", 6379)),
db=0,
decode_responses=True,
ssl=os.environ.get("REDIS_TLS") == "1", # ElastiCache in-transit encryption
)
Set REDIS_HOST to the ElastiCache primary (or configuration) endpoint. Put the cache
in the same VPC as your app and open port 6379 in its security group only to your app's
security group. Everything above - the keys, the TTL, the cache-aside flow - stays
identical.
Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.
Try it in a workspace →What you'll practice
- Wrapping a slow read endpoint in the Redis cache-aside pattern (GET, miss, SETEX)
- Choosing a TTL and key naming scheme so repeat queries skip the database
- Pointing the same Redis client at a managed AWS ElastiCache endpoint via env config
FAQ
What is ElastiCache in AWS?
Amazon ElastiCache is a managed in-memory cache service that runs Redis, Valkey, or Memcached for you - AWS handles the nodes, failover, patching, and backups. Because it speaks the standard Redis protocol, your application code is identical to using a local Redis; only the connection endpoint changes.
How do I connect to AWS ElastiCache from Python?
Use the standard redis-py client and point its host at the ElastiCache primary or configuration endpoint, for example: redis.Redis(host="my-cache.abc123.use1.cache.amazonaws.com", port=6379). Enable ssl=True if the cluster has in-transit encryption, and make sure your app runs in the same VPC with the cache security group allowing port 6379 from the app.
What is the cache-aside pattern with Redis?
The application reads Redis first; on a hit it returns the cached value with no database call, and on a miss it queries the database, stores the result with SETEX and a TTL, then returns it. It keeps the database as the source of truth and gives repeat reads a sub-millisecond Redis lookup instead of a slow query.
Is ElastiCache the same as Redis?
ElastiCache is AWS running Redis (or Valkey/Memcached) as a managed service, not a different technology. The Redis commands, clients, and patterns like cache-aside work exactly the same - the difference is that AWS operates the infrastructure and you connect over a managed endpoint instead of localhost.
Keep learning
Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.
Open the workspace →