Repository navigation
Conversation
1b6ea8c to
709a135
Compare
QA LLM Review1. MEDIUM -- A restart after a hydration that finished before the window is reported as an unfinished hydration
DetailsThe bounds don't match:
I checked this by running the model on: replica created and online at 2026-10-18 10:00, OOM-killed at 2026-10-20 11:30, back online at 11:31 and rehydrated by 11:35, window starting at 11:00. The output is Two possible fixes: skip a restart whose |
709a135 to
809b437
Compare
809b437 to
c6aeab0
Compare
QA LLM Review1. MEDIUM -- A zero-downtime upgrade's rehydration is classified as a new-objects episode
On Kubernetes, the full rehydration a 0dt upgrade runs on every replica finishes before status history records any boot for it, so Details
I ran the model on that timeline: the gen-N processes boot at 10:00, the episode runs from 10:00:05 to 10:06, and the leader's Boot detection needs a signal that also sees read-only boots. |
c6aeab0 to
2114ccc
Compare
2114ccc to
9c941b8
Compare
QA LLM Review1. MEDIUM -- Every restart of a replica with no indexes or materialized views is reported as an unfinished hydration
The episodes query drops zero-object episodes, but DetailsThe collector does record an episode after each boot of such a replica. Since 26.43, I ran the model on a source-only replica that came online at 2026-10-21 14:01 and was OOM-killed at 2026-10-22 03:00. The output is an unfinished hydration from 14:01 to 03:00 with reason This is separate from the earlier window-boundary finding. The boot here is inside the window, and any One option is to keep zero-object episodes for the unfinished check and filter them only from the hydrations that are displayed. |
9c941b8 to
9a2a02e
Compare
|
Thanks, all three are addressed in 9a2a02e:
Each has a regression test in |
9a2a02e to
31e12c7
Compare
QA LLM Review1. MEDIUM -- On Kubernetes, every new replica reports an unfinished hydration at its creation
DetailsHow the row gets written:
I ran the model on this case: replica created at 10:00:00.000, In #40074, One fix: count an |
31e12c7 to
44073ca
Compare
|
Thanks, fixed in 44073ca. An This push also makes live progress complete at once for a cluster with no replicas, for the finding on #40074. |
44073ca to
5a32288
Compare
QA LLM Review1. MEDIUM -- New objects created together are classified as a full hydration once they outnumber the cluster's existing objects
DetailsAt its start the cluster holds its N existing objects plus the first new one, which is created just before it is installed ( I ran the model on this case: a replica up since 08:00 with one index, then two new objects created at 10:00:00 and 10:00:01 that hydrate together by 10:03. The output is In #40074 that duration feeds "Its last full hydration took …" while a replica rehydrates after a restart, and also the hydrated verdict and the per-size typical value. Both then understate how long a full rehydration takes. The NOTE about dropped objects doesn't cover this case, since nothing was dropped. Comparing against the objects the cluster had when the episode finished ( |
QA LLM Review1. MEDIUM -- After a replica restarts, its live per-object hydration reads as an error for as long as the hook stays mounted
When the replica that DetailsHow the error sticks:
One fix is to treat a target-replica failure as recoverable in this hook, by restarting the session or by ignoring the error once rows arrive after it. Another is to have |
be7daec to
1ed5b4b
Compare
1ed5b4b to
85f2df5
Compare
There was a problem hiding this comment.
Devin Review found 3 potential issues.
1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)
| const finished = hydrations.some( | ||
| (hydration) => | ||
| hydration.replicaId === replicaId && | ||
| hydration.finishedAt.getTime() > attemptStartMs && | ||
| hydration.finishedAt.getTime() <= endMs, | ||
| ); | ||
| if (finished) return []; |
There was a problem hiding this comment.
🟡 Later hydration failures disappear after an earlier finish
When a replica crashes while hydrating new objects, findUnfinishedHydrations can discard that failure after an earlier episode finished. Its finished check covers the whole interval since the last restart, not just the hydration active at the crash.
Learn more
A replica can complete several distinct hydration episodes between restarts. This check treats any finished episode after creation or the previous restart as evidence that the replica finished hydrating before the next offline event. A later object installation can start a new episode and remain unfinished at that event, so the earlier finish says nothing about the active episode. The episode collector records only completed episodes; it cannot provide the missing start directly.
Example: A replica comes online at 10:00, finishes its initial hydration at 10:02, begins hydrating a new index at 10:10, then crashes at 10:12. The finish at 10:02 makes finished true, and the 10:10 failure disappears.
Recommended fix: Identify an unfinished attempt after the latest completed episode rather than treating any earlier completion as proof. Use available object installation or hydration state evidence if necessary to distinguish a new episode from an idle replica crash; add a test covering a completed initial hydration followed by a failed new-object hydration.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Good catch, fixed in c809a93. An attempt now counts as finished only if a hydration finished after the last object created during it, and the restart is dated from that object's creation when the replica had finished before it. Regression test in hydrationModel.test.ts.
| return useQuery({ | ||
| queryKey: clusterQueryKeys.clusterReplicaHistory({ | ||
| clusterId: params.clusterId, | ||
| timePeriodMinutes: params.timePeriodMinutes, | ||
| currentReplicaIdsKey: [...params.currentReplicaIds].sort().join(","), | ||
| }), | ||
| staleTime: Infinity, | ||
| queryFn: ({ queryKey, signal }) => { |
There was a problem hiding this comment.
🟡 Dropped replicas persist beyond the lookback window
As a dropped replica ages out of the selected window, useClusterReplicaHistory continues returning its cached row. The moving start date is fetched once per replica set, and staleTime: Infinity prevents refresh.
Learn more
The replica-history SQL selects replicas whose dropped_at is at or after a start date based on the current time. buildClusterReplicaHistoryQuery therefore returns a different set as time advances even when the current replica IDs do not change. This hook keys only on the duration and replica IDs and marks that result fresh forever. Leaving the tab open across the window boundary, or revisiting it while the query cache is retained, keeps replicas that have aged out.
Example: With a one-hour window, a replica dropped at 10:30 appears at 11:00. If the replica set remains unchanged, it still appears after 11:30 although the SQL would now exclude it.
Recommended fix: Refresh replica history on a cadence that bounds window staleness, or expire it at the nearest dropped replica's window boundary. Keep the query key scoped to the same cluster and selected duration.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Fixed in c809a93: mergeHydrationReplicas takes the window start and leaves out replicas dropped before it. The hook rounds the start down to the minute, so the filter moves on with the polled history without refetching the replica history.
85f2df5 to
c2fcd37
Compare
3fed262 to
c809a93
Compare
|
Thanks, both are fixed in c809a93:
|
c809a93 to
d11d3d6
Compare
d11d3d6 to
279504d
Compare
QA LLM Review1. MEDIUM -- A replica created and dropped within five minutes is missing from the hydration data
DetailsTimeline: Ways to hit it:
The console's own |
46176a4 to
83103d9
Compare
0bde204 to
a3d347c
Compare
QA LLM Review1. MEDIUM -- An OOM while hydrating a new object goes unreported on a replica that has been up since before the window
DetailsThe new-object check only needs episodes that finished after the object was created, and those fall inside the window. But line 275 returns before I ran the model on this timeline: a replica up since 2026-10-18, a window starting at 11:00, an index created at 11:10, an OOM kill at 11:15, back online at 11:16 and rehydrated by 11:25. The output is Related: A possible fix: when an object was created in the window during an attempt that began before it, decide with |
a3d347c to
0021de5
Compare
0021de5 to
5e618dc
Compare
First of two PRs adding a Hydration tab to the redesigned cluster page, on top of the configuration drawer. This one adds the data the tab reads. The next one adds the tab. - History, read with react-query since the history tables have no index: a cluster's hydration episodes from mz_replica_hydration_history (26.43 and later, one row per episode with the highest peaks across its processes), the cluster's replicas from mz_cluster_replica_history so dropped ones keep their name and size, an episode's objects from mz_object_hydration_history, and the highest CPU sampled during an episode. - Live state, in SUBSCRIBE-fed TanStack DB collections: whether each user object on each replica is hydrated (a lookup on mz_compute_hydration_times_ind), and each object's queued, working or hydrated state on one replica, from that replica's own introspection (26.39 and later) in a session pinned to it. - A model that counts an episode as a full hydration when it starts within two minutes of its replica booting, finds replicas that went offline before they finished hydrating, such as out-of-memory restarts, and splits an episode's objects into time waiting for a hydration slot and time working. - Collection subscribes keep their session variables across connection attempts. Each attempt sent the defaults instead, so a cluster given to a collection subscribe never reached the server. No existing caller passed one. - types/materialize.d.ts regenerated with yarn gen:types.
5e618dc to
ad6886e
Compare
QA LLM Review1. MEDIUM -- The replica-pinned live subscribe is resent in a tight loop for viewers without USAGE on the cluster
Pinning Details
One fix is to resend only after an error that a retry can clear, such as the target replica failing, and to leave other errors in place until the session restarts. Backing off between resends would also bound the loop. |
Motivation
Ninth PR in the cluster details redesign stack (#39509), on top of #40043. The redesign's prototype has a Hydration report tab. This PR adds the data a Hydration tab needs, built on the hydration history the database now records. The next PR in the stack, #40074, adds the tab. Part of CNS-173.
Description
mz_replica_hydration_history, one row per episode with the highest peaks across its processes. Gated on 26.43, which records a row per process, and leaves out older rows. Episodes with no objects stay in: on a replica with nothing to hydrate, such as one running only sources, they're the record that a boot finished;mz_cluster_replica_history, so a dropped replica (a resize, a burst replica) keeps its name and size. Fetched again only when the cluster's current replicas change, keeping the same cluster's answer up meanwhile, and filtered to the window as it moves on;mz_object_hydration_history, with catalog ids throughmz_object_global_ids. Fetched once per episode, since a finished episode doesn't change;mz_cluster_replica_metrics_history.mz_compute_hydration_times_ind, on the fast path;mz_compute_hydration_times_per_workerin a session pinned to the replica. Gated on 26.39. It runs on the replica, not onmz_catalog_server, so the tab mounts it only for a replica that is hydrating.hydrationModel.ts):useObjectCreationTimeskeeps its earlier answer up while the objects overlap, as after one is created, so a new index doesn't blank what depends on it.useClusterHydrationDataanduseHydrationDetailload and derive what the tab shows. The detail's object timings and CPU sample load and fail on their own, and the timings don't wait on a catalog that failed.SubscribeManagerstarts a clean snapshot when it resends a subscribe the server ended, as when the subscribe's target replica restarts. The old run's rows and error used to stay, so a per-replica subscribe read as failed for as long as it stayed mounted. The held state stays until the new run updates it, so a statement that fails every time keeps its error without flickering.mz_catalog_server.types/materialize.d.tsis regenerated withyarn gen:types. Most of its diff is the generator catching up with the catalog: builtin materialized views are no longerGenerated<>, and relations added since the last run (including the two hydration histories andmz_object_global_ids) appear.yarn gen:typesdoesn't run against Materialize as is: kysely 0.28's Postgres introspector callspg_get_serial_sequence, which Materialize lacks, and lists only tables and views, which leaves out the catalog's builtin materialized views. The types here were generated with both patched locally innode_modules. Fixing the script is a follow-up.Cost on
mz_catalog_server: the episode query and the object query read storage, scoped to one cluster or replica and a time bound, as the redesign's status timeline already does. Episodes poll at the history's 60s collection interval. The replica history scans its index, fetched once per change to the replicas.The Metrics tab's "Hydrating · N objects" badge counts introspection dataflows as well as user objects, so for a moment after a replica boots it can disagree with this PR's per-replica progress.
Tests
hydrationHistory.test.sql.ts: each builder against shadowed relations (grouping and peaks per episode, the 26.43 filter, episodes with no objects, dropped replicas in the window, user objects only, per-worker states combined into one per object, catalog ids for dropped objects), and against the real catalog, including a new index's hydration streamed from a session pinned to the quickstart replica.hydrationModel.test.ts: full and additive episodes, a zero-downtime upgrade's rehydration counted as full by the objects it covered, a burst of new objects that outnumbers the existing ones, out-of-memory restarts that never finished, a restart merged across processes, restarts after a finished hydration and on drop, a restart while hydrating an object created after the replica finished, an attempt that began before the window, an upgrade's late online row, a new pod's offline row, a boot with nothing to hydrate, the latest full hydration by finish, dropped replicas outside the window, heap limits, and object spans.useLiveHydration.test.ts: progress counted per replica from the stream onmz_catalog_server, progress complete at once for a cluster with no replicas, per-object states read in a session pinned to the replica, following the replica's new process after its subscribe fails over a restart, and no subscribe before 26.39.SubscribeManager.test.ts: a subscribe the server ends and runs again drops its error and old rows, and one that fails every time keeps reporting its error.replicaProcessMetrics.test.sql.tsreads its snapshot through the new sharedreadSubscribeSnapshothelper.🤖 Generated with Claude Code