skip to content

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%

answer

  1. cache-aside = on-demand
  2. cron/scheduled = fixed staleness window
  3. CDC/events = lowest latency, most complexity
  4. single-flight to avoid stampede
  5. hybrid: event-driven + periodic reconciliation

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.

solid answer

~40 s

On-demand refresh recomputes (or checks staleness and recomputes) at query time — freshest possible, but pushes the expensive computation into the read path, defeating some of the latency benefit and risking a stampede under concurrent requests. Scheduled refresh runs on a fixed interval regardless of whether data changed, which is operationally simple and predictable but wastes work when nothing changed and leaves a fixed staleness window even when it did. Event-driven refresh — often via CDC (change data capture) or a message/event stream — reacts to actual writes, giving low, variable latency and avoiding wasted recomputation, at the cost of more infrastructure (event pipeline, ordering/idempotency handling, possible incremental-update logic) and harder-to-reason-about consistency during bursts of events.

go deeper

for a junior

Should be able to name the three strategies and give a one-line description of each without confusing them.

for a middle

Should articulate the core trade-off for each (freshness vs. wasted work vs. complexity) and connect strategy choice to read/write frequency.

for a senior

Should discuss failure modes like cache stampede, incremental drift, and event pipeline lag, and propose concrete mitigations (single-flight, reconciliation jobs).

for a principal

Should reason about hybrid strategies across a whole platform, how staleness SLAs get negotiated with stakeholders, and how refresh strategy choice interacts with cost (compute, storage, pipeline infra) at scale.

## Why the choice matters Refresh strategy is the central design decision in the Materialized View pattern, because it determines where on the freshness-vs-cost spectrum a given view sits, and it interacts directly with how the view is used downstream. There are three broad strategies, and most production systems combine more than one depending on the view's criticality. ## On-demand refresh On-demand refresh means the view is recomputed (or its staleness checked and recomputed if needed) at the moment a read arrives, rather than on a background cadence. Mechanically, this is often implemented as a **cache-aside** pattern: a read checks whether a cached/materialized result exists and is fresh enough (e.g., via a TTL or a version/watermark check); if not, it synchronously recomputes and stores the new result before returning it. - **The advantage** is that the view is never staler than "since the last time it was actually requested," which can be attractive for infrequently-accessed views where scheduling a background job to refresh data nobody is reading would be wasted work. - **The downside** is that the recompute cost lands directly in the response-time budget of whichever request triggers it — the first reader after invalidation pays the full cost of the expensive query. - **Under concurrency** this creates the classic "cache stampede" or "dog-pile" problem: if many requests arrive simultaneously after the view goes stale, they can all independently trigger a recompute, multiplying load on the source system exactly when it's least wanted. - **Mitigations** include a single-flight lock (only one request triggers the recompute; others wait or get the slightly-stale value) and serving stale-while-revalidate (return the old value immediately while refreshing in the background for the next request). ## Scheduled refresh Scheduled refresh runs the recompute on a fixed cadence — every minute, every 15 minutes, nightly — independent of whether the underlying data changed or whether anyone is currently reading the view. This is operationally the simplest strategy: a cron job, a scheduled workflow, or a database's built-in scheduled refresh job. Its predictability is valuable for capacity planning — you know exactly when the load spike from refresh will occur and can schedule it during low-traffic windows. Its cost is twofold: 1. it does **wasted work** when the source hasn't changed since the last run, and 2. it imposes a **worst-case staleness bound of one full interval** — a write made one second after a scheduled refresh completes won't be visible until the next interval, which might be minutes or hours later. This is acceptable for reporting/analytics use cases but wrong for anything resembling a live operational view. ## Event-driven refresh Event-driven refresh ties the view's update directly to writes on the source data, typically via **change data capture** (CDC, reading a database's write-ahead log or replication stream), domain events published by the writing service, or database triggers. When a relevant write occurs, a consumer picks up the change and applies an update to the materialized view — either a full recompute of the affected slice or, better, an incremental update that adjusts just the rows/aggregates touched by that change. This gives the lowest achievable staleness (typically sub-second to a few seconds, bounded by pipeline latency) without the wasted-work problem of fixed scheduling. The cost is architectural complexity: - you now need an event/streaming pipeline with its own failure modes (lag, redelivery, ordering), and you must handle out-of-order or duplicate events idempotently, since at-least-once delivery is the norm for most event systems; - incremental update logic is also strictly harder to get right than "just recompute everything" — a bug in the delta logic can cause silent, cumulative drift between the view and the true source state. ## Blending the strategies in production In production, teams frequently blend strategies: an event-driven pipeline keeps the view close to real-time, paired with a periodic full-reconciliation job (e.g., nightly) that recomputes the view from scratch and corrects any drift the incremental path accumulated — trusting the source of truth over the incrementally-maintained copy. Canonical examples of each: | Strategy | Canonical example | |---|---| | **On-demand** | a reverse-proxy's cache-aside lookup with lazy population | | **Scheduled** | a data warehouse's nightly ETL job populating summary tables | | **Event-driven** | a change-stream feeding a downstream index update | The right choice depends on read frequency relative to write frequency, the acceptable staleness window for the specific business use case, and how much operational complexity the team can absorb — there's no universally "best" strategy, only the one matched to those three variables.

  • What is a 'cache stampede' and how does it relate to on-demand refresh of a materialized view?
    A cache stampede happens when a cached/materialized value expires or is invalidated and many concurrent requests arrive before it's repopulated, causing all of them to independently trigger the same expensive recompute against the source system at once. This can spike load on the source far above normal, sometimes causing a cascading outage. It's mitigated with a single-flight lock so only one request performs the recompute while others wait or receive a stale value.
  • Why might incremental event-driven updates drift from the true source state over time, and how do teams correct for it?
    Incremental updates apply deltas (e.g., 'add 5 to this running total') rather than recomputing from scratch, so any missed event, duplicate event, or ordering bug accumulates as permanent error rather than self-correcting on the next refresh. Teams typically correct for this by periodically running a full reconciliation — recomputing the view from the source of truth and overwriting the incrementally-maintained value — on a schedule independent of the event pipeline.
  • If a materialized view is read constantly but the underlying data changes only once a day, which refresh strategy wastes the least resources?
    Event-driven refresh triggered by the daily write is generally most efficient here, since it does exactly one recompute per actual change rather than repeatedly checking or recomputing on a schedule; a scheduled refresh running more frequently than the data changes wastes cycles on no-op recomputations, and pure on-demand refresh would need careful caching to avoid recomputing on every one of the constant reads.

Like restocking a vending machine: on-demand is restocking only when a customer finds it empty and complains; scheduled is a delivery truck that visits every Monday regardless of what sold; event-driven is a smart shelf sensor that radios the warehouse the instant an item's count crosses a threshold.

saying these in an interview costs you the question

  • Claims one refresh strategy is universally best regardless of read/write ratio
  • Doesn't recognize on-demand refresh can push heavy computation into the request's critical path
  • No awareness of cache-stampede/thundering-herd risk under on-demand or scheduled refresh
  • Assumes event-driven refresh is 'always instant and always correct' with no mention of pipeline lag or delivery guarantees
  • Can't explain why fixed-schedule refresh has a worst-case staleness bound

context