3 system design questions lean on this idea. Each walks through the full answer.
The views lesson measured what a materialized view is worth to read. The lesson measured what a derived value costs to maintain and how it drifts. Neither measured the refresh, which is where the whole operational question lives.

None of the three decisions that matter appears anywhere in CREATE MATERIALIZED VIEW.

A parallel session reads the view continuously while each refresh runs, and the worst read it saw is recorded.

The reader was not made slow. It was made to wait, because a plain REFRESH takes an ACCESS EXCLUSIVE lock and holds it until the new contents are complete.
That number is the refresh duration, not a coincidence. A reader arriving at any point during a plain refresh waits until the end of it, so the worst case is exactly how long the refresh takes.

The old contents stop being valid the moment the swap happens. Holding a lock across the whole operation is the only way that is safe.

The concurrent form does strictly more work: it computes the new contents, compares them against the old ones, and applies the difference.

The refresh becomes a set of ordinary row updates, which readers handle the way they handle any other transaction.


Not approximately the same. Identical, to the rupee, because nothing about a materialized view changes when the underlying table changes.
The staleness window is the refresh interval plus the refresh duration. Refreshed every five minutes with a 1.3 second concurrent refresh, the answer is between zero and about five minutes old, and nothing in the result says which.

PostgreSQL says a view is populated when it has contents. That says nothing about whether those contents resemble the table they came from.
Calling it a cache imports three guarantees it does not have. Nothing expires, there is no miss to fill, and no write invalidates it.

The stored answer is 2,196 rows in 256 kilobytes against a table of 538 megabytes. On the read side there is nothing to argue about.

A view is the query written down and the answer is always current. A materialized view stores the result and stops being related to the table.


A materialized view is right for a question whose answer may be minutes old. For anything that must be current it is the wrong shape entirely.

The refresh duration grows with the underlying data and the interval does not. The last row is what happens when they cross.
Refresh on the load that changes the data wherever that change is a known event. A nightly load followed by one refresh is both fresher and cheaper than a timer recomputing an unchanged table all day.

The read speed is believed instantly and the staleness is understood as an abstraction until somebody looks at two numbers that disagree.

The lesson's incremental summary is the exact opposite: always current and capable of being quietly wrong.

5 questions - Score 80% to pass
A read that normally takes 7 ms took 1,231 ms during a plain REFRESH that lasted 1,234 ms. Why are those two numbers almost the same?
REFRESH CONCURRENTLY took 1,271 ms against 1,234 for the plain form, and readers saw 8 ms instead of 1,231. Why does it not block?
REFRESH CONCURRENTLY raised an error before a unique index existed. Why does it require one?
Six hundred thousand orders were inserted and the view reported an identical total. Then two million were deleted and it still did, while pg_matviews reported it as populated. What does populated mean?
How does a materialized view differ from a cache?
A difference needs a way to say which row is which. Without a unique index there is no such way, so the feature is simply unavailable.
Create that index in the same migration as the view. Discovering it is missing at three in the morning, when a refresh has locked the dashboard everybody is watching, is the usual way this gets learned.

The drum is cheap to store and nearly free to read. Everything difficult about a materialized view is a property of the ring.
The view cannot drift and is always slightly old. The summary is always current and can be quietly wrong forever.
Choose by which failure the application can tolerate. A dashboard can read a number that is four minutes old; a balance shown to the person who owns it cannot be wrong by an amount nobody can explain.

Nothing reports it. The refresh succeeds, the job logs success, and a request that normally takes seven milliseconds took over a second.

One decides whether the refresh can avoid blocking readers at all. The other decides whether anybody looking at the number can tell how old it is.
A p99 that spikes on a round number of minutes is almost always a scheduled job holding a lock. Materialized view refreshes are the most common one, and the fix is one keyword plus one index.
The read is not in dispute. Everything else is about the refresh.
Correct-and-stale against current-and-driftable is the real choice, and the two lessons that measured them are five apart. Neither is a default, and picking on read speed alone picks the same one every time.
That closes this run through the database fundamentals: storage, indexes, plans, and the four ways of keeping an answer ready. Every one of them was measured against a real PostgreSQL instead of described.