Skip to content

get_ag_health caps its fleet-wide answer, most severe first, and says when it cut (#4471) - #4474

Merged
erikdarlingdata merged 3 commits into
devfrom
fix/4471-ag-health-limit
Sep 27, 2026
Merged

erikdarlingdata merged 3 commits into
devfrom
fix/4471-ag-health-limit

Conversation

@erikdarlingdata

@erikdarlingdata erikdarlingdata commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

Fixes #4471.

Why

get_ag_health's only parameter was server_name. A fleet-wide call (the tool's own description invites omitting it) returned every (reporting server, AG) group with no cap: a production fleet measured 265,794 characters, over an MCP client's typical per-result token limit, and got spilled to a file or refused outright. get_analysis_findings already carries the pattern this tool lacked — a limit with a stated default, most-severe-first ordering, and a truncation flag.

What changes

  • get_ag_health (and the shared DarlingAgReader.GetAgHealthAsync/Build) gained a limit parameter, default 11, on the unit a caller reasons about: groups (one monitored server's view of one AG).
  • Ordering is most-severe-first (unchanged), with a new tie-break for groups that share a severity: the largest secondary_lag_seconds or queue depth (KB) anywhere in the group. Lag and queue depth stay un-banded, by design (see the reader's own doc comment) — this tie-break only decides which same-severity groups a cut drops first, it never changes a group's severity.
  • The envelope carries three new fields: groups_returned, groups_total, groups_truncated (+ groups_truncated_note when true), naming the same trio get_analysis_findings uses for its own findings_truncated. availability_group_count/distinct_ag_count/worst_severity still describe the WHOLE scope, not just the returned page.
  • The /api/read/get_ag_health dispatch row and the API catalog gained the limit parameter to match; /api/ag (the dashboard's own read) is unchanged and stays uncapped — it renders its own page, not an MCP client's result buffer.
  • Lite has no AG-health read (get_ag_health is Darling-only, per its own doc comment), so no Lite change was needed.

Test plan

Live Postgres pins in DarlingMcpAgToolsTests.cs (container rig, role darling on a Timescale image):

  • AgHealth_DefaultLimitCapsMostSevereFirstAndFlagsTruncation_AgainstDevPostgres (new): seeds 42 HEALTHY single-replica groups plus one CRITICAL group (a suspended database) planted LAST by both insertion order and name, then asserts the default call (no limit argument) returns exactly 11 groups, groups_returned/groups_total/groups_truncated/groups_truncated_note are correct, and the critical group is first. Also asserts limit >= total clears the flag.
  • All 6 pre-existing DarlingMcpAgToolsLivePostgresTests/DarlingMcpAgToolsSurfaceTests pins pass unchanged (the surface pin's expected parameter list was updated to server_name,limit).
  • McpToolsListBudgetTests (5/5), McpToolGuideHeadsAgStoreTests (5/5), DocCommentHygieneTests (77/77), DarlingWebEndpointsTests (70/70), AvailabilityGroupCountReadTests, DarlingAgStatesReaderLiveEqualityTests, DarlingAgStatesReaderTests, PgTargetMcpSurfaceTests, DarlingCoreToolProfileTests — all green.
  • RUNTIME RED on origin/dev (6f50c73b0): a throwaway test file, compiled clean on dev (it calls GetAgHealth with no limit argument, since dev's signature has none), seeded 15 groups on one server and called the tool with defaults — failed at runtime asserting <= 11 groups returned and groups_truncated == true, because dev returns all 15 uncapped with no such field. Removed after confirming.
  • Mutations, both reverted: (1) commented out groups.Sort(CompareGroups) — the new severity-first pin went RED. (2) removed the limit cut (pagedGroups/groupsTruncated stayed unconditional) — the same pin went RED (no truncation, 43 groups back). Both reverted; the pin is GREEN again on the current branch (7/7 in the class).

Sizes (43-server / 3-AG-per-server / 2-replica / 3-database fixture, ~2.7 KB/group on this shape, via a throwaway harness against the built DLLs):

  • Uncapped (limit set past the total, i.e. the old, unbounded behavior on this fixture): 348,187 characters.
  • The fixed tool's own default call (no arguments, limit=11): 30,115 bytes — under the shared 32 KB budget. limit=12 measured 32,812 bytes, over it, which is why 11 is the largest default that clears the budget on this shape.

CHANGELOG entry

SECTION: Changed
ENTRY:

Adds a limit parameter to get_ag_health (default 11, matching
get_analysis_findings' pattern), orders groups worst severity first
then by the largest lag/queue magnitude, and adds groups_returned /
groups_total / groups_truncated (+ note) to the envelope.
@erikdarlingdata
erikdarlingdata marked this pull request as ready for review September 27, 2026 17:15
@erikdarlingdata
erikdarlingdata merged commit 09a0ac7 into dev Sep 27, 2026
15 of 16 checks passed
@erikdarlingdata
erikdarlingdata deleted the fix/4471-ag-health-limit branch September 27, 2026 17:15
erikdarlingdata added a commit that referenced this pull request Sep 28, 2026
…, not a fixed group count (#4568)

get_ag_health fills its fleet-wide answer to the 32 KB budget by size, not a fixed group count. Refs #4471, #4474.

- DarlingAgReader.Build walks the availability groups most-severe-first, serializing each group once and adding its bytes to the envelope's, and stops before the group that would cross McpResponseBudget.DefaultBytes. At least one group always comes back.
- The response actually returned is then re-measured, note included, and the last group dropped (the note rebuilt) while it is over the budget.
- The default of 11 groups stays as an upper bound, an explicit limit is still honored, and groups_returned, groups_total, groups_truncated and the note stay truthful; the note says whether the size budget or the limit cut the page.
- The tool and parameter descriptions say so, within the tools-list budget.
- Tests (DarlingAgReaderTests): 42 groups of 10 databases each come back within 32 KB and truncated (280,142 bytes on the code before the change); 5 small groups all come back; one oversized group returns alone, not truncated; an explicit small limit is exact; and groups that fit only without the note are cut to fit with it. The live test checks that the serialized response fits the budget.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant