Case Study · Caching · 2023 — 2024

Caching — absorbing read load without going stale

Caching layer that absorbs read load with TTL-based invalidation and a database fallback on miss.

Spec Sheet

Role

Full-stack engineer

Timeline

2023 — 2024

Status

In production

Surface

API caching layer

Stack

Node.js · Redis

Data

Upstash Redis · Postgres

01

Problem


Read-heavy endpoints were hitting the primary database on every request, driving up latency and cost. Naive caching risked serving stale data — unacceptable for things like balances and statuses — so the system needed a cache that was fast on the hot path but never silently wrong.

02

Context & constraints


A serverless deployment where database connections are scarce and cold reads are expensive. The cache had to work over HTTP (Upstash) to fit the serverless model, while keeping a clear, predictable invalidation story.

Freshnessstale reads bounded by an explicit TTL
Fallbacka cache miss always falls back to the database
Serverless-fitcache reachable over HTTP, no persistent socket
03

Architecture


Request path

Client

request

Backend API

lookup

Redis

hit?

DB fallback

on miss

Cache set

with TTL

Failure recovery & consistency

Write

data changes

Invalidate

key

Next read

miss

Refill

fresh value

04

Implementation


Cache layer

  • ·Read-through cache with per-key TTL
  • ·Upstash Redis over HTTP (serverless-safe)
  • ·Explicit invalidation on writes

Rate limiting

  • ·Sliding-window counters in Redis
  • ·Per-IP and per-user limits
  • ·429 with retry-after on breach

Sessions

  • ·Session cache keyed by token
  • ·Short TTL, refreshed on activity
  • ·Invalidated on logout / revoke
05

Key decisions


01

Read-through with bounded TTL

Every cached value carries an explicit TTL, so the worst-case staleness is known and small. On a miss the request transparently falls back to the database and repopulates the cache.

02

Invalidate on write, don't just expire

For data that must be fresh, writes actively invalidate the relevant keys rather than waiting for TTL expiry — combining a short staleness ceiling with immediate correctness after changes.

03

HTTP-based Redis for serverless

Upstash's HTTP interface avoids the connection-exhaustion problem that persistent Redis sockets cause in serverless functions, making the cache safe to use from short-lived instances.

06

Tradeoffs


Chose

·Bounded-staleness TTL + active invalidation

·HTTP Redis to fit the serverless model

·Database fallback on every miss

Gave up

·Always-perfectly-fresh reads (TTL allows a small window)

·Lowest-possible latency of a socket connection

·Cache-only operation (DB stays the source of truth)

07

Outcome


↓ load

read traffic absorbed before the database

bounded

staleness capped by explicit per-key TTL

0

connection-exhaustion incidents in serverless