Tile PG-target TPS, sessions, CPU and Aurora waits detectors (#3653 A8 option B, lane L3a) - #4171
Conversation
c7d1d81 to
f68a59f
Compare
Behaviour tests (lane T4171-2, tests-only)Landed T4171's Result: 6 of 7 pass live. 1 (TPS scenario 1) is still red — pushed as-is per the brief's "push scenario 1, iterate" rule, but I ran out of budget before root-causing it. Flagging for a fast follow-up, not closing the loop myself. What I found and fixed
Still red: TPS scenario 1After fixing the canary gate and re-sizing the shift (76 tps vs 60±3 baseline, chosen so the computed window mean sits below the 3.5σ cutoff against the measured bucket), the detector still returns an EMPTY fact list — Live verification
CHANGELOG entry(none — tests-only lane; no user-facing change) HandoffNext lane should: (1) root-cause the TPS scenario-1 failure (instrument |
…on B, lane L3a) Moves DetectTpsAnomalies, DetectSessionAnomalies and DetectCpuAnomalies onto per-hour tiles through AnomalyGate.EvaluateTiles: each hour of the analysis window is scored against its own hour-of-week baseline, and when no tile scores the family falls back to today's whole-window EvaluateZScore path. New SQL consts (DatabaseCounterWindowSql left byte-identical for the deadlock-rate ratio path): - TpsTileWindowSql - SessionTileWindowSql (keeps the per_collection CTE, groups the outer select) - CpuTileWindowSql (per-tile array_agg peak time instead of the ORDER BY/LIMIT 1 subquery; carries the raw cpu_percent peak per tile) Aurora waits (WaitRateWindowSql) is NOT yet tiled in this commit -- see the PR body.
…B, lane L3a) Adds WaitRateTileWindowSql and moves DetectWaitProfileAnomalies onto per-hour tiles. This family's own three-armed trust rule (robust pair / classical ratio pair / absolute fallback) does not fit AnomalyGate's z-only EvaluateTiles, so each tile is decided by hand through the same three arms the whole-window body already had (DecideWaitProfileTile), and the worst FIRING tile is reported. Never-blind fallback: when no tile scores, today's whole-window logic runs unchanged.
Only the trusted robust arm (baseline.IsTrustworthy && EffectiveRobustSigma > 0) moves onto tiles now, chosen from the START-hour bucket exactly as dev's whole-window body decides it. AnomalyGate.EvaluateTiles scores that arm; when it returns null the never-blind fallback runs today's EvaluateZScore over WindowTiles.WholeWindow(tiles). The classical-ratio arm and the no-baseline absolute arm stay whole-window and unchanged, fed from WholeWindow(tiles)'s Peak/Mean, with no second SQL read. Deletes the private DecideWaitProfileTile/WaitProfileTileDecision L3a wrote to run all three arms per tile by hand (no multiplicity correction on the ratio arm, uncorrected k on the z arm's per-tile mean, and a single untrustworthy tile could switch the WHOLE window to the absolute arm even when the start bucket trusted the z arm).
…in regression s_detectorSql/EveryDetectorRead_* census gets TpsTileWindowSql, SessionTileWindowSql, CpuTileWindowSql, WaitRateTileWindowSql, plus table/local_hour/dialect pins for each. Also fixes a pin L3a's TPS/sessions/CPU tiled call-flow refactor already broke: the never-blind fallback's EvaluateZScore call now passes a local variable instead of the inline expression the old regex matched verbatim (ThePairGate_TpsSessionsAndCpu... test class's source pin). Updated the regex to match and added a pin that the local is assigned from the same expression. Same fix applied to the wait-profile detector's own EvaluateZScore pin after step 1's rewrite. LocalClockBucketKeyTests' census only covers PgBaselineProvider/PgTargetBaselineProvider baseline queries, not detector window-SQL consts, so the four new TileWindowSql consts are not in scope for that census (confirmed against lane L3b-2's own step-1 commit, which touched the same census file for its own tiled families and made no change there either).
…binds only $1..$3; the detectors' catch turned 'no value for $4' into silence); waits fetches its map before the read; the Aurora waits live test pins the worst tile
… TpsTileWindowSql insertion stacked two summaries)
… assertions in tile behaviour tests
… mean below cutoff (TPS test still red)
The TPS scenario 1 fixture's minute-resolution rows (i in 0..11) only spanned minutes 0-11 of each hour, leaving a ~49-minute gap to the next hour's first sample. The per-collection LAG-rated tps for that boundary-crossing sample divides one minute-increment's worth of xacts by the whole ~49-minute gap, producing a spurious near-zero tps sample inside every tile. That dragged each tile's AVG well below the correctMean-raised cutoff even though its PEAK cleared easily, so EvaluateTiles never fired any tile, EvaluateTiles returned a non-firing verdict (never null - it still returns a display verdict even when no tile fires), and the fallback branch was never reached either: the tile path's own Decision.Fire was false, so DetectTpsAnomalies returned with no fact at all. Fixed by spacing the TPS fixture's rows every 5 minutes across the full hour (i in 0..11, 5 min apart, matching the sessions/CPU fixtures' own convention) instead of every 1 minute, and scaling the per-step increment from *60 to *300 to keep the seeded mu/sigma tps unchanged under the new 300-second step. Diagnosed with a temporary [Fact] that ran TpsTileWindowSql's own 6 binds directly, printed the per_collection rated rows, the bucket map per window hour, and the fired fact count - never committed. Verified live (timescale/timescaledb:2.28.1-pg18): all 7 PgTargetTileBehaviourTests pass; the *PgTargetAnomaly* + *PgTargetTileBehaviour* live classes are 51/51; the mutation check (AnomalyGate.EvaluateTiles forced to return null) failed exactly the 5 tile-shift tests and left the 2 non-tile tests (lone spike, sparse fallback) green, then reverted cleanly (no diff on AnomalyGate.cs). Full suite with DARLING_TEST_PG unset: 214 failures, matching dev's known Windows-only baseline exactly - no new failures.
6286331 to
798c287
Compare
CommandCapture.IsBaselineRead matched any SQL containing
BaselineLocalClock.LocalCollectionTimeSql, on the doc comment's promise
that "nothing else the pass runs does". B's tiled window reads (this PR:
TPS, sessions, CPU, Aurora waits) key their tiles through
WindowTiles.LocalHourSql, which wraps that same LocalCollectionTimeSql
spelling in date_trunc('hour', ...) — so every tiled window read on every
pass, cached baseline or not, was miscounted as a baseline compute. That's
exactly the 4 the second-pass cache-hit test found where it expected 0.
Excludes any SQL containing WindowTiles.LocalHourSql from the classifier
(loop seat's diagnosis and fix). No baseline arm on dev uses
date_trunc('hour', <LocalCollectionTimeSql>); only WindowTiles.LocalHourSql
does, so this can't misclassify a real baseline compute.
Same fix as feat/3653-b-l3a's 584ace6, applied here since #4171 has not merged yet: CommandCapture.IsBaselineRead matched any SQL containing BaselineLocalClock.LocalCollectionTimeSql. B's tiled window reads (this PR: CPU, waits, I/O read and write) key their tiles through WindowTiles.LocalHourSql, which wraps that same spelling in date_trunc('hour', ...), so every tiled window read was miscounted as a baseline compute regardless of cache state. Excludes any SQL containing WindowTiles.LocalHourSql from the classifier. No baseline arm on dev uses date_trunc('hour', <LocalCollectionTimeSql>); only WindowTiles.LocalHourSql does.
…ectors move onto per-hour tiles (#4172) * issue-3653 A8 option B (lane L2a): move SQL Server CPU and wait detectors onto per-hour tiles Part of #3653. CPU: DetectCpuAnomalies reads CpuTileWindowSql (one row per target-local hour), scores every tile through AnomalyGate.EvaluateTiles, and falls back to today's whole-window EvaluateZScore when no tile scores (never-blind rule). Waits: per coordinator ruling, only the robust-z start-bucket arm moves onto EvaluateTiles; the classical-ratio and no-baseline arms stay whole-window, fed from WindowTiles.WholeWindow(tiles), with no second SQL read. total_wait_ms is summed from the tiled rows since it isn't a WindowTile field. Added CpuTileWindowSql, WaitRateTileWindowSql, and shared BindTiledWindow / ReadTilesAsync helpers for reuse by lane L2b (batch, sessions, query duration, memory) in the same file. I/O family and test-pin updates are NOT yet in this commit; see the PR body. * issue-3653 A8 option B (lane L2a): move SQL Server I/O detector onto per-hour tiles Part of #3653. ONE tiled read (IoTileWindowSql) feeds both the read-latency and write-latency gates, each through its own WindowTile list and its own EvaluateTiles call (each family's fallback re-fetches its own bucket map lookup, since the tile key is shared but the gate call and floor are per-direction). Never-blind fallback to today's whole-window EvaluateZScore per direction when no tile scores. * 3653 B L2a-2 step 1: fix pins falsified by the CPU/wait/I-O tile move, delete unused old consts - CpuWindowSql, WaitRateWindowSql, IoWindowSql were dead once L2a's tile move landed (no remaining production reader); deleted, with cref updates on the two class-doc comments that named them. - DarlingAnomalyBaselineTests: census list, the I/O peak/mean pair pin, and the wait-rate pair pin now point at the Tile consts. The wait-rate pin's stale reader-ordinal regexes (which assumed the old single-collapsed-row read) are replaced with a direct pin on the new whole.Peak/whole.Mean/ totalReader.IsDBNull(3) shape; Lite's own single-row ordinals are pinned unchanged. The EvaluateZScore baseline-argument capture widened to catch readBaseline/writeBaseline (the tiled I/O arms' own locals), and the never-blind fallback's window: argument now accepts either window (PG) or the inline TimeRangeEnd - TimeRangeStart (Lite, unmoved). - LatestCpuReadShapeTests' two crefs already point at the still-live CpuTileWindowSql text (unchanged, no fix needed there). Verified: DarlingAnomalyBaselineTests, LatestCpuReadShapeTests, LocalClockBucketKeyTests, MeasurementContractCensusTests, CollectorMeasurementSeamTests, DocCommentHygieneTests all green, no PG. * 3653 B L2a: the I/O and wait-profile live tests pin the WORST TILE (its mean over the hour holding the hot row, located by tile_start_ticks), not the whole-window mean * WIP: draft behaviour tests for PR #4172 tiled SQL Server detectors (5/6 red, needs fixing) * T4172-2: size CPU tile fixtures from the family's own magnitude floor CpuFloorPct is 50.0%. The prior fixture (mu=20, sigmaAmp=2) put mu+6sigma around 29 -- never clears the floor, so nothing fired in any of the 3 CPU shift/24h/fallback scenarios. Raised to mu=30, sigmaAmp=5 (sample stddev ~3.69, mu+6sigma ~52.1). Also corrected two assertions that expected the classical DefaultDeviationThreshold (2.0): this CPU baseline carries a positive robust sigma, so the tiled gate runs the modified-z frame (ModifiedZThreshold, 3.5) on both the peak and the tile-corrected mean, not the classical one. CPU: 3 of 4 scenarios now pass (shift, lone spike, fallback). The 24h as_of scenario and both wait/IO scenario-1 tests are still red; next commit. * T4172-2: fix 24h scenario's baseline seeding day-of-week gap; document a RED product bug in waits 24h scenario: the window spans two calendar days (Tue 12:00 - Wed 12:00). SeedCpuBaselineAsync anchors every hour it's given to ONE calendar date, so seeding all 24 hours off windowStart's date alone left every Wednesday-side tile bucket empty. Now seeds each hour off the day it actually falls on in the window. Passes. Waits scenario 1: RED, documented rather than bent. DetectWaitAnomalies never writes fire_threshold to the fact's metadata on any arm, unlike every sibling tiled family (CPU, I/O) which stamp it from decision.ThresholdUsed. The fact still fires correctly (right worst tile, tiles_scored/tiles_fired) -- asserted -- but the missing key is a product gap, described in the PR body's Behaviour tests section per the lane rule (a red test after true preconditions is a product bug, not something to bend the assertion around). I/O scenario 1 is still red; not yet diagnosed (context budget). Left for follow-up, described in the PR body. * T4172-3 step 1: DetectWaitAnomalies now stamps fire_threshold (coordinator ruling) The SQL Server-store waits detector never wrote a fire_threshold key on ANY arm, while every sibling tiled family (CPU, I/O) does. Added: - robust-z arm: decision.ThresholdUsed (the tiled/whole-window cutoff actually applied) - ratio arm: DefaultRatioThreshold - no-baseline arm: 0 Flipped Waits_TwoHourSustainedShift_FiresOnWorstTile_... from asserting the key's absence to asserting its value (AnomalyThresholds.HeavyTailModifiedZThreshold on this scenario, since the robust-z tiled arm fires). Live run (DARLING_TEST_PG, timescale/timescaledb:2.28.1-pg18): SqlServerStoreTileBehaviourTests: Total 6, Failed 1 (Io_TwoHourSustainedShift_..., still being diagnosed per step 2), Skipped 0. * WIP T4172-4: read the I/O start bucket at T (not T-24h) to match the tile map's lookup key Diagnosed: GetBaselineAsync(serverId, IoLatency, T.AddHours(-24), ...) missed the seeded Wednesday 10/11 Full bucket (T-24h is a Tuesday) and fell through to a pooled HourOnly bucket, an under-sized/untrustworthy sigma that mis-sized the shift. Reading at T (the tile map's own lookup key) now reports the seeded Full bucket and Assert.True(IsTrustworthy) passes, but the detector still emits no ANOMALY_READ_LATENCY fact. Not yet green -- see PR body handoff for the next diagnostic step (dump IoTileWindowSql's tile rows + EvaluateTiles verdict, per lane brief step 1's escape hatch). * T4172-5 step 1: drop the I/O tiled-shift fixture (coordinator ruling, scope trim) * 3653 B L2a: exclude tiled window reads from the baseline-read classifier Same fix as feat/3653-b-l3a's 584ace6, applied here since #4171 has not merged yet: CommandCapture.IsBaselineRead matched any SQL containing BaselineLocalClock.LocalCollectionTimeSql. B's tiled window reads (this PR: CPU, waits, I/O read and write) key their tiles through WindowTiles.LocalHourSql, which wraps that same spelling in date_trunc('hour', ...), so every tiled window read was miscounted as a baseline compute regardless of cache state. Excludes any SQL containing WindowTiles.LocalHourSql from the classifier. No baseline arm on dev uses date_trunc('hour', <LocalCollectionTimeSql>); only WindowTiles.LocalHourSql does.
…gap (lane G1-3) - RunBothWindowsAsync now returns Task<bool> (CommandCapture.CaptureAsync needs Func<Task<T>>, not Func<Task>) — the merge with dev (#4171) broke the build. - AssertEveryWindowSqlRan excludes PgTargetAnomalyDetector's SessionWindowSql, CpuWindowSql, WaitRateWindowSql by name: #4171's tiled-window recipe gave those arms tile twins the detector actually reads now; the plain consts stay only for PgTargetAnomalyTests' pinning. PgAnomalyDetector's SQL Server consts of the same names are NOT excluded (still read directly there). - SeedSqlServerFamilyTablesAsync: added a spiked current-window wait_stats row so the wait-profile detector's fallback bar actually fires and WaitContribWindowSql runs (a flat series across every seeded row never exceeded the bar).
…gap (lane G1-3) - RunBothWindowsAsync now returns Task<bool> (CommandCapture.CaptureAsync needs Func<Task<T>>, not Func<Task>) — the merge with dev (#4171) broke the build. - AssertEveryWindowSqlRan excludes PgTargetAnomalyDetector's SessionWindowSql, CpuWindowSql, WaitRateWindowSql by name: #4171's tiled-window recipe gave those arms tile twins the detector actually reads now; the plain consts stay only for PgTargetAnomalyTests' pinning. PgAnomalyDetector's SQL Server consts of the same names are NOT excluded (still read directly there). - SeedSqlServerFamilyTablesAsync: added a spiked current-window wait_stats row so the wait-profile detector's fallback bar actually fires and WaitContribWindowSql runs (a flat series across every seeded row never exceeded the bar).
… error (silent-family guard) (#4179) * Add a live guard: no anomaly detector logs an error over a seeded window (#3653 A8 option B) Both PgAnomalyDetector (SQL Server store) and PgTargetAnomalyDetector (PG target) swallow their own per-family exceptions into a logged Error line, so a SQL error or a missing parameter bind silently kills a family with no fact and no failed test. This adds Darling/Darling.Tests/AnomalyDetectorErrorGuardLiveTests.cs: a capturing ILogger runs every family over minimally seeded tables (one row per table each *WindowSql*/*TileWindowSql* const reads, across a 4h and a 24h window) and fails if any Error-or-above line was captured. Part of #3653 * issue-3653 A8 option B: make the silent-detector guard non-vacuous (lane G1-2) Seed 12 same-hour/day-of-week rows per family table (12 weeks back) instead of 1, so every detector's baseline.SampleCount clears BaselineMath.CollapseThreshold and each family reaches its *WindowSql read instead of returning early on SampleCount == 0. Capture the SQL Npgsql actually executed via CommandCapture.CaptureAsync and assert every public const string *WindowSql field on PgAnomalyDetector / PgTargetAnomalyDetector (reflected, so it covers every partial-class piece automatically) appears verbatim among the captured commands, naming any that never ran. * issue-3653 A8 option B: fix guard build break + wait-profile fixture gap (lane G1-3) - RunBothWindowsAsync now returns Task<bool> (CommandCapture.CaptureAsync needs Func<Task<T>>, not Func<Task>) — the merge with dev (#4171) broke the build. - AssertEveryWindowSqlRan excludes PgTargetAnomalyDetector's SessionWindowSql, CpuWindowSql, WaitRateWindowSql by name: #4171's tiled-window recipe gave those arms tile twins the detector actually reads now; the plain consts stay only for PgTargetAnomalyTests' pinning. PgAnomalyDetector's SQL Server consts of the same names are NOT excluded (still read directly there). - SeedSqlServerFamilyTablesAsync: added a spiked current-window wait_stats row so the wait-profile detector's fallback bar actually fires and WaitContribWindowSql runs (a flat series across every seeded row never exceeded the bar). * issue-3653 B (G1-4): seed PgTarget fixtures for the detector error guard's 4 window consts WIP: DatabaseCounterWindowSql, WaitContribWindowSql and SampledWaitContribWindowSql now run; IoLatencyWindowSql still needs a fix (baseline gate: PgIoReadLatency SampleCount==0). * issue-3653 B (G1-4): attempt IoLatencyWindowSql baseline predecessor row (still gapped) DatabaseCounterWindowSql, WaitContribWindowSql and SampledWaitContribWindowSql now run live. IoLatencyWindowSql still 'never ran' -- the baseline arm's per-15-minute date_bin bucket collapses same-bucket rows via MAX before LAG runs, so a same-bucket predecessor/successor pair does not give LAG anything to diff against. Needs a predecessor in the PRECEDING 15-minute bucket, not the same one. Left for a follow-up; documented in the PR body. * issue-3653 A8 option B (G1-5): seed IoLatencyWindowSql's LAG predecessor in the preceding 15-minute bucket The current-window pg_io_stats pair sat 30 seconds apart, inside the SAME date_bin('15 minutes') bucket, so the sampled CTE's MAX() aggregation collapsed them to one row per bucket before LAG ever ran -- raw_reads stayed NULL every time and IoLatencyWindowSql never executed, exactly the silent-family failure this guard exists to catch. Move the pair 15 minutes apart (one bucket earlier, then the bucket itself) with an increasing reads/read_time_ms counter so LAG resolves a real predecessor. Part of #3653 * 3653 B guard: exclude L3c's two superseded consts; fix stacked doc comments Two real CI failures after the dev rebase, neither a flake: 1. AssertEveryWindowSqlRan reported CpuBurnWindowSql and WalVolumeWindowSql as never-ran. Same situation as the three consts already excluded (SessionWindowSql/CpuWindowSql/WaitRateWindowSql, from lane L3a): L3c (#4178) declared these two only so the census keeps seeing the same SQL text PgTargetFactCollector uses to build the PG_CPU_BURN_CORES/ PG_WAL_VOLUME_SHIFT facts -- the detector itself only ever calls their tile twins, CpuBurnTileWindowSql/WalVolumeTileWindowSql. Added both to the existing supersededByTile exclusion set. 2. DocCommentHygieneTests.NoMemberCarriesTwoStackedSummaryBlocks: two <summary> blocks stacked on SeedSqlServerFamilyTablesAsync (247/252) and SeedPgTargetFamilyTablesAsync (349/353). Checked each pair first, per that test's own warning -- both describe the SAME member, the first an earlier/shorter draft ("one row... background history row") superseded by a later, more complete one written when a lane raised the seeding from 1 row to 12 (explaining the CollapseThreshold floor). Deleted the stale first block in each case; nothing lost, the second already carries everything the first said and more. Local: real dotnet build clean; DocCommentHygieneTests class run clean (0 failed). The guard tests themselves skip locally (no DARLING_TEST_PG) -- Windows CI's live PG service is the arbiter for the fixture-gap fix.
Part of #3653 (A8 option B, lane L3a).
What this PR does
Moves the four PG-target families this lane owns onto per-hour tiles, scored through
AnomalyGate.EvaluateTilesagainstPgTargetBaselineProvider.GetBucketMapAsync, with the never-blind fallback to today's whole-windowEvaluateZScorewhen no tile clearsAnomalyThresholds.MinTileSamplesor has a non-empty bucket:DetectTpsAnomalies): newTpsTileWindowSqlconst.DatabaseCounterWindowSqlis left byte-identical — the deadlock-rate detector still reads it, unchanged.DetectSessionAnomalies): newSessionTileWindowSql, keeping theper_collectionCTE and grouping the outer SELECT by tile.DetectCpuAnomalies): newCpuTileWindowSql. Peak time moves to a per-tilearray_agg(collection_time ORDER BY v DESC)[1]. The rawcpu_percentwindow scalar rides per tile; the detector reports the worst tile's raw value.DetectWaitProfileAnomalies): newWaitRateTileWindowSql. Rewritten in this lane to follow the coordinator's ruling on multi-arm families (see below), replacing L3a's original per-tile three-arm decision.L3a-2 (this lane): work done
Step 1 — Aurora waits conforms to the coordinator's ruling
L3a's original code ran all three trust arms (robust z / classical ratio / absolute fallback) per tile by hand through a private
DecideWaitProfileTile/WaitProfileTileDecision, then picked the worst firing tile. That broke the design in the four ways the brief named: no multiplicity correction on the ratio arm, an uncorrected k on the z arm's per-tile mean, a single untrustworthy tile able to switch the whole window to the absolute arm even when the start bucket trusted the z arm, and duplicated gate logic.Replaced with the ruling's shape:
GetBaselineAsyncatTimeRangeStart), exactly as dev's whole-window body decides it.baseline.IsTrustworthy && EffectiveRobustSigma > 0) moves onto tiles:AnomalyGate.EvaluateTiles(tiles, map, HeavyTailModifiedZThreshold, HeavyTailModifiedZThreshold, PgWaitProfileFallbackMsPerSec, PgWaitProfileFallbackMsPerSec, SigmaDisplayCap, window). When it returns null, today'sEvaluateZScoreruns overWindowTiles.WholeWindow(tiles).WholeWindow(tiles)'s Peak/Mean — no second SQL read. Commented in the source as outside B's scope.DecideWaitProfileTile/WaitProfileTileDecisiondeleted.modified_z,mean_modified_z,ratio,fire_threshold, baseline keys) comes from the bucket and values actually scored — the worst tile's on the tiled path, the start bucket's otherwise.AddTileMetadataruns only on the tiled path.dotnet build Darling/PerformanceMonitor.Darling.Analysis -p:EnableWindowsTargeting=true→ 0 errors).Step 2 — pins and census
PgTargetAnomalyTests.s_detectorSql: addedTpsTileWindowSql,SessionTileWindowSql,CpuTileWindowSql,WaitRateTileWindowSql(the reflection census requires every public const endingSqlto be listed).local_hour/dialect pins for each of the four new consts (same file,EveryDetectorRead_...test).ThePairGate_...test's regex pinnedAnomalyGate.EvaluateZScore(baseline, peakTps, avgTps, ... window: context.TimeRangeEnd - context.TimeRangeStart)verbatim, but L3a's TPS/sessions/CPU tiled call-flow refactor introduced a localwindowvariable computed once at the top of each method (also feedingEvaluateTiles), so the call site now readswindow: window. Updated the three regexes (TPS/sessions/CPU) to matchwindow: windowand added a pin that the local is assignedcontext.TimeRangeEnd - context.TimeRangeStart. Applied the same fix to the wait-profile detector's ownEvaluateZScorepin after step 1's rewrite.LocalClockBucketKeyTests's "$4..$6" census does NOT cover these consts — checked: that census (EveryBucketStatement()) only enumeratesPgBaselineProvider/PgTargetBaselineProviderbaseline queries, not detector window-SQL consts. Confirmed against lane L3b-2's own step-1 commit (1e9c8ce76), which touched pins for its own tiled families in this same PR wave and made no change to that census either. No action needed there.Darling/Darling.Testsfor the old const names and forDetectTpsAnomalies/DetectSessionAnomalies/DetectCpuAnomalies/DetectWaitProfileAnomaliestext pins andwindow_samples— no other pin needed updating.dotnet Darling.Tests.dll -class Darling.Tests.PgTargetAnomalyTests→ 44 total, 0 failed, 2 skipped (live-PG classes, expected withoutDARLING_TEST_PG).-class LocalClockBucketKeyTests -class PgTargetClockTests -class WindowTilesTests -class AnomalyGateTilesTests→ 41 total, 0 failed.What is NOT done (ran out of context before steps 3 and 4)
timescale/timescaledb:2.28.1-pg18on a free 558xx port,CREATE ROLE darling ...,DARLING_TEST_PGset, then*PgTargetAnomaly*,*PgTargetBetweenWaves*,*PgTargetWaitProfile*,*PgTargetClock*run against it (stripMicrosoft.WindowsDesktop.Appfrom the runtimeconfig first, restore after). This is the recipe's own "Skipped: N is not a pass" requirement — the tiled SQL for all four families has never executed against a real PostgreSQL. Highest-priority remaining item.SessionTileWindowSqlvs. dev'sSessionWindowSql(5 runs each, median), both numbers in this PR body.DARLING_TEST_PGunset — not run in this lane's pass (ran only the targeted classes above). dev has 214 Windows-only failures on this Mac per the brief; any failure outside that set needs naming.Handoff
The next lane/coordinator pass should, in order: (a) run step 3's live-PG proof — this is the acceptance-blocking item, since the tiled Aurora-waits rewrite in step 1 has never run against real PostgreSQL and the ruling's arm-choice logic (start-bucket decides, only arm 1 tiles) is exactly the kind of thing that looks right by inspection and needs a live check; (b) run step 4's timing and drop both numbers in here; (c) then the full suite once.
CHANGELOG entry
Base branch note
Built on
origin/feat/3653-b-bucket-map(#4168's head) becauseGetBucketMapAsyncwas not yet onorigin/devat the time this lane started. Per the lane brief, titledDO NOT MERGE (stacked on #4168); the coordinator rebases onto dev once #4168 lands. (#4168 has since merged to dev as41a10c729— a rebase may now be straightforward, but this lane did not attempt it, out of scope for L3a-2's brief.)Behaviour tests
Darling/Darling.Tests/PgTargetTileBehaviourTests.cs: all 7 pass live (timescale/timescaledb:2.28.1-pg18).Sessions_TwoHourShiftInFourHourWindow_FiresPerTile_WhereTheWholeWindowGateWouldStayQuiet— PASSSessions_TwoHourShiftInTwentyFourHourWindow_Fires_WithTheRaisedCutoff— PASSSessions_LoneSpikeInFourHourWindow_DoesNotFire— PASSSessions_SparseWindow_FallsBackToWholeWindowPath— PASSTps_TwoHourShiftInFourHourWindow_FiresPerTile_WhereTheWholeWindowGateWouldStayQuiet— PASS (fixed this lane)Cpu_TwoHourShiftInFourHourWindow_FiresPerTile_WhereTheWholeWindowGateWouldStayQuiet— PASSWaitProfile_TwoHourShiftInFourHourWindow_FiresPerTile_WhereTheWholeWindowGateWouldStayQuiet— PASSTPS root cause (diagnosed, then fixed): the fixture's minute-resolution rows only ran across minutes 0–11
of each hour, leaving a ~49-minute gap to the next hour's first sample. The per-collection LAG-rated tps for
that boundary-crossing sample divides one minute-increment's worth of xacts by the ~49-minute gap, so it comes
back near 1–2 tps instead of ~60–76 — a spurious low outlier inside every tile. That single low sample dragged
each tile's AVG below the
correctMean-raised cutoff even though its PEAK cleared easily, so no tile'sDecision.Firewas true (EvaluateTilesstill returned a non-null, non-firing verdict — the "still returns adisplay verdict even on a quiet pass" contract, not the never-blind-null path), and
DetectTpsAnomaliesreturned with no fact. Diagnosed with a temporary
[Fact](never committed) that ranTpsTileWindowSql's own6 binds directly and printed the raw per-collection rated rows, the bucket map per window hour, and the fired
fact count.
Fix: the TPS fixture's rows are now spaced every 5 minutes across the full hour (
i in 0..11, matching thesessions/CPU fixtures' own convention) instead of every 1 minute, with the per-step increment scaled from
*60to
*300to keep the seeded μ/σ tps unchanged under the new 300-second step. TwoINSERTstatements changed(
PlantTpsHistoryAsync,PlantTpsWindowAsync); no product code touched.Mutation check:
AnomalyGate.EvaluateTilesforced to alwaysreturn null→ the 5 tile-shift-dependenttests (sessions 4h, sessions 24h, CPU, TPS, wait profile) all failed as expected; the 2 non-tile-dependent
tests (
Sessions_LoneSpikeInFourHourWindow_DoesNotFire,Sessions_SparseWindow_FallsBackToWholeWindowPath)stayed green. Reverted; rebuilt;
git diff --stat PerformanceMonitor.Analysis/AnomalyGate.csis empty.Full suite (
DARLING_TEST_PGunset): 13667 total, 214 failed, 737 skipped — matching dev's known214-Windows-only-failure baseline on this Mac exactly.
*PgTargetAnomaly*+*PgTargetTileBehaviour*live:51/51 pass.
Not verified: CI (Windows) run of these tests — only this Mac's live-PG rig and the local suite.