Repository navigation
console: Add freshness percentile statboxes and p99 - #40127
Conversation
QA LLM Review1. MEDIUM -- Summary says "No readings in this range" while the objects subscribe is still loading
Before the all-objects subscribe delivers its snapshot, Details
I reproduced it by rendering Fix: treat |
|
Re the QA review's MEDIUM finding (Summary shows empty values while the objects subscribe is still loading): fixed in ea5ba4b. The Summary's query waits for the objects snapshot and the cards show their loading state until then. A page test covers a link opened before the snapshot lands. (The "No readings in this range" caption it quoted was removed in 8b0d5f9. The cards showed "—" in that window until this fix.) |
ea5ba4b to
83d4d05
Compare
There was a problem hiding this comment.
Great work keeping the Summary and the controls up across a range change, and the statboxes tests read the way the page does. Suites 79/79, eslint and tsc clean on this head, and the QA review's Summary-before-snapshot finding is fixed here. Small nits plus two product questions, none blocking.
- Is there a Figma for the Summary and the new control rows? The screenshot helps, but a design link in the description is what we usually go by.
- Cost note for later: the Summary's TopK keeps objects × 1,441 rows at 24 hours plus one
row_numberover them, so on the biggest clusters it will take a few seconds per refresh, on top of the IN-list cliff. Fine for now, worth folding into CNS-186.
For whoever runs it in a browser: "Not queryable" at heading size inside a two-column card at narrow widths, and the Summary grid in dark mode.
| { key: "peak", label: "pMAX" }, | ||
| ]; | ||
|
|
||
| const StatCard = ({ |
There was a problem hiding this comment.
Could we reuse MetricCard? Same label-over-value tile on Chakra Card. If it moves to components/, both pages share one.
There was a problem hiding this comment.
Done in 4aeaf44. MetricCard moved to components/ unchanged, and the statboxes pass the theme's tertiary background to keep their grey fill.
| borderWidth="1px" | ||
| borderColor={colors.border.primary} | ||
| borderRadius="lg" | ||
| background="chakra-subtle-bg" |
There was a problem hiding this comment.
Nit: chakra-subtle-bg isn't used anywhere else in the Console; surfaces come from the theme (colors.background.secondary). How does this look in dark mode?
There was a problem hiding this comment.
Covered by 4aeaf44. The fill now comes from colors.background.tertiary, which matches the old grey in light and dark.
| }), | ||
| columnHelper.accessor("p90", { | ||
| header: "p90", | ||
| columnHelper.accessor("p99", { |
There was a problem hiding this comment.
At the default 1 hour every row's p99 equals its pMAX (61 readings), so the table opens with two identical columns. Worth a product check: grey p99 under 100 readings, or default the range to 24 hours? CNS-213 also had product suggesting Now, p90 and pMAX, so flagging that this settles it.
There was a problem hiding this comment.
Deliberate. Product wants p99, and the page's fine print explains the overlap at 1 hour.
| * Pooling dilutes one slow object with the healthy ones' minutes, so these | ||
| * describe the scope, not any one object, and never judge by the threshold. | ||
| */ | ||
| const FreshnessStatboxesInner = ({ |
There was a problem hiding this comment.
Nit: we use the Inner suffix for a suspense split, and here it's the memo target. Maybe FreshnessStatboxesBase, or memo inline.
There was a problem hiding this comment.
Done in c8dcc90, as ...Base. I renamed FreshnessTableInner the same way so the two match.
| padding="3" | ||
| > | ||
| <Text textStyle="text-ui-reg" color={colors.foreground.secondary}> | ||
| Highlight objects that had a |
There was a problem hiding this comment.
Nit: "had a current freshness exceeding" reads oddly for the current option. Maybe "whose [current] freshness exceeded".
There was a problem hiding this comment.
Done in c8dcc90: "whose [statistic] freshness exceeds" for current and "exceeded" for the others.
| represents the worst minute during a 6-minute span. | ||
| </Text> | ||
| <Text textStyle="text-small"> | ||
| A percentile needs enough readings to differ from pMAX: about 100 |
There was a problem hiding this comment.
Nit: five paragraphs is a lot of fine print. I'd keep the two that change what a reader does (what a percentile ranks, why p99 equals pMAX at 1 hour) and move the graph-vs-Now mechanics to a tooltip. Not blocking.
|
Re the Summary cost note: I checked scope sizes against catalog snapshots from the last 7 days, covering 90 production deployments (staging excluded). Objects per cluster, as cluster mode loads them (617 clusters): 457 have 20 or fewer, 151 have 21–200, 5 have 201–330, 3 have 331–1,000, and 1 has more than 1,000 (the largest, 1,315). Objects per schema, as schema mode loads them (1,213 schemas): 1,081 have 20 or fewer, 126 have 21–200, 1 has 201–330, 3 have 331–1,000, and 2 have more than 1,000 (the largest, 1,882). The snapshot records no schema for indexes, so these run slightly low. The scope query measured locally at 24 hours on v26.47.1: 30 ms for 20 objects, 171 ms for 200 and 253 ms for 300, about 0.85 ms per object. That puts the largest production cluster near 1.1 s per refresh and the largest schema near 1.6 s. The multi-second cases are in staging, which has clusters of up to 10,000 objects and schemas of up to 28,908. A busy production |
83d4d05 to
c8dcc90
Compare
|
No Figma; the screenshot is the reference. Checked in a browser: "Not queryable" wraps inside the card at narrow widths and the card grows to fit. The Summary reads fine in dark mode. |
1777339 to
6241681
Compare
- A collapsible Summary section shows the scope's pooled p90, p99, p99.9 and pMAX, from the pooled query in the previous commit. Its pMAX also takes in the latest minute, so like the table's pMAX it updates once a minute.
- The threshold menu offers current, p90, p99 and pMAX. The table gains a p99 column, ordered Now, p90, p99, pMAX.
- The controls split into a scope row ("[types] within [cluster] over the last [range]") and a highlight row below the Summary. Both render before the data loads, so a range change cannot remove them. The header is no longer pinned, and the redundant headline is removed.
- The page note explains percentiles of 1-minute readings and why p99 equals pMAX at short ranges.
Closes: CNS-213
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Drop the captions from the Summary cards and give them the subtle background. "—" and "Not queryable" remain as values. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Before the objects subscribe delivers its snapshot, the Summary saw an empty scope and showed "—" in every card. It now waits, showing its loading state, and sends no query until the list is complete. Co-Authored-By: Claude Opus 5.5 <[email protected]>
The scope percentile hook returns null for an unreadable rank, so the statboxes map it with lagOrUnreadable, as computeStats does. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Moves MetricCard to components/ and renders the freshness statboxes with it. An optional background lets the statboxes keep their grey fill, now from the theme's tertiary surface. Co-Authored-By: Claude Opus 5.5 <[email protected]>
The prompt shown before a cluster or schema is chosen uses body text, and the fine print waits for a selection, since it explains numbers that are not on screen yet. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Name the memoized statboxes and table components ...Base, keeping Inner for suspense splits, and word the threshold sentence "whose [statistic] freshness exceeds" for current and "exceeded" otherwise. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Ticks show 24-hour local time and zone over the date, as the connector graphs do, on round clock times spaced so labels neither overlap nor clip. MultilineXTick moves to graphComponents for both. Co-Authored-By: Claude Opus 5.5 <[email protected]>
The y-axis top is rounded up from 110% of the highest reading, so a peak on a round value no longer draws along the graph's top border. Co-Authored-By: Claude Opus 5.5 <[email protected]>
The graph runs from the start of the range to now, advanced each minute, with a point per bin so a line breaks where its object has no reading. Lines are clipped to the plot. Part of CNS-207. Co-Authored-By: Claude Opus 5.5 <[email protected]>
It left a wide gap before "over the last" when the selected name, or the placeholder, was shorter than the minimum. Co-Authored-By: Claude Opus 5.5 <[email protected]>
6241681 to
37f44ce
Compare
The scope row becomes [Cluster ▾ | picker] [Time range] [Object types] using a new ScopedSelect, styled like the sidebar's links so the scope menu reads as a control beside the static labels. Its whole purple area opens the menu. One frame now holds the highlight row, graph and tables. SearchableSelect gains frameless, and LabeledSelect labels no longer wrap. Co-Authored-By: Claude Opus 5.5 <[email protected]>
The Freshness page's p90 ranked the graph's 60 bucket peaks, so its meaning changed with the range. At 24 hours it was the 7th worst 24-minute peak. It now ranks every 1-minute reading in the range, in SQL. - `buildObjectLagPercentilesQuery` returns each object's peak, p90, p99 and p99.9 by nearest rank. Each object keeps only the worst readings p90 needs (a `LATERAL` TopK), so no query sorts every reading. At 24 hours it took 49 ms for 20 objects, 411 ms for 200 and 828 ms for 400 in a local benchmark (v26.48), reading the lag index through the id list from #40306. - `buildScopeLagPercentilesQuery` does the same over all objects' readings pooled. Its hook has no caller here; the statboxes in the next PR use it. - `computeStats` takes the percentiles from the query. pMAX comes from the same query, so no percentile can exceed it. pMAX and the graph are separate fetches on one schedule, so they can briefly disagree. The UI half is the next PR in this stack, #40127: p99 and p99.9 columns, statboxes, and the page's fine print, which still describes the old p90 until then. Merge the two together. Tests: SQL tests check every percentile against a reference computed from the raw rows, covering young objects, unreadable readings and readings outside the range. Unit tests cover the NULL-to-unreadable mapping and `computeStats`. This release will compute the Freshness page's percentiles over every 1-minute reading in the selected range. Closes: [CNS-212](https://linear.app/materializeinc/issue/CNS-212) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <[email protected]>
QA LLM Review (Post Merge)@jdonelson — an automated review of commit 1. MEDIUM -- The graph drops the newest fetched minute whenever
|
Stacked on #40123, which adds the percentile queries. Merge the two together.
MetricCard, moved tocomponents/.Tests:
FreshnessStatboxes.test.tsxcovers the values, "Not queryable" and missing values, the latest minute raising pMAX, and a failed request. A page test checks that the Summary and controls render before the data arrives. Unit tests coverlatestPeakand the p99 option. Graph tests cover the clock-time labels, tick spacing and edges, the headroom, and clipping.fitToRangeanduseNowhave their own tests.This release will add p99 to the Freshness page's threshold menu and table, and a Summary of the scope's p90, p99, p99.9 and pMAX, and show the graph's full selected range with clock-time labels.
Closes: CNS-213
Closes: CNS-207
🤖 Generated with Claude Code