docs: Hydration details and concepts page - #38168
Conversation
| materialized view (public preview) and replacing the existing view in | ||
| place](/transform-data/updating-materialized-views/replace-materialized-view/) | ||
| may be simpler, but briefly reduces freshness. | ||
|
|
There was a problem hiding this comment.
Did not include compute_hydration_concurrency as a strategy
| Hydration primarily impacts memory usage, and its speed scales with cluster | ||
| size. Some hydration-related strategies you may want to consider: | ||
|
|
||
| - Add an [`AUTO SCALING STRATEGY (ON HYDRATION)`](/sql/alter-cluster/) to your |
There was a problem hiding this comment.
probably will move it to the hydration-details.yml later on.
| then removes it once a steady-size replica catches up. You pay for the burst | ||
| replica while it is provisioned, but not at steady state. | ||
|
|
||
| - Split materialized views and indexes across multiple clusters. Each cluster |
There was a problem hiding this comment.
I like this! I'd recommend checking out what @tylerhartwig did to shard MVs well. There's a number of strategies you can employ and I think sharing more details would help
There was a problem hiding this comment.
Yup. added in patch: f828c86 (discussion https://materializeinc.slack.com/archives/C04LQV4KUR4/p1786542843367309)
| require a significant amount of CPU and memory. | ||
|
|
||
| - During hydration (both initial and subsequent rehydrations), materialized | ||
| - During hydration (both initial hydration and subsequent rehydrations), materialized |
There was a problem hiding this comment.
I'm not sure where to put this, but rehydration technically uses more memory! I'll let @antiguru confirm, but my understanding is:
- If you create a cluster for a first time with 10 MVs, you'll use X memory
- If you restart that cluster and have to rehydrate the 10 MVs, you'll need 2X memory
It might be worth splitting up those concepts and specifying the memory requirements for both
There was a problem hiding this comment.
Oh, heh .. I think I clobbered that line where we stated "materialized views require memory proportional to both the input and output." (i.e., your 2X 😄 ) But, will also wait for confirmation.
There was a problem hiding this comment.
Replaced with the table (with the corrected details) so that the pages are in sync.
antiguru
left a comment
There was a problem hiding this comment.
Left some comments.
My main concern is that we're getting less precise about clusters and cluster replicas. Clusters are a collection of cluster replicas, but themselves have no concept of start/stop/(re)hydrate/crash etc. Cluster replicas are the concept that has the life cycle, and represent physical resources. Their properties are immutable. The text in this PR uses clusters and cluster replicas interchangeably, but they're really two distinct concepts. Keeping them separate creates clarity.
I don't think this is specific to this page, as we're causing confusion with the CREATE CLUSTER syntax that papers over this detail.
| ## Objects and hydration | ||
|
|
||
| Hydration is per replica. When a trigger above occurs, the objects on the | ||
| affected replicas hydrate as described below. A restart re-hydrates a cluster's |
There was a problem hiding this comment.
A restart of what? Needs more specific, because only a restart of a replica causes (re)hydration, a environmentd restart causes reconciliation.
There was a problem hiding this comment.
Will do a sweep of this page to be more precise (probably will have the update by early tomorrow morning ... as need to shift attention to a few other tasks today).
| - Add an [`AUTO SCALING STRATEGY (ON HYDRATION)`](/sql/alter-cluster/) to your | ||
| cluster. With this strategy, Materialize automatically provisions an extra, | ||
| larger replica (a burst replica) while the cluster has un-hydrated objects, | ||
| then removes it once a steady-size replica catches up. You pay for the burst | ||
| replica while it is provisioned, but not at steady state. |
There was a problem hiding this comment.
What happens if the stead-size replica is in an OOM loop?
There was a problem hiding this comment.
added details for this. resize ... burst will serve (had claude verify)
There was a problem hiding this comment.
OLTP sources (PG, MySQL, SQL Server) don't run on multiple replicas, so there are complications. Those sources run on the replica with the lowest ID (the oldest replica).
If a hydrating object is causing an OOM loop on the steady-size replica, a burst-replica (or a second replica created by the user), will allow the hydrating export to run on the second replica, but the OLTP source(s) will stay on the OOM looping replica and not be able to make progress.
The current recommendations:
- Avoid colocating (e.g. don't put PG and Kafka upsert on the same cluster).
- Don't add a second replica, replace the existing replica with a larger one.
| - Split materialized views and indexes across multiple clusters. Each cluster | ||
| hydrates its own objects independently, which distributes the memory required | ||
| for hydration, lets objects on different clusters hydrate in parallel, and | ||
| limits how much must re-hydrate when a single cluster restarts. |
There was a problem hiding this comment.
Precision: Clusters are resource-less objects, cluster replicas can restart.
| - For a single materialized view, creating and hydrating a [replacement | ||
| materialized view (public preview) and replacing the existing view in | ||
| place](/transform-data/updating-materialized-views/replace-materialized-view/) | ||
| may be simpler, but briefly reduces freshness. |
There was a problem hiding this comment.
I'm not sure I follow this part. Does it say that a replacement MV should be on the same cluster, or on a separate cluster?
There was a problem hiding this comment.
Can clarify that it can be on either the same/different since it's like creating any other mv ... (like people should use the same cluster judgement as they do for other objects).
| The burst-replica and blue/green strategies run extra replicas alongside the | ||
| existing ones, as do a resize or a zero-downtime upgrade. During the overlap, | ||
| the cluster temporarily uses additional resources, up to roughly double during a | ||
| resize or upgrade. Account for the additional cost and, on self-managed | ||
| deployments, the additional capacity required. |
There was a problem hiding this comment.
Not double, but the sum of the replica sizes. It's billed exactly, not approximately.
There was a problem hiding this comment.
I just left off ", up to ..." so that if flows better "... uses additional resources. Account for the ..."
| - **Memory Use**: Its memory scales with those arrangements, so joins and | ||
| aggregations are the memory-hungry cases while a pure map or filter view is | ||
| nearly stateless. It also holds an output-side buffer that scales with the | ||
| view's output volume and is largest during hydration. |
There was a problem hiding this comment.
Maybe more precisely: Its memory use scales with the view's definition and, during hydration, with twice the output size.
| - **Memory Use**: Its memory is proportional to the indexed data plus those | ||
| arrangements, and is held for as long as the index exists. |
There was a problem hiding this comment.
Do we want to talk about retain history? It affects the indexes' size, roughly based on the update rate.
There was a problem hiding this comment.
since we advise against it for indexes (and there have been discussions about its use w other objects as well), will not mention now. Can revisit in the future.
| - **When**: On creation only. Because a subscription is transient and tied to | ||
| its session, it is not rehydrated on restart and is not reported in | ||
| `mz_hydration_statuses`. |
There was a problem hiding this comment.
Not entirely true: Adding a new replica to a cluster will hydrate the same subscribe dataflow.
| - **Memory Use**: Its memory scales with those arrangements, so joins and | ||
| aggregations are the memory-hungry cases while a pure map or filter view is | ||
| nearly stateless. It also holds an output-side buffer that scales with the | ||
| view's output volume and is largest during hydration. |
There was a problem hiding this comment.
Addendum: MVs have the property that their first startup memory cost is proportional to
Co-authored-by: Moritz Hoffmann <antiguru@gmail.com>
| - **When**: Hydrates on creation and on every replica (re)start or cluster resize. | ||
|
|
||
| - **What**: Loads a full copy of its input snapshot into the arrangement that | ||
| feeds the sink before it can emit. |
There was a problem hiding this comment.
As an fyi for the sources & sinks team: Claude at first refuted this because it got confused by the comment https://github.com/MaterializeInc/materialize/blob/main/src/storage/src/sink/kafka.rs#L771-L780 (which is referring to something else). I had it double check that we're not talking about different things ... and then, it corrected itself.
| Hydration behavior: '{{< include-from-yaml data="hydration-details" name="materialized-view" >}}' | ||
| - Object: "Indexes" | ||
| Hydration behavior: '{{< include-from-yaml data="hydration-details" name="index" >}}' | ||
| - Object: "Kafka **upsert** sources and associated read-only tables/subsources" |
There was a problem hiding this comment.
Upsert and debezium envelopes need to perform hydration, does this imply coverage for both?
There was a problem hiding this comment.
Yes. Discussed with @maheshwarip and we'll be going with upsert sources (but when talking about envelopes, we distinguish them. I could go Kafka upsert sources (including those using Debezium envelopes) or something like that.
| - Add an [`AUTO SCALING STRATEGY (ON HYDRATION)`](/sql/alter-cluster/) to your | ||
| cluster. With this strategy, Materialize automatically provisions an extra, | ||
| larger replica (a burst replica) while the cluster has un-hydrated objects, | ||
| then removes it once a steady-size replica catches up. You pay for the burst | ||
| replica while it is provisioned, but not at steady state. |
There was a problem hiding this comment.
OLTP sources (PG, MySQL, SQL Server) don't run on multiple replicas, so there are complications. Those sources run on the replica with the lowest ID (the oldest replica).
If a hydrating object is causing an OOM loop on the steady-size replica, a burst-replica (or a second replica created by the user), will allow the hydrating export to run on the second replica, but the OLTP source(s) will stay on the OOM looping replica and not be able to make progress.
The current recommendations:
- Avoid colocating (e.g. don't put PG and Kafka upsert on the same cluster).
- Don't add a second replica, replace the existing replica with a larger one.
https://preview.materialize.com/materialize/38168/concepts/hydration/
Part 1 of Hydration content:
Once approved and merged, Part 2 will include the approved details in appropriate places.