Repository navigation
v3 plumbing for the PostgreSQL-target engine: the plan, kernel and memory families exist as stubs behind every shared switch, so a PostgreSQL operator sees nothing new yet and the content lanes never touch a shared file (partial #3691) - #3792
Merged
Merged
Conversation
…mory families exist as vocabulary and inert stubs, every shared switch has its arm, and the censuses are re-pinned (partial #3691)
This was referenced Sep 20, 2026
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.
What does this PR do?
Headline mechanism. The v3 plumbing keystone for the PostgreSQL-target analysis engine (#3691) — what #3665 did for v1, #3715 for v2 and #3737 for the wave-3 blocking family, done again for the plan, kernel and memory families: the v3 vocabulary is declared once (
PgTargetSources/PgTargetFactKeys/MetricNames), every new family exists as a stub in its own partial file with a/* filled by lane N */marker, every shared switch grew exactly one delegating arm per family, both anomaly composers are root-declared with theirComposeAnomalycase (the #3737 hook shape, so no content lane edits the anomaly partial), and the censuses that pin the shared surface are re-pinned deliberately. Lanes 27 (plans), 28 (kernel), 32 (memory) fan out from this merge and never touch a shared file again; lanes 29 (buffer drill-down) and 30 (Seq-Scan advisory) are content lanes on existing / plan files and needed no stub beyond the plan family's. PARTIAL for #3691 — v3 plumbing; no checklist line is this PR's. Do not close.No content: no threshold, no advice prose, no graph edge, no SQL (the stubs'
AllSqlcontribution is empty), no rung (StorageVersionstays where dev has it — V136 is lane R5's), noAnomalyThresholdsconstant. No operator-visible change: theanalyze_servergated e2e returns the same payload (executed, below).Every declaration, stub and switch arm — with the lane that fills it
Sources (
PgTargetSources, each ending inSourcefor the registry sweep):PlansSource = "pg_plans"(27),KernelSource = "pg_kernel"(28),MemorySource = "pg_memory"(32).All15 → 18 (ordinal order kept);FactScorer.KnownSources30 → 33; both SKUs'get_analysis_factssource description spells the new list (the one Lite edit — the census-pin boundary extension every plumbing PR takes).Keys (
PgTargetFactKeys, doc-commented with design § and table):pg_plan_capture×pg_statement_stats,pg_column_stats,pg_predicate_stats):PlanRegression = "PG_PLAN_REGRESSION",ParameterSensitivity = "PG_PARAMETER_SENSITIVITY",SeqScanAdvisory = "PG_SEQ_SCAN_ADVISORY"(lane 30 — EVIDENCE only, never DDL, D8; the doc says the maintainer's decision gates the lane),AnomalyPlanRegression = "ANOMALY_PG_PLAN_REGRESSION".pg_kernel_stats,pg_stat_kcache; availability frompg_extension_availability):CpuBurnCores = "PG_CPU_BURN_CORES"(the self-hosted CPU proxy),CpuDecomposition = "PG_CPU_DECOMPOSITION",AnomalyCpuBurn = "ANOMALY_PG_CPU_BURN".pg_server_config× the V136 host-memory columns onpg_cpu_utilization's row):ConfigMemoryOvercommit = "CONFIG_PG_MEMORY_OVERCOMMIT",HostMemoryPressure = "PG_HOST_MEMORY_PRESSURE".max_wal_size-vs-disk composition key — disk free is not collected, and a key nothing can measure is a lie waiting for a lane. Lane 29 declares nothing: it is a drill-down onPG_BUFFER_CACHE_PRESSUREoverpg_buffer_usage, lane 16's deadlock shape.AnomalyToFamiliesgainsAnomalyPlanRegression → PlanRegression,AnomalyCpuBurn → CpuBurnCores.PgTargetScorer.IsDeviationScoredAnomalyKeyregisters both by shape (z-score on one series).ConfigAdvisoryRootsgainsConfigMemoryOvercommit— a CONVENTION reading of five knobs against the host roots at the 0.4 advisory base and reaches ≥ 0.5 only when a workload co-fire (PG_HOST_MEMORY_PRESSUREorPG_TEMP_SPILL) lifts it (D5); the content lane decides which and pins it, this PR only routes.HostMemoryPressureis measured, not config, and is NOT a root (pinned).Baseline metric names (
MetricNames):PgStatementMeanMs = "pg_statement_mean_ms"(27),PgCpuBurnCores = "pg_cpu_burn_cores"(28). The plan name's doc records the decision lane 27 owns FIRST:PgBaselineProvider.GetBaselineAsynckeys one series on (server, metric) with no per-statement dimension, so the arm is either the server-wide mean (blunt, honest) or the seam grows a key dimension — a shared-file item to report, never made inside the lane. The stub takes no side.Stub partials, each with the exact marker the content briefs quote:
PerformanceMonitor.Analysis/PgTargetScorer.Plans.cs/* filled by lane 27 */ScorePlanFact→ 0.0,PlanAmplifiers→[]…/PgTargetScorer.Kernel.cs/* filled by lane 28 */ScoreKernelFact→ 0.0,KernelAmplifiers→[]…/PgTargetScorer.Memory.cs/* filled by lane 32 */ScoreMemoryFact→ 0.0,MemoryAmplifiers→[]…/PgTargetAdvice.Plans.cs/* filled by lane 27 (…) and lane 30 (…) */+/* filled by lane 27 — the ANOMALY_PG_PLAN_REGRESSION arm …ComposePlan→ null,ComposePlanRegressionAnomaly→ null…/PgTargetAdvice.Kernel.cs/* filled by lane 28 */+/* filled by lane 28 — the ANOMALY_PG_CPU_BURN arm …ComposeKernel→ null,ComposeCpuBurnAnomaly→ null…/PgTargetAdvice.Memory.cs/* filled by lane 32 */ComposeMemory→ null…/PgTargetRelationshipGraph.Plans.cs/.Kernel.cs/.HostMemory.cs/* filled by lane 27 *//28/32BuildPlanEdges/BuildKernelEdges/BuildHostMemoryEdges— emptyDarling/…/PgTargetFactCollector.Plans.cs/.Kernel.cs/.Memory.cs/* filled by lane 27 (lane 30 adds …)//* filled by lane 28//* filled by lane 32CollectPlanFactsAsync/CollectKernelFactsAsync/CollectMemoryFactsAsync→Task.CompletedTaskDarling/…/PgTargetAnomalyDetector.Plans.cs/.Kernel.cs/* filled by lane 27/28DetectPlanRegressionAnomalies/DetectCpuBurnAnomalies→Task.CompletedTaskDarling/…/PgTargetBaselineProvider.Plans.cs/.Kernel.cs/* filled by lane 27/28StatementMeanMsBaselineQuery/CpuBurnCoresBaselineQuery→ nullThe memory family's graph partial is
HostMemory.csbecausePgTargetRelationshipGraph.Memory.csis lane 2's v1 buffer chain (BuildMemoryEdges); one source node's edges live in one place, and the root comment says so. The collector method isCollectPlanFactsAsyncinPlans.cs(theCollectQueryFactsAsync/Queries.csshape) — the method→file maps inPgTargetFactCollectorTestsandCollectionCaveatsTestseach gained the line. Every baseline stub's doc states the Q6 contract the filled arm must follow (clean(collection_time, v)+ the ONERobustTierScaffold, bound through the root's$4..$6after$3, never its ownEXTRACT); every detector stub's doc names the #3653 pair gate; every collector stub's doc names the timeout / token /$N/ clock / degrade / observed-time rules.Shared-switch arms (one per family, nothing else moved):
PgTargetScorer.ScoreBase:PlansSource → ScorePlanFact,KernelSource → ScoreKernelFact,MemorySource → ScoreMemoryFact.Amplifiers: the three plan keys →PlanAmplifiers; the two kernel keys →KernelAmplifiers;ConfigMemoryOvercommit or HostMemoryPressure → MemoryAmplifiers, named AHEAD of theCONFIG_PG_prefix arm because the check ispg_memory-sourced (theConfigSharedBuffers-in-BufferAmplifiersprecedent; pinned by ordinal position in source).PgTargetAdvice.Compose: the same three groups →ComposePlan/ComposeKernel/ComposeMemory, the memory arm again ahead of the prefix arm (which would compose null for it forever — theConfigAutovacuumDisabledprecedent).ComposeAnomaly:case AnomalyPlanRegression → ComposePlanRegressionAnomaly,case AnomalyCpuBurn → ComposeCpuBurnAnomaly, both partials declared in the root file.PgTargetRelationshipGraphctor:BuildPlanEdges(); BuildKernelEdges(); BuildHostMemoryEdges();after the blocking chain.PgTargetFactCollector.CollectFactsAsync: the three stubs LAST, afterCollectBlockingFactsAsync— the plan facts name lane 7's bad actors, the kernel decomposition reads the wait facts, the memory arithmetic reads the config knob facts by key (pinned by position).PgTargetAnomalyDetector.DetectAnomaliesAsync: the two stubs after the sampled wait profile, behind the same gate.PgTargetBaselineProvider.GetPgTargetBaselineQuery: two arms to the stubs (null = the default's answer).PgTargetToolRecommendations.ByFactKey: a row for every new key — every tool verified registered byrgover the Mcp folder:get_pg_plans(there is noget_pg_plan_captures),get_pg_top_queries,get_pg_query_duration_trend,get_pg_column_stats,get_pg_predicate_stats,get_pg_kernel_stats,get_pg_extensions,get_pg_wait_stats,get_pg_server_config,get_pg_buffer_usage,get_pg_session_states,get_pg_cpu_utilization. The host-memory columns ridepg_cpu_utilization's row (R5's V136 shape), so the pressure fact's first read is the capacity read; no memory tool exists or is needed.Censuses re-pinned (each edited deliberately, none deleted)
PgTargetFactCollectorTests: collect surface 16 → 19 names, stub files 14 → 17, the three markers pinned by exact string,CollectPlanFactsAsync → Plans.csin the method→file map. FROM/JOIN whitelist:pg_plan_capture,pg_statement_stats,pg_column_stats,pg_predicate_stats,pg_kernel_stats,pg_buffer_usage,pg_cpu_utilizationareCollectorCatalogtargets already — admitted with no edit, each now pinned BY NAME with its lane.pg_database_sizeis NOT pinned (it arrives with R5's catalog row; the first lane to read it adds the pin).PgTargetSharedSwitchRoutingTests: sources 15 → 18;AnomalyToFamiliestwo entries;ConfigAdvisoryRootscontains the overcommit key and not the pressure key; the advice delegation census gains nineNulllines (null IS the delegation — each lane moves its own lines toNotNull); the routing censusEveryDeclaredPgKey_IsRoutedByEveryRootDispatcher…passes with zero exclusions added (every new key is routed by name; the floors≥ 25 / 12 / 9hold).PgTargetAnomalyTests: anomaly keys 10 → 12 (both deviation); the inert-null loop gains the two metric names; the provider-arm text pins gain two lines; the detector-order check gains two calls; the stub-marker loop gains four files; the fence test names the two v3 stubs as exemptions by exact stub text (the The PostgreSQL engine absorbs what the v2 lanes reported back: the I/O and WAL baseline tables are floored, the I/O baseline restores hour-of-week at a quarter-hour grain, the WAL co-fire reads the anomaly and not a base-0 fact, a Lock wait reaches its idle holder, and the blocking family exists as stubs for lane 17 (partial #3691, between waves) #3737 shape).FactSourceRegistryTests: 30 → 33.CollectionCaveatsTests/PgTargetCollectionCaveatsLiveTests: the family-count denominator 16 → 19 (a stub is a family READ that returns nothing; the count is reads, not facts — it renders only inside a collection caveat, so a clean pass's payload is byte-identical), plus the["plan"] = "plans"label map; the root's"1 of 16"example →"1 of 19".Darling/Darling.Tests/PgTargetV3PlumbingTests.cs(3 tests): the vocabulary + folds + advisory root + no disk key; inert through every shared entry point (base 0, no amplifier, null advice static and viaFactAdvice, no edge, null baseline in both providers, a tool row) with the memory arm's position ahead of the prefix arms pinned as source; the sixteen stub files' markers, noaurora_stat_, noCREATE INDEXin any string literal,Memory.csuntouched, both anomaly hooks root-declared, the collector emission order.PgTargetThresholdLineageTests: zero bars added.AuroraOnlySqlIsGatedTests: no new file mentionsaurora_stat_(grepped + pinned).DocCommentHygiene,RepoFileAdoptionTests(single-line anchors,ReadRepoFilenotLf),CommentFilterAdoptionTests,StoreSqlClockDisciplineTests,BaselineSupplyTests(null arms contribute nothing),LocalClockBucketKeyTests: green, no edit.What it does NOT do
No PostgreSQL SQL, no threshold, no advice text, no graph edge, no baseline CTE, no
AnomalyThresholdsconstant, no rung, noDailySummaryHorizon.BaselineServingRawCollectorsfloor (the stubs' arms are null, soBaselineSupplyTestsderives nothing to floor —pg_statement_stats/pg_kernel_statsjoin the set the day lanes 27 / 28 make their arm non-null, and each stub's doc says so). NoMaintenanceShapeShiftstub. A SQL Server target's pass is byte-identical by construction: every new arm is behind apg_source or aPG_/CONFIG_PG_/ANOMALY_PG_key no SQL Server fact carries, and the routing pins that proved v1's byte-identity pass unchanged.Test plan
Builds, zero warnings:
PerformanceMonitor.Analysis,PerformanceMonitor.Darling.Service(pulls Darling.Analysis + Storage),Darling.Tests,Lite.Tests,deprecated/Dashboard.Tests(-p:EnableWindowsTargeting=trueon the test projects).EXECUTED here through a net10.0 harness (
AssemblyName=Darling.Tests, the PgTarget census set — everyPgTarget*.csplusFactSourceRegistryTests,CollectionCaveats*Tests,BaselineSupplyTests,LocalClockBucketKeyTests,BaselineTierJoinShapeTests,StoreSqlClockDisciplineTests,DocCommentHygieneTests,RepoFileAdoptionTests,CommentFilterAdoptionTests) against a throwawaytimescale/timescaledb:2.28.1-pg18on Docker contextdesktop-linux, migrated by the fixture, torn down after: 744 / 744 green, 0 skipped, on the rebased tree (origin/dev@ 69a3a78, after lane 26's #3791). That includes everyDARLING_TEST_PG-gated e2e in the set — the exit criterion:PgTargetFactCollectorTests.APostgresTargetWithADayOfHistory…(analyze_server→ the same payload; the stubs emit nothing) andPgTargetCollectionCaveatsLiveTests(a clean pass carries no caveat; a blind vacuum family says1 of 19).DocCommentHygieneTestsran green with the harness underobj/(gitignored, deleted before commit). First execution of the Lite literal's pins is CI's Windows job.Which component(s) does this affect?
get_analysis_factssource description, a census pin)How was this tested?
Above. No production store or fleet server was read; the rig was a local container, torn down.
Checklist
dotnet build -c Debug)Partial for #3691 (v3 plumbing; lanes 27–32 follow). CHANGELOG deliberately untouched — the coordinator splices this wave's entries.