Repository navigation
console: show subsource and source table freshness in Maintained Objects - #40105
Conversation
Maintained Objects hid subsources and every table, so a source's per-table freshness was invisible. The source row's own freshness tracks its progress collection, which advances on a timer, so a source could look fresh while its tables lagged by minutes. The critical path also dropped table inputs, so an MV over source-fed tables never reached the lagging table. List subsources and tables created with CREATE TABLE ... FROM SOURCE with their own freshness and ingestion status, on their source's cluster. Add a per-table freshness section to the source drawer, follow source-fed tables in the critical path, and read the drawer's lag history without the cluster join that dropped these objects. Closes: CNS-210
A subsource or source-fed table now walks back to its source, so its breakdown separates the source's lag from its own ingestion delay, and an MV's path continues past the table to the source. A source has no upstream inside Materialize, so its critical path draws the subsources and tables it writes to, with the one trailing it the most marked as the bottleneck. The graph's node panel carries the cluster metrics the separate source section used to show.
The source view's per-export lag came from a catalog query that scanned the whole lag history and the object catalog to label its rows. It now reads the exports' binned lag history by id, an index lookup, and labels the nodes from the list. An unreadable export sorts first so the node cap cannot hide a table that is still snapshotting. The subsource and source-table to source mapping is one builder shared by the hydration feed, the critical path and the workflow graph. The critical path graphs and the object drawer show an error instead of spinning when their query fails.
The source node took its lag from the list, which can describe a later minute than the bucket its subsources and tables are read from.
Canvas passed the graph's numeric size to Chakra, which reads a bare number as a theme size token when one matches. A single-row critical path is 56px tall, the size token for 14rem, so it rendered 224px tall and clipped its own node. The source critical path draws a single row for any source without subsources or tables.
jdonelson
left a comment
There was a problem hiding this comment.
Really nice to see subsources and tables get their own freshness, and moving the parent lookup into one buildSourceExportsQuery cleans up three copies. Unit tests pass (39/39) and typecheck is clean. I didn't run the SQL tests. One design question on the critical path edge and one nit, otherwise looking good.
| WHERE src.type IN (${MAINTAINED_OBJECT_TYPES_SQL}) | ||
| OR t.source_id IS NOT NULL | ||
| UNION ALL | ||
| SELECT source_id, export_id FROM source_exports |
There was a problem hiding this comment.
I wonder if the edge from a source to its subsources and tables should look different from the other edges in the critical path. The other edges mean "computed from": an MV can't be fresher than its input, so a slow input holds it back. A subsource isn't computed from its source. Both are written by the same ingestion, and the source's lag doesn't carry down to it. With the same arrow, the graph reads as if it does.
There was a problem hiding this comment.
Good call, it isn't a "computed from" edge, so it's now dotted, with a one-line legend under the graph (8389c8d). The source's lag does carry down for CREATE TABLE … FROM SOURCE tables, though. The source's own collection is the ingestion's progress collection (progress_id() in src/catalog/src/memory/objects.rs), and its tables are reclocked against it, so a table can't be fresher than its source. That's what the "1s from source + 2s self-delay" split relies on. It doesn't hold for legacy subsources, where the source is just another export and progress lives in <source>_progress, so those no longer get the edge.
| * True for a source and for the subsources and tables it ingests into. These | ||
| * report ingestion status in place of replica hydration, and have no upstream | ||
| * dataflow for the critical path to walk. |
There was a problem hiding this comment.
Nit: subsources and tables do have an upstream for the critical path now (their source). Since this only picks the status to show, maybe drop the last clause?
The edge from a source to one of its subsources or tables isn't "computed from". For a source whose tables come from CREATE TABLE ... FROM SOURCE, the source's own collection is the ingestion's progress collection and its tables are reclocked against it, so the source's lag bounds theirs. Those edges are now marked and drawn dotted, with a legend under the graph. A legacy source keeps its progress in a separate progress subsource and is itself another export of the same ingestion, so its lag says nothing about its subsources. Legacy sources no longer get these edges, and a subsource without one still draws alone with its source's cluster metrics.
martykulma
left a comment
There was a problem hiding this comment.
lgtm, thanks @leedqin!
Motivation
Closes: CNS-210
Maintained Objects hid subsources and every table, so the only freshness a source showed was the source row's own. That number tracks the source's progress collection, which advances on a timer as it reads the upstream position, whether or not table data has been written. A source can therefore read 1s while its subsources and tables lag by a minute or more, and the console gave no way to see the per-table number. This also covers CNS-190.
The critical path had the same blind spot. It only followed edges between sources, MVs, indexes and sinks, so an MV reading
CREATE TABLE ... FROM SOURCEtables stopped at the MV and never reached the lagging table. Sources had no critical path at all.With Postgres sources now snapshotting a newly added table without holding back the rest, users need to see each table's freshness and snapshot status to see that improvement.
Description
List
subsourceortable. Each carries its own freshness and ingestion status, its source's cluster, and its source's connector type. A table that is still snapshotting reads "Snapshotting" next to siblings that read "Running". The Object Type, Cluster and Status filters cover them.Drawer
Critical path
source,subsourceortable.Data and cost
buildSourceExportsQuery. The hydration-aggregate SUBSCRIBE, the critical path and the workflow graph all use it.mz_tables_indscan and anmz_object_dependenciesjoin to that page-scoped subscribe. There is no new subscribe or poll.mz_wallclock_global_lag_recent_history(an index lookup) and labels nodes from the list.mz_clusterson the object's own cluster, so it returned nothing for subsources and tables.Fix in a shared component
Canvasnow sizes the graph in pixels. Chakra read the graph's numeric height as a theme size token when one matched, so a single-row graph (56px, the token for 14rem) rendered 224px tall and clipped its own node.Not in this PR:
fetchSourceStatisticsresolves stats throughmz_sources. Subsources get a percentage.snapshot_committedreports. The per-table rows show which table it is.Screenshots
From a local Materialize.
shop_srcwrites four tables and is snapshotting a fifth,page_views. The 15M-row snapshot saturates the small local replica, which is why the other tables lag too.crm_srchas a legacy subsource, andorder_summaryreads one table from each source.Source: per-table freshness and status, and the critical path out to its tables.
Source-fed table: the critical path back to its source.
Materialized view: the path now runs through the subsource and table to their sources.
Verification
Tests added or changed:
queries.test.ts(new) covers the list builder:MaintainedObjects.test.tsx: a snapshotting table renders next to a running one from the same source.criticalPathRenderable.test.tscovers the source critical path:hydrationAggregate.test.sql.ts:criticalPath.test.sql.ts:I checked all views in a browser against a local Materialize with Postgres sources. I also ran
EXPLAINonmz_catalog_serverfor the changed queries.🤖 Generated with Claude Code