catalog_server: key mz_cluster_replica_frontiers_ind on (object_id, replica_id) - #36978
catalog_server: key mz_cluster_replica_frontiers_ind on (object_id, replica_id)#36978antiguru wants to merge 1 commit into
Conversation
Query plans: before (
|
|
|
We've discussed with @antiguru on zoom, and a thing that could make such changes much easier to evaluate is to have an slt (maybe auto-generated), which would show the EXPLAINs for all the objects on @SangJunBak, this could be considered as a work item in the |
|
Could we use catalog_server: as the commit/pr prefix? We also have an actual catalog component so that can be confusing, please. |
|
@ggevay Sounds like a good idea! Nick has created a ticket here https://linear.app/materializeinc/issue/SQL-351/add-an-slt-maybe-auto-generated-showing-the-explains-for-all-the |
4cab6da to
f8da5ba
Compare
The `(object_id, replica_id)` join in `mz_hydration_statuses` was building its own full-relation arrangement of `mz_cluster_replica_frontiers` because the index was keyed on `object_id` only. That arrangement grows very large under replica churn (observed ~47 GB on a production mz_catalog_server). Widening the index key lets the join reuse the index; the remaining `object_id`-only consumers all filter to `write_frontier IS NULL`, so their arrangements stay small. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
f8da5ba to
2db552d
Compare
Motivation
Fixes CPU-112. On
mz_catalog_server, themz_hydration_statusesindex dataflow joinsmz_cluster_replica_frontierson(object_id, replica_id). Becausemz_cluster_replica_frontiers_indwas keyed onobject_idonly, that join built its own full-relation arrangement ofmz_cluster_replica_frontiers. Under replica churn this arrangement accumulates a lot of uncompacted history — observed at ~47 GB on a productionmz_catalog_serverreplica via continuous profiling.Description
Widen the index key to
(object_id, replica_id)so the join reuses the index instead of duplicating the relation into its own arrangement. The other consumers that join onobject_idonly (mz_hydration_statusesindex/MV branches andmz_compute_hydration_statuses) all filter towrite_frontier IS NULL, so when they build their ownobject_idarrangements those stay small. Net effect onmz_catalog_server: two full-relation arrangements ofmz_cluster_replica_frontiersbecome one (the index) plus two small filtered ones. A comment on the index records the rationale so the key isn't naively narrowed back.Caveat: a
(object_id, replica_id)index cannot serve anobject_id-only equality lookup (Differential arrangements key on the full tuple), so an ad-hoc point lookup such asSELECT ... FROM mz_cluster_replica_frontiers WHERE object_id = X(withoutreplica_id) no longer uses this index and falls back to a scan. This is minor — the relation is small per object — and is the intended trade-off, but it's a visible behavior change beyond the maintained dataflows.Verification
The plan impact is captured by the
catalog_server_explain.sltsnapshot (#36995), regenerated here. It shows exactly three changed plans, all expected:mz_cluster_replica_frontiers_ind— the new key.mz_hydration_statuses_ind—l9reuses the index (its fresh(object_id, replica_id)arrangement is gone);l1builds a smallwrite_frontier IS NULL-filteredobject_idarrangement.mz_compute_hydration_statuses— same small-filtered-arrangement shift; this is an unindexedSELECT *view, so it is only surfaced by the snapshot, not by anEXPLAIN INDEXsweep.No other
mz_catalog_serverobject's plan changes.mz_catalog_server_index_accounting.sltandindexes.tdare updated for the new key; sqllogictest andcargo clippy -p mz-catalogpass.🤖 Generated with Claude Code