Repository navigation
adapter: Compare 0dt hydration against the leader - #39155
Conversation
|
I've triggered a relevant subset of Nightly: https://buildkite.com/materialize/nightly/builds/18373 |
0b1f93b to
a69db78
Compare
a69db78 to
1b7c6fc
Compare
1b7c6fc to
60b9489
Compare
60b9489 to
a0bccdb
Compare
a0bccdb to
dac07c9
Compare
The incoming generation should not need more hydration than the leader has. Read the leader's mz_compute_hydration_times and waive local hydration where the leader explicitly reports none: * A compute collection within the lag bound need not hydrate when every leader replica hosting it reports it unhydrated. The stability gate then ignores it too. * A replica the leader reports unhydrated, for a collection the classification did not ignore, only has to be Online. A replica the leader has fully hydrated still has to hydrate here. Missing reports never waive anything. Protect the hydration source from builtin migration, so the check keeps reading the leader's shard. Closes: SQL-696 Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-01a0d85f-8cd1-7078-afa3-21a9cce44857
b562872 to
8044721
Compare
8044721 to
8346e99
Compare
8346e99 to
c466c46
Compare
3a1e9bc to
88b47a6
Compare
88b47a6 to
dfabbdf
Compare
### Motivation Closes: [SQL-691](https://linear.app/materializeinc/issue/SQL-691) A DDL-triggered reboot of the read-only environmentd discarded the 0dt stability streak, even when the incoming replicas kept running. With DDL more frequent than the stability period, a deployment could never become ready. ### Description The stability gate now asks the replicas. Each tick queries every replica's `mz_compute_hydration_times_per_worker` log with a one-row query: the latest `hydrated_at` it reports, or NULL if any non-transient export on it is not hydrated yet. Compute collections the caught-up classification ignored (excluded new builtins and their dependents, collections whose leader frontier is past the cutoff) are left out of the query, because they may never hydrate in read-only mode. A caught-up cluster is ready once every replica is Online and has been fully hydrated for the stability period. A replica-pinned MV or a collection without a dataflow never appears in a replica's log, so neither needs special handling. The queries are plain SQL through the frontend peek path (`PeekClient::background_peek`), at serializable isolation so they read the freshest log state even when the timestamp oracle does not advance. The frontend peek path calls back into the coordinator, so the check now runs in its own task, like the cluster controller: each tick it asks the coordinator to classify the clusters, then queries the replicas and applies the gate itself. The background `PeekClient` constructor is shared with hydration history collection. Replicas are asked concurrently, so a tick waits at most 5s for answers. A query that misses the deadline is dropped. That does not cancel its peek: it keeps its read holds until the replica answers, and in the rare case of a drop between registration and issue the registration stays behind. We accept that, like for user peeks: a stuck replica holds at most one peek per tick, on a log index that barely changes. The log lives in the replica process. A restarted environmentd reconciles with the same replica processes, which keep compatible dataflows, so it reads the original hydration times. A restarted replica starts a new log and must hydrate again. Hydration times come from the replica's clock: a replica clock ahead of environmentd's only lengthens the wait, one behind shortens the period by the skew. The period counts from the replica's latest hydration. On main it starts at the first tick where the cluster is caught up and healthy. Hydration is a proxy for that, so a long catch-up after hydration eats into the period. Replicas without introspection logging cannot answer and only need to be Online. The per-cluster status-flap and restart tracking is removed, because replica-reported hydration covers restarts directly. No orchestrator changes and no new durable state. Behavior changes worth noting: * Every replica of a caught-up cluster must have hydrated everything it hosts. Previously each collection hydrated on some replica plus all replicas Online sufficed. A replica that is Online but cannot hydrate (for example undersized but not OOMing) now holds the cutover until skip-catchup or `with_0dt_deployment_max_wait`. #39155 relaxes this for replicas the leader reports unhydrated too. * A cluster that gets new dataflows restarts its period when they hydrate. Other clusters keep theirs. * Source ingestions are not in the compute log. For them, the period counts from replica start. * An Offline-then-Online blip without a restart no longer restarts the period. * Replicas with introspection logging disabled are not held to the period. ### Verification Unit tests cover the stability fold and that a missing, NULL, or pre-epoch `hydrated_at` reads as not hydrated. The new `caught_up_stability_survives_restart` 0dt workflow triggers a DDL restart of mz_new partway through the stability period, checks that its replicas survived, and asserts it becomes ready before a restarted period could end. The new `caught_up_stability_without_dataflows` workflow checks that a sealed `REFRESH AT` MV and a replica-pinned MV on a two-replica cluster do not block promotion. The existing `caught_up_stability` and `caught_up_stability_crash_loop` workflows cover the healthy and crash-looping cases. ### Release notes * Zero-downtime deployments no longer restart the replica stability period when environmentd reboots to pick up DDL. * Zero-downtime deployments now wait for every replica of a cluster to hydrate before cutting over. A replica that stays Online but never hydrates holds the deployment until it is promoted with skip-catchup or `with_0dt_deployment_max_wait` passes.
dfabbdf to
e7f907f
Compare
9cb5696 to
c042e35
Compare
c042e35 to
6850338
Compare
| } | ||
| } | ||
|
|
||
| /// The leader's hydration reports, read from its `mz_compute_hydration_times`. |
There was a problem hiding this comment.
Is this across all replicas of some cluster, so the unhydrated/hydrated is mixed, or hydration status of one specific replica. Need to be specific in the code docs and naming here, please
5a49495 to
5abda6d
Compare
| use ReplicaHealth::{Exempt, HealthySince, NotHydrated, NotOnline}; | ||
|
|
||
| #[mz_ore::test] | ||
| fn collection_hydration_waiver_requires_complete_leader_reports() { |
There was a problem hiding this comment.
Nothing in the 0dt suite creates a collection or replica the leader hasn't hydrated, and CI runs with a 0 s stability period, so a no-op would pass. Could we add a workflow for each rule (e.g. a leader whose hydration of one object is stalled, and the crash-loop workflow with a replica the leader hasn't fully hydrated)?
| /// one collection outside `ignored` as unhydrated. | ||
| /// | ||
| /// This exempts the incoming replica from hydration stability checks: it | ||
| /// need not provide redundancy the leader lacks. Missing reports grant no |
There was a problem hiding this comment.
Could this say why one unhydrated collection is enough to exempt the whole replica, i.e. that we don't count a replica as useful until it's fully hydrated?
942a933 to
ea704c4
Compare
QA LLM Review1. MEDIUM -- A leader-waived collection that the incoming generation already hydrated exempts its replicas from the stability gate
A compute collection that every leader replica reports unhydrated only goes into DetailsScenario: a single-replica cluster with indexes
So more progress on the incoming side gets the replica less scrutiny. That contradicts the description's rule that the stability gate ignores a leader-waived collection. In multi-replica clusters, every replica hosting such an Suggested fix: ignore the collection whenever the leader waiver applies, regardless of local hydration. Then the replica rule only fires on collections that some leader replica did hydrate. This is a superset of today's condition, because - if (readiness != CollectionReadiness::Ready || !collection_hydrated)
+ if (readiness != CollectionReadiness::Ready || unhydrated_on_leader)
&& matches!(collection_type, CollectionType::Compute) |
Motivation
Closes: SQL-696
A rollout can wait on hydration the leader does not have either: a new collection the leader has not hydrated, or an undersized replica that never hydrated on the leader. The incoming generation should not need more hydration than the leader.
Description
The caught-up check reads the leader's
mz_compute_hydration_timesnext to its frontiers and waives local hydration only on explicit evidence:Missing reports never waive anything. Storage collections are unchanged. The hydration source is protected from builtin migration, so the check keeps reading the leader's shard. There is no flag: skip-catchup and manual promotion remain the escape hatch.
Verification
Unit tests pin both waiver rules, including mixed, missing, and absent reports. Two 0dt workflows exercise them with a nonzero stability period. A replica-scoped hydration-concurrency override suspends both leader replicas for the collection rule, and only one leader replica for the replica rule while the other hydrates the same index. Both verify promotion readiness while the corresponding incoming index remains unhydrated.
Release notes