skip to content

Data Management

Cloud data patterns for making reads fast, spreading data out, and keeping large payloads out of the wrong places: cache-aside, materialized view, sharding, valet key, claim check and index table.

part ofResilience & cloud-native patternsoverview, primer and where to startread it →
on this pageshow

questions

page 1 of 2

In the cache-aside (lazy-loading) caching pattern, walk through step by step what happens when application code requests a key that is not currently in the cache.

level: juniorimportance: must knowfreq 85%

answer

  1. read-through is app, not cache
  2. miss then populate
  3. cache is a dumb store
  4. lazy loading
  5. cold key penalty

basics

~20 s

The app checks the cache first. If the value isn't there (a miss), the app reads it from the real database, saves a copy in the cache, then returns it to the caller. Next time it's a fast cache hit.

solid answer

~40 s

Cache-aside puts the application in charge of loading: on a read, the app queries the cache directly. On a hit, it returns the cached value with no database involvement. On a miss, the app itself queries the database (or origin store), gets the current value, writes that value into the cache — usually with a TTL — and then returns it to the caller. The cache is purely a passive key-value store; it never talks to the database on its own. This means the cache and database can go out of sync since two independent operations connect them, and the application owns both keeping them consistent and handling misses.

go deeper

for a junior

Should describe the two-branch hit/miss flow correctly and know that a miss means the app itself queries the database and populates the cache.

for a middle

Should additionally explain why the cache is described as 'passive' / 'dumb', and mention TTL as the mechanism that eventually expires stale entries.

for a senior

Should discuss the cold-start/self-healing property, the latency cost of the first read, and how this pattern decouples cache technology choice from the database.

for a principal

Should connect this base mechanism to system-level consequences: cache warm-up strategy after deploys, cache outage blast radius on the database, and when cache-aside is the wrong default for an entire service.

## What cache-aside is **Cache-aside**, also known as **lazy-loading**, is a caching pattern in which the application code — not the cache server — owns the logic for keeping the cache populated and in sync with the underlying data store. The cache itself (`Redis`, `Memcached`, an in-process LRU map, etc.) is a completely passive key-value store: it can only answer "do you have this key, and if so what's the value" or accept "store this value under this key." It has no idea what a database is, no query capability against one, and no mechanism to refresh itself. ## The read path has two branches Every read against a cache-aside cache follows the same two-branch flow. 1. First, the application issues a `GET` for the key. 2. **Hit** — if the cache has the key, the cached value is handed straight back to the caller, and the request never touches the database. This is the fast path that gives caching its latency and load-reduction benefit. 3. **Miss** — if the cache does not have the key, the application falls through to a second step: it queries the system of record — a relational database, another microservice, an external API — for the current value, gets the result back, writes that result into the cache with a `SET` (typically attaching a TTL so the entry expires and self-cleans over time), and only then returns the value to the original caller. ## Why the design exists This design exists because it lets you cache exactly the working set that's actually being read, with no advance warming step and no coupling between the cache technology and the write path of the system of record. - **Cheap and swappable.** Because the cache is dumb, it's cheap, horizontally scalable, and swappable — you can point the same read-then-populate logic at `Memcached` today and `Redis` tomorrow without touching the database schema or write path at all. - **A cold cache self-heals.** It also means a cold cache (freshly deployed, just flushed, or a brand-new node joining a cluster) self-heals automatically: the first request for any key simply takes the miss path and populates it, no separate warm-up job required. ## The costs of that simplicity The cost of that simplicity is that the cache and the database are two independently-updated stores connected only by application logic, so they can and will drift out of sync for short windows — cache-aside offers no built-in consistency guarantee, only "eventually correct, cache expires or gets invalidated." Two more costs follow: - **The "cold key" penalty.** A second cost is that the very first read of any key (or any read after expiry) pays the full latency of a database round trip plus a cache write, on top of the actual work — there's no way to make that first read cheap under pure cache-aside. - **Duplicated populate logic.** A third cost is that every application (or service) that reads this data has to correctly implement the get-then-populate logic; if that logic is duplicated across services without a shared client library, subtle bugs (missing TTLs, wrong key formats) creep in. ## How it shows up in production In production this shows up in a few characteristic ways. - **A mass expiry event** — many keys with the same TTL expiring at once, e.g. after a cache flush or a deploy that resets TTLs — produces a burst of concurrent misses that all hit the database simultaneously for the same or related keys, which can look like a self-inflicted spike on the database tier. - **A benign failure.** If the application crashes or times out after reading from the database but before writing to the cache, the failure is benign: the value just never gets cached this round, and the next reader takes the miss path again — no corruption results. - **The riskier failure mode** is when the cache goes down entirely: cache-aside degrades to "every read hits the database," and if the database was sized assuming a high cache hit rate, this can cascade into a full outage rather than a graceful slowdown, so production systems typically pair cache-aside with circuit breakers or load shedding on the database side. ## The canonical example at scale The canonical large-scale example is Facebook's use of `Memcached` in front of `MySQL`, described in their well-known "Scaling Memcache at Facebook" engineering writeup: web-tier servers read from Memcached first, and on a miss query MySQL directly and populate Memcached with the result before answering the request, while writes go to MySQL and then delete (rather than update) the corresponding Memcached key. That delete-on-write choice, and the miss-then-populate read path, is textbook cache-aside operating at a scale of many millions of reads per second, and it's the pattern most engineers reach for by default whenever they bolt caching onto an existing read path without redesigning the write path.

  • What happens on a cache hit versus a cache miss in terms of database load?
    On a hit, the database is never touched at all — the request is served entirely from the cache, which is the whole point of the pattern. On a miss, the database is queried exactly once, and the result is cached so subsequent requests for the same key become hits until the TTL expires or the key is invalidated.
  • Does cache-aside require the cache to know anything about the database schema?
    No — that's one of its main selling points. The cache just stores opaque key-value pairs; all knowledge of how to fetch and shape the data lives in the application code, so the same cache technology can sit in front of completely different data stores.
  • What happens if the application crashes after reading from the database but before writing to the cache?
    Nothing dangerous — the cache simply stays cold for that key, and the next request takes the miss path again, re-reading from the database and retrying the populate. It's a safe, self-correcting failure, unlike a crash mid-write to the database itself.

Like a barista who only makes a latte when someone orders it, then keeps a cup on the counter for the next person who asks for the same drink — nobody pre-brews drinks nobody's ordered yet.

saying these in an interview costs you the question

  • Says the cache automatically fetches from the database on a miss
  • Doesn't mention that the app has to write the value back into the cache after a miss
  • Thinks a miss means the key definitely doesn't exist in the database
  • Can't explain why the first read of a key is always the slowest

context

open as a page

A message queue advertises a 256 KB maximum message size, but a service needs to move a 50 MB video file through an event-driven pipeline. What is the standard fix, and why don't teams just send the file directly in the message body?

level: juniorimportance: must knowfreq 72%

basics

~10 s

Save the big file to storage like S3, then send only a small pointer (its location or ID) through the queue. The receiver downloads the real file from storage when it needs it.

open as a page

In a cloud-native application, why might a team split the write side and read side of a service into separate models using CQRS (Command Query Responsibility Segregation) instead of using one shared model for both?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Because writing data and reading data back have different needs. CQRS keeps them as two separate models so each can be built, scaled, and hosted the way that suits it best, instead of one design compromising for both jobs.

open as a page

A key-value store like Azure Table Storage or DynamoDB lets you look up an item fast only by its primary key (partition key + sort key). Your application also needs to find a customer's orders by their email address, which is not part of that key. Name a simple technique for making that lookup fast without switching databases, and say what it costs you.

level: juniorimportance: must knowfreq 55%

basics

~20 s

Build a second, smaller table that maps the field you want to search by (email) to the ID of the matching row in the main table. Look there first, then fetch the real record. It costs extra storage and effort to keep both tables in sync.

open as a page

What is a materialized view, and why might a system use one instead of running the same expensive query against source tables every time a client asks for the data?

level: juniorimportance: must knowfreq 70%

basics

~20 s

A materialized view is a saved copy of a query's result, stored ahead of time. Instead of recalculating joins and aggregates on every request, the app just reads the pre-built copy, which is much faster but can be slightly out of date.

open as a page

What is database sharding, and why would a team split one big table across multiple database servers instead of just running a bigger single machine?

level: juniorimportance: must knowfreq 75%

basics

~20 s

Sharding splits a big table's rows across several separate database servers so no single machine has to hold or serve all the data. Each server (shard) keeps a slice of rows, chosen by a shard key like user_id.

open as a page

In the Valet Key cloud design pattern, a client wants to upload a large file directly to object storage (like Amazon S3 or Azure Blob Storage) instead of routing the bytes through your application server. What does the application server give the client to make this possible, and what two properties does that credential typically have?

level: juniorimportance: must knowfreq 70%

basics

~20 s

The server hands the client a special temporary link (like a pre-signed URL) that lets it talk straight to the storage service. That link only works for a short time and only for one specific action, like uploading one file.

open as a page

In a cache-aside pattern, a service updates a row in its database and then deletes the corresponding key from the cache (instead of writing the new value directly into the cache). Why is 'invalidate on write' usually preferred over 'update the cache value on write', and what race condition can still leave the cache holding stale data afterward?

level: middleimportance: must knowfreq 75%

basics

~20 s

Deleting the old cache entry is simpler and safer than trying to recompute and overwrite it. But there's a timing gap: another request could reload the old value into the cache right after the delete, right before the write finishes, leaving the cache wrong until it expires.

open as a page

Walk through the claim check pattern step by step for a pipeline where a producer emits a large report and a consumer needs to process it: what exactly happens, in what order, and what does each side own?

level: middleimportance: must knowfreq 65%

basics

~20 s

Producer saves the big report to storage first, gets back a location, then sends a small message with that location through the queue. Consumer reads the message, fetches the report from storage using the location, then processes it.

open as a page

In an event-sourced cloud service — where the write side persists state changes as an ordered log of events rather than overwriting rows — how are CQRS read-model projections typically built and kept up to date from that event log?

level: middleimportance: must knowfreq 65%

basics

~20 s

A separate background process reads new events one by one from the log, in order, and updates a query-friendly copy of the data (a projection) to match. That copy is what queries actually read from.

open as a page

In the index table pattern, a write to a base entity needs to also update a separate index table keyed by a non-key attribute of that entity. Describe two different strategies for keeping the two tables consistent with each other, and what each one trades off in terms of latency and consistency guarantees.

level: middleimportance: must knowfreq 65%

basics

~20 s

You can either write to both tables together as one atomic operation when the store allows it, or write to the base table first and update the index table afterward in the background. The first is safer but slower and not always possible; the second is faster but can leave the index briefly out of date.

open as a page

When maintaining a materialized view, what are the trade-offs between refreshing it on-demand (when queried), on a fixed schedule, and in response to change events from the source data?

level: middleimportance: must knowfreq 75%

basics

~20 s

On-demand refresh recalculates the view only when someone asks, so it's always fresh but slow the first time. Scheduled refresh updates it every so often (like every hour), which is simple but can be stale between runs. Event-driven refresh updates it right when the source data changes, keeping it fresh with less wasted work, but is more complex to build.

open as a page

A time-series table hashes writes on device_id, but one industrial sensor array alone generates 40% of all writes and keeps overloading its shard. What techniques let you avoid this single hot shard without abandoning hash sharding entirely?

level: middleimportance: must knowfreq 60%

basics

~20 s

Change the key so that one busy device's writes don't all pile onto one shard — for example, combine the device ID with a time bucket or a random suffix so its data spreads across several shards, then merge them back together when reading.

open as a page

When choosing a shard key strategy for a data store, what are the differences between lookup-based, range-based, and hash-based sharding, and what does each cost you?

level: middleimportance: must knowfreq 70%

basics

~20 s

Range sharding groups nearby keys (like consecutive IDs) on the same shard, which is great for range scans but can pile new writes onto one shard. Hash sharding spreads keys evenly using a hash function, avoiding that but breaking range queries. Lookup sharding uses an explicit table to say which shard each key lives on, giving full control at the cost of an extra hop.

open as a page

When an application server constructs a pre-signed URL (or Azure SAS token) for a client to upload a single file to object storage, which specific parameters should it constrain to keep the resulting credential least-privilege, beyond just setting an expiry time?

level: middleimportance: must knowfreq 65%

basics

~10 s

It should limit the link to one exact file, one action (like upload-only, not delete), and sometimes the file size or type — not just make the link expire soon.

open as a page

In the cache-aside pattern, every cached entry carries a TTL (time-to-live) after which it's treated as a miss even if nothing invalidated it explicitly. What trade-off are you making when you pick a short TTL versus a long TTL, and why can't you rely on TTL alone to guarantee freshness?

level: seniorimportance: must knowfreq 70%

basics

~20 s

Short TTL means fresher data but more trips to the database because things expire fast. Long TTL means less database load but data can be wrong for longer. TTL alone doesn't fix staleness — it just puts a ceiling on how long stale data can survive.

open as a page

A claim-check pipeline has been running for six months and the object storage bucket backing it has grown to 40 TB, far more than the traffic volume would suggest. What's the likely root cause, and how would you diagnose and fix it?

level: seniorimportance: must knowfreq 58%

basics

~20 s

Old files in storage are probably never getting deleted after being processed -- orphaned blobs pile up forever. Fix by adding a cleanup step or an automatic expiry rule on the storage bucket so old, unused files get removed.

open as a page

A cloud service uses CQRS with a read store that's updated asynchronously from an event-sourced write side. A user updates their profile, immediately reloads the page, and still sees the old value for a couple of seconds. What's causing this, and how would you address it for a production user experience?

level: seniorimportance: must knowfreq 60%

basics

~20 s

The read copy of the data hasn't caught up to the write yet — there's a short delay while the update flows through to it. Fixes include showing the user their own change right away without waiting for the read copy, or making that specific read wait until it's caught up.

open as a page

A service writes a new order to a base orders table, then makes a second, separate call to write a pointer into an index table keyed by customer email so orders can be looked up by email later. The process crashes after the first write succeeds but before the second write is attempted. What is the resulting failure mode, and what are two concrete ways to guard against it?

level: seniorimportance: must knowfreq 70%

basics

~20 s

The order exists but is invisible to anyone searching by email, because its index entry was never created. Fix it by either doing both writes as one atomic operation when possible, or by having a background process that finds and repairs these gaps automatically.

open as a page

How would you decide, for a given feature, whether to serve reads from a materialized view (accepting some staleness) versus computing the result with a query-time join against the live source tables — and what breaks if you get that call wrong in either direction?

level: seniorimportance: must knowfreq 65%

basics

~20 s

Use a materialized view when the query is expensive and slightly old data is fine, like a dashboard. Use a live join when the data must always be current, like a bank balance. Picking wrong means either a slow, overloaded system or a fast one that shows outdated info to people who needed the latest.

open as a page

In a sharded relational database, why can't you run a JOIN across two tables that live on different shards the same way you would in a single-instance database, and how do teams typically handle a transaction that must update rows on two different shards?

level: seniorimportance: must knowfreq 65%

basics

~20 s

Each shard is a separate database that doesn't know about the others, so it can't run a join or a single all-or-nothing transaction spanning two of them. The application has to query each shard separately and combine results, and cross-shard writes need special handling like two-phase commit or a saga, or the schema should be redesigned so related data lives on the same shard.

open as a page

A team is deciding whether to let clients upload directly to object storage via pre-signed URLs, or keep proxying uploads through their application server. Name two concrete scenarios where proxying through the app server is the better choice, and explain why the Valet Key pattern falls short there.

level: seniorimportance: must knowfreq 55%

basics

~20 s

If you need to check, transform, or tightly control the file as it arrives, like scanning for viruses before it's stored or enforcing a quota that can change moment to moment, routing through your server lets you do that inline. Direct-to-storage uploads only let you react after the fact.

open as a page

What does a team actually give up by adopting the claim check pattern for a pipeline that previously sent every payload inline on the message bus?

level: middleimportance: should knowfreq 55%

basics

~20 s

It gets slower per message (extra round trip to fetch the payload) and more complex (a second system to run, pay for, and secure). In exchange, the queue stays fast and cheap and can handle way more messages per second.

open as a page

Which categories of managed cloud services are commonly used to implement the event store and the projection pipeline in a CQRS-plus-event-sourcing architecture, and what role does each typically play?

level: middleimportance: should knowfreq 55%

basics

~20 s

Cloud providers offer managed 'always-on' logs (streaming services) to hold the events, managed databases with built-in 'change feeds' to trigger updates automatically, serverless functions to run the update logic, and managed read stores (like document databases or search services) to hold the fast, query-ready copies.

open as a page

A team building a product catalog on a key-value store creates four separate index tables alongside the base product table — one each to look up products by category, by brand, by warehouse, and by price tier. What operational cost does this design add to every single product write, and how does that cost scale as more index tables are added?

level: middleimportance: should knowfreq 50%

basics

~20 s

Every time a product is created or changed, the app now has to write not just to the main table but to all four index tables too — so one logical update turns into up to five separate writes. Add more index tables and every write gets slower and more expensive, even if nothing about that particular field changed.

open as a page

A cache-aside key backing a hot database query expires, and within the same millisecond thousands of concurrent requests for that key all miss the cache. Describe two distinct mechanisms an application can use to stop this from turning into a 'thundering herd' that overwhelms the database.

level: seniorimportance: should knowfreq 65%

basics

~20 s

Use a lock so only one request reloads the data while everyone else waits or gets a slightly old value. Or make one request per process share its in-flight database call with all the others asking for the same thing, so only one query actually runs.

open as a page

A claim-check pipeline stores customer documents in a shared object storage bucket and passes references through a queue. What access-control mistakes commonly show up here, and how should read access to the payload actually be scoped?

level: seniorimportance: should knowfreq 45%

basics

~20 s

The common mistake is making the storage bucket wide-open or giving every consumer permanent access to everything in it. Better: give each consumer only the narrow permission it needs, ideally a short-lived link scoped to just that one file.

open as a page

You're designing a new cloud service and a colleague proposes building it with CQRS and full event sourcing from day one because 'it's the scalable, cloud-native way to do it.' When would you push back on that default?

level: seniorimportance: should knowfreq 50%

basics

~20 s

When the service is small, simple, or needs strict instant consistency — the extra moving parts (two models, an event log, projectors) cost more in complexity than they save, and a plain database with normal reads/writes does the job fine.

open as a page

A team maintains an index table that maps order status — one of PENDING, SHIPPED, DELIVERED, or CANCELLED — to order IDs, so they can quickly list all orders in a given status. Under heavy load, writes to this index table start throttling even though the base orders table stays healthy. What's the likely root cause, and how would you redesign the index table's key to fix it?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Using status as the partition key means all orders with the same status pile into one 'bucket,' so writes for that status compete for the same limited capacity even though the rest of the system is fine. Spreading the writes across more buckets — for example by adding some kind of extra prefix or suffix — fixes the bottleneck.

open as a page

You're designing incremental refresh for a materialized view that maintains a running 'total spend per customer' from an orders table, instead of recomputing the full aggregate from scratch on every refresh. What does the incremental update logic need to handle correctly, and what can go wrong if it doesn't?

level: seniorimportance: should knowfreq 55%

basics

~20 s

Instead of re-adding up every order every time, you just add or subtract the new order's amount to the existing total. But you also need to handle edited or canceled orders (subtract the old amount first) and make sure you don't double-count an order you've already processed.

open as a page

showing 1–30 of 39