Repository navigation
The server page labelled page sums as totals, drew one instant as a trend, and four tools called their capture time by another name: get_pg_database_stats' totals ride the row statement, the memory-grant pair renders window[] beside its newest snapshot, get_pg_io_stats' page count is combinations_returned, and four latest reads stamp captured_at on both SKUs (partial #3653) - #3697
Merged
Merged
Conversation
…rend, and four tools called their capture time by another name: get_pg_database_stats' totals ride the row statement as window aggregates, the memory-grant pair renders its window[] beside its newest snapshot, get_pg_io_stats' page count is combinations_returned, and get_database_sizes / get_running_jobs / get_server_properties / get_session_stats stamp captured_at on both SKUs (partial #3653)
Closed
39 tasks done
erikdarlingdata
enabled auto-merge (squash)
September 19, 2026 06:26
This was referenced Sep 19, 2026
erikdarlingdata
added a commit
that referenced
this pull request
Sep 20, 2026
…ption names its reader's two documented traps, the web catalogue's duration-trend line says rates and never percentiles, and server-tabs.js reads every Stamped tool by captured_at (partial #3653 A15/A16) (#3801) The six drifts item 17 still listed were all delivered before this branch (#3665, #3696, #3697); three of the fixed texts had nothing holding them. Pins only, no wire change: - DarlingMcpAgToolsSurfaceTests.Description_SurfacesTheReadersTwoDocumentedTraps anchors on DarlingAgReader's own bolded paragraph headers, then holds the description and the instructions paragraph to each trap's mechanism, consequence and tell (NO row for a server with no AGs / last non-empty snapshot / collection_time; lag and queue depth NOT banded / secondary_lag_seconds / is_suspended / 0 lag while suspended). - DarlingMcpTrendToolsSurfaceAndSqlTests.QueryDurationTrend_CatalogueLine_NamesTheRatesThePayloadCarries_NotPercentiles holds the catalogue line to ms/sec + executions/sec + 'not percentiles', SerializeTrend to the two rates it projects, and the whole catalogue to 'percentile' only ever negated. - ServerPageTabsTests.TheStampedReads_AreReadByTheirStamp_NeverTheRetiredKey derives the Stamped + SearchBound roster from McpLatestSnapshotStampTests.LatestTools, slices every stat/table/fanout call by bracket balance, and refuses key: "collection_time" in any descriptor array bound to one of them; the four named reads must be reached; SESSION_STATS must show captured_at. Each red against its defect, then green, in the Mac harness.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The server page labelled page sums as totals, drew one instant as a trend, and four tools called their capture time by another name
Partial for #3653 — the wwwroot twins item (all three clauses) and the
StampedUnderCollectionTimeclause of A15/A16. Does not close the issue.Three of the payload-contract censuses that landed in the #3541 campaign each carried exactly ONE stated allowance, and each allowance pointed at the same file:
Darling/PerformanceMonitor.Darling.Service/wwwroot/js/pages/server-tabs.js, the web server page, which sat outside those lanes' boundaries. This PR is the lane that owns that file, so it retires the allowances from the tool side and the page side in one diff — which is the only way a rename of a wire key can land without a release where one half lies.1.
get_pg_database_stats: the totals were the page's, honestly labelled as such — now they are the window'sThe tool fetched its
limitrows,rows.Sum(...)'d them, and published the sums astotal_temp_files/total_temp_bytes/total_deadlocks. #3613 (the A7 percent lane) found this and — because relabelling the web tile was outside its boundary — carried it honestly by disclosure: alimit_reachedflag, a note saying the totals covered only the databases returned, adatabase_countthe tile labelled "Databases returned", and a ratio already spelledcache_hit_pct_of_returned. The A7 census (McpPageContractTests.NoPagedTool_DividesByOrPublishesAPageSumAsATotal_OnEitherSku) named those three keys as its one stated allowance, with a control that fails the day the allowance stops being used.The window's figures are free on the statement the read already runs.
DarlingPgDatabaseReader.PgDatabaseSqlgains seven columns on its final select list —CAST(SUM(coalesce(SUM(d_temp_files), 0)) OVER () AS bigint) AS window_total_temp_filesand its siblings for bytes, deadlocks,blks_hit,blks_read;COUNT(*) OVER ()aswindow_database_count; andSUM(count(*) FILTER (WHERE reset_here) + count(*) FILTER (WHERE rewound_here)) OVER ()aswindow_reset_count. PostgreSQL evaluates a window aggregate over the grouped result afterGROUP BY/HAVINGand beforeORDER BY/LIMIT, so each sums every database the window holds under the SAME "moved or was reset" filter the rows are drawn from, cannot drift from them (one statement), and costs one pass over a result the query has already grouped and is about to sort. The reader returns a page record (PgDatabasePage: rows + the window figures) — the #3613 shape, chosen for the same reason: these are facts about the window, and the Viewer's projection census maps every ROW property to a display column. The Viewer'sGetPgDatabaseStatsAsynckeeps its signature and returns.Rows.The tool, in the A3 dialect, through a new
BuildDatabaseStatsJsonsplit-out (theBuildIoJsonprecedent, so the wire shape is assertable without a store):limit + 1and observestruncated(page.Rows.Count > limit); the inferredlimit_reached(Count >= limit, MCP pages now say what bounded them: caps bind to the caller's limit, truncation is detected not inferred, and no page count is called a total (#3541 A3) #3594's class, the one get_pg_server_config counted the page and called it the server: non_default_count is now the snapshot's, computed above the cap, truncated is observed off a limit + 1 fetch, and its three sibling pages stop inferring truncation from a full page (#3653 A3 residue) #3679 removed from the sibling PostgreSQL pages) is gone;total_temp_files/total_temp_bytes/total_deadlocksare the WINDOW's; the page's own sums travel asreturned_temp_files/returned_temp_bytes/returned_deadlocks;database_countis the window's (how many databases moved or were reset — what a count beside window totals has to mean; thenon_default_count/non_default_returnedpattern from get_pg_server_config counted the page and called it the server: non_default_count is now the snapshot's, computed above the cap, truncated is observed off a limit + 1 fetch, and its three sibling pages stop inferring truncation from a full page (#3653 A3 residue) #3679),databases_returnedis the page's,orderis named;cache_hit_pctis the cluster-wide ratio over the window's blocks — the figure the old comment declined to compute ("a second unfiltered aggregate on every call");cache_hit_pct_of_returnedSTAYS, because its name was the one honest spelling before, is still true, and the pair beside each other shows how much the cap hid;statistics_were_reset_in_windowis now true of the window (a database reset OFF the page makes every window total a lower bound; the old page-scoped flag would have said no reset happened);top_spillerkeys on the window total — the page is ordered by spilled bytes, so its first row IS the window's biggest spiller whenever anything spilled;limitparameter repeats the bound; thenoteon every payload carries "TOTALS ARE OF THE WINDOW, NOT OF THE PAGE" with the window's database count.PG_DATABASE_STATSon the page: "Databases active" (database_count) beside "Databases returned" (databases_returned), the total tiles' labels now true, "Cache hit %" readscache_hit_pct, "Window held more" readstruncated; the panel subtitle is "totals over every database that moved". The allowance set in the census is now empty — kept as an empty set rather than deleted, with the paragraph saying why, so the next tool that needs one has to write its reason there and the still-used control keeps refusing a stale one.2.
get_pg_io_stats.combination_count→combinations_returned#3613 kept
combination_countonly because the web I/O tile read it by key. It IS a page count (the description said so,truncatedsat beside it) — but a bare_countbeside two window totals reads as the window's third, and every other paged tool spells its page count<noun>s_returned(queries_returned,wait_events_returned,waits_returned,settings_returned, …). Renamed to the dialect; the "kept for the JS" caveat is gone from the tool's comment; the tile reads the new key and is labelled "Combinations returned"; the two pins (DarlingMcpPgPercentDenominatorTests,McpPageContractLivePostgresTests) read the new key.3. The Memory Grants LINE chart drew one instant as a trend
server-tabs.jsdrewget_memory_grants.grants[]as a line chart keyed oncollection_timeunder the page's range label. Since #3637grants[]is the NEWEST snapshot in the window — one row per pool, every row carrying the same stamp — so the "series" was one x-position with a dot per pool, captioned as if it spanned the window. The Resource Semaphore table one panel down told the same lie more quietly:grants[]rows under a window subtitle.A brief claim found false while doing this: the item says "chart the
window[]block #3637 added" and the lane brief describeswindow[]as a "per-collection series across the window". It is not.window[]is ONE row per pool (per semaphore and pool on the semaphore lens) aggregating every snapshot in the window —snapshots_in_window,peak_waiter_count+peak_waiters_at,peak_granted_memory_mb,min_available_memory_mb,timeout_errors_in_window,forced_grants_in_window, first/last stamps (DarlingMcpMemoryGrantTools.WindowShape; #3637's own body says "per-pool aggregate over EVERY snapshot"). It cannot be a line chart. The honest rendering is what this PR does: each read becomes afanout(one fetch, two panels — the no-double-fetch pin holds) of two TABLES, the window first ("Memory Grant Pressure", subtitled "every snapshot in the window, per resource pool") and the newest snapshot second ("Memory Grants", subtitled "newest snapshot in , per resource pool - a moment, not the window") — the reading order the read's own description gives ("read window[] first for 'was there pressure', grants[] for 'is there pressure now'"). Same for the semaphore pair.GRANT_SERIESis deleted;GRANT_WINDOW_COLUMNS,SEMAPHORE_WINDOW_COLUMNSandGRANT_COLUMNSare the new column sets. A per-collection memory-grant TREND would need a read this service does not serve (there is none inDarlingMemoryGrantReader), and is not something this lane should invent to satisfy a sentence that assumed one existed. The Memory Pressure Events panel moves up one slot so the two span-1 panels (clerks, pressure events) still pair on the grid.4. Four tools said
collection_timewhere the census sayscaptured_atget_database_sizes,get_running_jobs,get_server_properties,get_session_statsstamped their latest snapshot truthfully, but under the pre-#3637 spelling, and were held out of the roster asMcpLatestSnapshotStampTests.StampedUnderCollectionTimebecause the web's SESSION_STATS "Collected" tile readget_session_statsby the old key. The tile moves in the same PR, so all four cut over tocaptured_aton BOTH SKUs (Darling:DarlingMcpObjectStatsTools/DarlingMcpJobTools/DarlingMcpDataTools/DarlingMcpSessionTools; Lite:McpServerInfoTools/McpJobTools/McpSessionTools) with a "LATEST IS A TIME … captured_at is the instant it was collected" sentence in each description, in the Stamped dialect's voice. No alias: the census is the contract, the only reader was the tile edited here, and a second key for one instant is the drift the vocabulary exists to refuse (said in each tool's comment).get_running_jobs' description also stops sayingstart_timematches "the collection_time on this payload".The census: the four join
LatestToolsasShape.Stampedrows (so thecaptured_at/ description / no-knob / same-parameters-on-both-SKUs assertions hold them like every other latest read);StampedUnderCollectionTimeand its sweep arm and control are gone; the old indentation-keyedTopLevelCollectionTimeKeydiscriminator is replaced by one anchored onJsonSerializer.Serialize(new {and now runs as a NEGATIVE sweep over every latest-reading Darling body (a renamed tool cannot keep both spellings; a new one cannot revive the old) — witnessed both ways, including against the memory-grant shape whose per-rowcollection_timesits at the exact indent the old form keyed on; the stamp-column Theory gains the four readers' SQL consts, and its select-list slice is re-anchored on the main statement's column-zeroFROMbecauseRunningJobsSqlopens with a CTE whose ownFROMthe old first-FROMslice stopped at. Lite's roster inLite.Tests/McpLatestSnapshotStampTestsgains the same four rows.Blast radius
get_pg_database_stats: addeddatabases_returned,truncated,order,returned_temp_files,returned_temp_bytes,returned_deadlocks,cache_hit_pct; removedlimit_reached;database_countand the threetotal_*keep their names and change meaning from page to window — which is what the names said.cache_hit_pct_of_returnedunchanged.get_pg_io_stats:combination_count→combinations_returned.collection_time→captured_at. Per-rowcollection_timecolumns elsewhere are untouched.DarlingPgDatabaseReader.PgDatabasePage+GetPgDatabaseStatsPageAsync;GetPgDatabaseStatsAsynckeeps its signature (.Rows).PgDatabaseSql's select list grows 14 → 21 columns (positional reader updated)./api/read/*dispatch unchanged (DarlingWebEndpoints.csuntouched).wwwroot/js: no other reader ofcombination_count,limit_reachedon this tool,database_counton this tool, or the four tools'collection_time. Swept both READMEs anddocs/: none name the changed keys.What it does NOT do
DarlingMcpTools.cs,DarlingMcpServerAdminTools.cs, the deadlock / plan / index PG tool files,McpHelpers.cs,DarlingWebEndpoints.csor anyPgTarget*file.limit_reached(get_pg_table_bloat,get_pg_session_states,get_pg_index_usage) — separate tools, other lanes' files; reported.deprecated/Dashboardchange.Test plan
New / changed, executed here (the whole
Darling.Tests.dllruns in-process on this Mac onceMicrosoft.WindowsDesktop.Appis dropped from its runtimeconfig; Lite's class through a selectivenet10.0-windowsharness):McpPageContractTests— the A7 allowance set is EMPTY (the still-used control passes over an empty set; population floor 60 bodies unchanged); newTheDatabaseStatsRead_CarriesItsWindowTotalsOnTheSameStatement_AboveTheCap(eachwindow_total_*column is the window aggregate of the SAME grouped sum the row reports,window_database_count,window_reset_count, theWindowTotalColumnshape, parameterisedLIMIT,OVER ()below theHAVINGand above theLIMIT) andTheDatabaseStatsTool_PublishesTheWindowAsTotal_AndThePageAsReturned(observed truncation offlimit + 1, noTruncationInferred, nolimit_reached, every key sourced frompage.Window*, no page-sum local reaches atotal_*or a share, the description andlimitsentences).DarlingMcpPgPercentDenominatorTests— newDatabaseStats_TotalsAreOfTheWindow_NotOfThePage(the cut/whole pair: atlimit = 1the totals are the window's 1,000 / 3 / 36.67% whilereturned_*is 600 / 1 / 90%; atlimit = 3they meet) andDatabaseStats_PublishesThePagesWindowFigures_NeverASumOfTheRows(a page handed window figures that disagree with its rows reports what it was handed; the reset flag is true off the window with a never-reset row on the page; a window with no block access reports a null ratio, not 0).combination_count→combinations_returnedin the I/O pair.McpLatestSnapshotStampTests— roster +4; sweep with the negativecollection_timearm; discriminators witnessed against the shippedget_session_statsshape, the fixed shape, and the memory-grant shape; stamp-column Theory +4 with the re-anchored slice;McpLatestSnapshotStampLivePostgresTestsseeds an older and a newer row for each of the four tables and assertscaptured_atis the newer stamp and the retired key is ABSENT.DarlingPgDatabaseStatsLiveTests(DARLING_TEST_PG) — a second, smaller spiller seeded so a one-row page leaves temp files and a deadlock OFF the page: atlimit = 1total_*is 12 files / 93 MB / 3 deadlocks over 5 databases whilereturned_*is 9 / 90 MB / 2,cache_hit_pctis the window's 22,400 / 22,600 against the page's 20,000 / 20,100; atlimit = 2both spillers are on the page andreturned_*meetstotal_*; the unlimited call is not truncated and everyreturned_*equals itstotal_*;limit_reachedabsent,cache_hit_pct_of_returnedpresent.Lite.Tests/McpLatestSnapshotStampTests— roster +4.ConsumedTimestampFrameDisciplineTests— one census reason string names the new key.Executed: 347/347 across the 24 Darling test classes touching these surfaces (including the four
*LivePostgres*/*Live*classes against a throwawaytimescale/timescaledb:2.28.1-pg18on Docker contextdesktop-linux, migrated byPgMigrations); the wholeDarling.Tests.dll(11,003 tests) with only platform reds (WPFPresentationCore/PresentationFramework,Microsoft.Data.SqlClienton macOS, Windows install paths — none in a touched class); Lite'sMcpLatestSnapshotStampTests12/12 through the harness;DarlingPgReadSqlParsesLiveTestsparses the reshapedPgDatabaseSqlagainst live PG. The statement was also run raw throughpsqlwith three planted databases andLIMIT 2(tool limit 1 + sentinel): the page holds two rows, the window columns on every row read 3 databases / 13 files / 94,000,000 bytes / 3 deadlocks / 1 reset — the reset on the database that is NOT on the page.node --checkonserver-tabs.js, and the shipped modules were executed under a throwaway DOM shim with a stubbed fetch: the Memory tab fetchesget_memory_grantsandget_resource_semaphoreexactly once each, renders the window tables fromwindow[](peak 7,777 MB → "7.6 GB", min 111 MB, the peak instant), the latest tables fromgrants[], no line chart; SESSION_STATS renderscaptured_atas a relative time; the PostgreSQL tiles carry the new labels and none of the old ones; the I/O tile readscombinations_returned. Container torn down. Five projects build with 0 warnings (Darling.Service,Darling.Storage,Lite,Darling.Tests;Lite.Testscarries dev's one pre-existing xUnit2000 inLogTailOverlapThresholdPinTests.cs:95, untouched here).Which component(s) does this affect?
Checklist
dotnet build -c Debug)