Skip to content

Server-side caching

Technique · Chapter 10

  • Caching = copying data that may be needed again into fast storage so later access is quicker. It improves both performance and scalability.
  • Server-side caches avoid repeatedly making expensive retrievals from the origin store (e.g. a relational database).
  • Place the cache as close to the application as possible to minimize latency. Use fast storage (e.g. an in-memory database). The more data and users, the bigger the payoff.
  • (HTTP caching and CDNs are covered in Improving web application performance.)

Two strategies, usable together in one app:

  • Held on the machine running the application instance (often in-memory, extremely fast; can spill to local file system if it exceeds available memory).
  • Each instance has its own cache → the same query can yield different results across instances (a consistency risk).
  • Lives in a separate location, reached via a cache service; all instances use it.
  • Removes the divergent-view problem and improves scalability (a cluster can back the cache; the service locates data across the cluster).
  • Slower than a private cache due to network latency — but worth it when data consistency matters more.
  • Pre-populate the cache at application startup with data needed at startup or widely used enough to be worth having immediately.
  • Improves performance from the very first requests.

“There are only two hard things in Computer Science: cache invalidation and naming things.” — Phil Karlton

Cached data goes stale when the source changes after caching. Cache invalidation = replacing or removing cached items so stale data doesn’t persist; also needed when the cache fills up.

  • Configure data to expire after a set time.
  • Some systems support a system-wide expiration policy plus per-item policies.
  • Expiration is typically an absolute value (e.g. 1 day).

Eviction policies (when the cache is full)

Section titled “Eviction policies (when the cache is full)”
PolicyDiscards…Assumption
LRU (Least Recently Used)least-recently-used items firstrecently used → likely used again soon
MRU (Most Recently Used)most-recently-used items firstrecently used → won’t be needed again
FIFO (First-In, First-Out)oldest inserted itemignores last-use time
LIFO (Last-In, First-Out)newest inserted itemignores last-use time
Explicit evictionspecifically chosen itemse.g. after underlying data is deleted/updated

Two broad ways an app works with a cache:

  • The application maintains the cache and reads/writes the database itself; the cache doesn’t touch the DB.
  • On read: check cache first; hit → return from cache (bypass system-of-record); miss → read DB, store in cache, return.
  • On write: the app must invalidate/refresh cached data to keep it consistent with the system-of-record.

Read-through / write-through / write-behind

Section titled “Read-through / write-through / write-behind”

Here the cache is treated as the system-of-record and handles DB I/O itself:

  • Read-through: on a miss, the cache loads from the DB, stores it, and returns it.
  • Write-through: the cache writes to the DB and updates the cache; the thread waits for the DB write.
  • Write-behind: like write-through but the DB write is queued — the thread proceeds sooner, at the cost of a brief window where cache and DB are inconsistent.
  • Software Architect’s Handbook (Packt, 2018), Ch.10 “Server-side caching”, pp. 755-767.