import hashlib
from functools import wraps
from flask import request, make_response
from redis import Redis
redis = Redis.from_url("redis://localhost:6379/0")
def _make_key(vary_on):
parts = [request.path, request.query_string.decode("utf-8")]
for header in vary_on:
parts.append(f"{header}={request.headers.get(header, '')}")
return "etag:" + "|".join(parts)
def _compute_etag(body):
digest = hashlib.sha256(body).hexdigest()
return f'"{digest}"'
def _not_modified(etag):
resp = make_response("", 304)
resp.headers["ETag"] = etag
resp.headers["Cache-Control"] = "private, max-age=0, must-revalidate"
return resp
def etag_cache(ttl=300, vary_on=None):
vary_on = vary_on or []
def decorator(view):
@wraps(view)
def wrapper(*args, **kwargs):
key = _make_key(vary_on)
client_tag = request.headers.get("If-None-Match")
cached = redis.get(key)
if cached is not None:
stored_tag = cached.decode("utf-8")
if client_tag == stored_tag:
return _not_modified(stored_tag)
resp = make_response(view(*args, **kwargs))
etag = _compute_etag(resp.get_data())
redis.setex(key, ttl, etag)
if client_tag == etag:
return _not_modified(etag)
resp.headers["ETag"] = etag
resp.headers["Cache-Control"] = f"private, max-age={ttl}, must-revalidate"
return resp
return wrapper
return decorator
import time
from flask import Flask, jsonify, request
from etag_cache import etag_cache
app = Flask(__name__)
def _expensive_report(region, window):
time.sleep(2) # stand-in for heavy aggregation / external calls
return {
"region": region,
"window": window,
"total_orders": 48213,
"revenue": 1_204_998.42,
"generated_by": "batch-aggregator-v3",
}
@app.route("/reports/sales")
@etag_cache(ttl=600, vary_on=["Accept"])
def report():
region = request.args.get("region", "global")
window = request.args.get("window", "7d")
data = _expensive_report(region, window)
return jsonify(data)
@app.route("/healthz")
def healthz():
return jsonify(status="ok")
if __name__ == "__main__":
app.run(port=8000)
This snippet shows how an expensive Flask endpoint can be made cheap for repeat callers using HTTP conditional requests. The core idea is that a response body rarely changes between requests, so instead of recomputing and re-sending it every time, the server sends a validator — an ETag — that the client echoes back on its next request via If-None-Match. When the tag still matches, the server answers 304 Not Modified with an empty body, saving both the recomputation and the bandwidth.
In etag_cache.py, the etag_cache decorator wraps a view function. It first builds a stable cache key from the request path and query string via _make_key, then looks in Redis for a previously stored ETag under that key. If a stored tag exists and the incoming If-None-Match header matches it, the view is never called: the decorator short-circuits and returns a bare 304 through _not_modified. This is the fast path and the whole point — the expensive work is skipped entirely on a cache hit.
On a miss, the wrapped view runs, its response is normalized with make_response, and the body is hashed with hashlib.sha256 to derive a content-addressed ETag. That approach means the tag changes if and only if the bytes change, which keeps correctness simple. The tag is stored in Redis with a TTL so stale keys expire, and it is attached to the outgoing response along with Cache-Control. A second identity check compares the freshly computed tag against If-None-Match, covering the case where the Redis entry had expired but the content is in fact unchanged.
In app.py, the decorator sits between Flask's @app.route and the view report, which simulates a slow aggregation with _expensive_report. Because the decorator is transparent, the view stays focused on producing data. Note the trade-offs: weak versus strong ETags matter for range requests, hashing the whole body costs CPU for very large payloads, and the Redis lookup adds a dependency but enables validator sharing across processes. The vary_on argument lets callers include headers like Accept in the key so content negotiation does not serve the wrong variant. This pattern fits read-heavy JSON endpoints where recomputation dominates cost and payloads are moderately sized.
Related snips
module Api
module V1
class UsersController < BaseController
def show
user = User.includes(:profile).find(params[:id])
ETags for conditional requests and caching
require "csv"
class PeopleCsvStream
include Enumerable
HEADERS = %w[id full_name email signed_up_at plan].freeze
Resilient CSV Export as a Streamed Response
Rails.application.configure do
config.after_initialize do
Bullet.enable = true
Bullet.alert = false
Bullet.bullet_logger = true
Bullet.console = true
N+1 query detection with Bullet gem
json.array! @posts do |post|
json.cache! ['v1', post], expires_in: 1.hour do
json.id post.id
json.title post.title
json.excerpt post.excerpt
json.published_at post.published_at
Fragment caching for expensive JSON serialization
import { randomBytes, createHash } from "crypto";
import jwt from "jsonwebtoken";
import { RefreshTokenStore } from "./store";
const ACCESS_SECRET = process.env.ACCESS_SECRET!;
const ACCESS_TTL = "15m";
JWT access + refresh token rotation (conceptual)
package deps
import (
"net"
"net/http"
"time"
HTTP client tuned for production: timeouts, transport, and connection reuse
Share this code
Here's the card — post it anywhere.