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.

Backend Engineerrediselasticachecaching

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

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

Redis Caching - The Cache-Aside PatternBackend projectCache LLM Responses With RedisBackend projectFix a Redis Connection Refused ErrorBackend projectBackend roadmapStep by step to hiredBackend interview questionsSTAR answersAll Backend projectsProjects hub

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 →