DBRaven
Pattern · caching

Read-Through Cache

established

Summary

Place a cache in front of the database such that all reads go to the cache; on a miss, the cache itself fetches the data from the database, populates itself, and returns the result: the application never reads the database directly and cache population is handled automatically by the cache layer.

Problem

Cache-aside requires the application to implement the check-miss-populate pattern in every code path that reads data. This is repetitive, error-prone, and creates a race condition (multiple concurrent readers can all trigger a database fetch for the same cache miss). Read-through centralizes this logic in the cache layer.

Description

In cache-aside, the application manages the cache explicitly: check cache, on miss read database, populate cache. In read-through, the cache is interposed between the application and the database. The application calls only the cache; if data is absent, the cache delegates to the database internally, caches the result, and returns it. The application code is not aware of whether the response came from cache or database.

Read-through provides a simpler programming model: the application has one read interface. It avoids the cache-aside thundering herd issue: when multiple concurrent requests miss on the same key simultaneously, only one cache-to-database fetch is issued (the cache coalesces concurrent misses). Other requesters wait for that single fetch to complete.

Read-through is natively provided by some caching systems: Amazon ElastiCache with DAX (DynamoDB Accelerator), Redis Enterprise's RediSearch, and some CDN configurations. In application-layer read-through, a caching library (Caffeine for JVM, django-cacheops) intercepts reads and manages population internally.

Read-through works best for data that is read-heavy, changes infrequently, and can tolerate bounded staleness. It does not naturally handle cache invalidation on writes, that requires a write-through or write-invalidation strategy applied alongside.

A Caffeine LoadingCache (JVM) is a concrete example: build the cache with a loader function (`.build(key -> userRepository.findById(key))`), and every `cache.get(userId)` call is either a hit or an automatic database fetch through that loader. Python's django-cacheops offers the equivalent via a `@cached_as` decorator on the read function. AWS DynamoDB Accelerator (DAX) is a fully managed read-through and write-through cache for DynamoDB: applications talk to DAX through its SDK, and cache population and invalidation are handled by the service rather than application code.

Tradeoffs

Application simplicity
+0.7

One read interface regardless of whether data is in cache

Population automation
+0.6

Automatic cache population on misses; no manual populate logic

Thundering herd protection
+0.7

Built-in miss coalescing prevents a thundering herd on the same key

Cold-start latency
-0.2

The first request for any key always pays the miss penalty

Invalidation strategy
-0.3

Cache invalidation must be implemented separately (write-through or TTL-based expiry)

Availability coupling
-0.4

The cache layer is now a required dependency; if it fails and the fallback is not handled, reads fail entirely

Debuggability
-0.2

Opaque cache population makes it less obvious when stale data is being served

When to use

Read access pattern is highly repetitive (same keys read by many requests)

Cache hit ratio improves as the same keys are read repeatedly; read-through's automatic population fills the cache quickly on startup

Cache miss handling should be transparent to application code

Simplifies application logic by hiding the cache-or-database routing decision

Concurrent miss coalescing is needed (many requests for the same missing key simultaneously)

Read-through caches typically serialize concurrent misses on the same key to a single database fetch

Cache layer supports read-through semantics natively (DAX, Caffeine LoadingCache)

Implementing read-through manually in application code negates much of its simplicity benefit

When not to use

Data has highly variable or unpredicatable access patterns

Read-through populates the cache on every miss; if data is rarely reread, the cache fill-and-evict cycle provides no benefit

Application requires fine-grained control over when database reads occur

Read-through triggers database reads transparently; use cache-aside when you need explicit control

Operational Requirements

recommended

Pre-warm the cache on startup

Needed if cold-start miss latency is unacceptable for the workload.

mandatory

Monitor cache hit ratio by key space

A low hit ratio indicates TTLs are too short or the working set exceeds cache capacity.

mandatory

Combine read-through with write-through or invalidation on writes

Prevents stale reads, since read-through alone does not handle invalidation.

Characteristics

Scales on
Implementation complexitymedium
Operational complexitymedium

Relationships

Complements

cache asidewrite behind cachematerialized view

Basis

Read-through caching is documented in AWS DAX documentation, Caffeine documentation, and Redis Enterprise documentation; the pattern is well-established in both academic caching literature and industry practice

Related Architecture Knowledge

Outbound: this entity affects

ComplementsPattern
write behind cache
Grounded

Read-through handles cache population on miss; write-behind handles cache population on write. Together they form a complete transparent cache layer.

Full relationship →

Inbound: affects this entity

Benefits FromWorkload
read heavy api
Grounded

Read-heavy API workloads benefit from read-through caching which automatically populates the cache on miss, reducing database read pressure and eliminating the miss coalescing problem under high concurrency.

Full relationship →

Used In Architecture Scenarios

Content Management Platformmoderate

Read-Heavy Application

A CMS for publishing and serving structured content: articles, documentation, product pages, and localized variants: where read APIs serve 50–100x more traffic than editorial write APIs. PostgreSQL stores the content graph (articles, authors, categories, taxonomy) and workflow state (draft, in-review, scheduled, published). Redis caches published content objects for read APIs. Elasticsearch powers full-text content search with faceting and relevance ranking. Cache invalidation on publish must be fast and complete; N+1 query patterns on content relationship traversal are the dominant database performance risk during reads.

ML Feature Serving Platformexpert

AI / RAG Application

A low-latency feature serving platform for ML model inference, providing both batch (offline) and real-time (online) feature access with strict training-serving consistency. Redis serves the hot feature cache with p99 latency targets below 5ms for online inference requests; PostgreSQL provides point-in-time feature lookups for offline training jobs with temporal consistency guarantees; Cassandra stores high-cardinality feature entities at write scale beyond PostgreSQL's single-primary ceiling; Kafka streams feature computation events from online feature pipelines to update the cache; Qdrant stores vector features for embedding-based model inputs and similarity lookups; ClickHouse serves feature analytics and drift monitoring across training dataset populations. Feature versioning is first-class: every feature value is tagged with a pipeline_version and computed_at timestamp to support model reproducibility and training-serving skew diagnosis.