release: keep compacted query attribution honest - #190
Closed
samo-agent wants to merge 7 commits into
Closed
Conversation
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.
Stack / scope
Addresses #136. This reworks and supersedes the approach in #150; it does not modify #150's branch.
This PR is intentionally stacked on #182 (
agent/w2-b1at2af229a). #182 is still open/draft, so this branch has not been rebased ontomain; rebase it after #182 lands. Do not merge this PR before its base. This does not close #122, #156, or #163.What changed
top('query_id')now emits a positiveNULLresidual equal tomax(total wait counts - preserved named query counts, 0)per source row. That residual enters the same ranking stream beforen, so compacted-away load can win a top-N slot.query_countsonce withGROUPING SETSand itswait_countsonce. It removes fix: keep compacted query attribution honest #150's second relation/query-array scan and window-wide materialized expansion.query_idfilter uses raw samples. The widened retention guard is deliberately narrow: it raises only when ordinary planning selects a rollup that has matching retained coverage in the requested slice before the physical oldest raw sample._pick_source()naming a fallback rollup is not sufficient.rollup_1h.minute_countsmust contain a non-NULL pre-raw minute; legacy/incomplete hourly detail is handled conservatively._raw_oldest_sample()is passed to the guard when real coarser history exists; tie-only calls without such history retain_raw_retention_start(). That composes with Release blocker: fresh-install tie drills reject retained raw samples #163's pending logical/physical boundary split.compare()NULL-safely pairs rollup residuals, andsummary()renders them as(other / unattributed)rather than claiming all compacted load was never captured.Five-minute install proof
The regression fixture holds one raw row with seven appearances of query 111, five minutes old. It also holds first-minute
rollup_1mand valid-detailrollup_1hrows covering only that same retained raw minute. Even though_pick_source(default-left-edge)names a fallback rollup, the exact-query readers remain usable:ash.aas(query_id => 111):source=raw,buckets_with_data=1,backend_seconds=7.00ash.timeline(query_id => 111): 60 rows, one data row,source=raw, data-rowavg_aas=0.12ash.top('database', query_id => 111):source=raw,backend_seconds=7.00,pct=100.00ash.compare(..., dimension => 'database', query_id => 111):source_1=raw,source_2=raw,avg_delta=0.00, both percentages100.00The same fixture proves that unrelated database history does not raise, while a matching old rollup outside raw coverage does raise.
Combined source-selection rule re-derived on #182
aasquery_idforcesraw; matching pre-raw rollup history raises. Wait+query retains the tie guard. Otherwise_pick_source_agg()applies: spans over one hour preferrollup_1mwhen the requested start is within its retention; other calls fall through to_pick_source(). Unfiltered/database-onlyrollup_1hreads use valid minute detail, or discloserollup_1h_flat; wait-filtered hourly reads use hour arrays.timelineaas, independently for its window.topquery_id, ordimension='query_id'plus a wait filter, forcesraw. Other breakdowns use_pick_source_agg(). Plain hourly database breakdowns may use valid minute detail; hourly wait/query dimensions remain hour-grain.n; raw NULL remains genuinely uncaptured attribution.compareaas; dimensional rows come from per-windowtop. Explicit query filters force both sides raw. A query dimension with a wait filter is raw. Otherwise each side independently selects its aggregate source.top's support rule, then NULLs peak and p99 on both sides when retained grains or effective buckets differ.There is no disagreement with #182: its effective bounds, disclosed bucket,
rollup_1h_flat, and honest extrema NULLing remain authoritative. Exact query filters simply bypass compacted sources. The known_pick_source_agg()end-watermark/completeness gap remains #122 and is not claimed fixed here.RED / GREEN (verbatim)
1. Residual competes for top-N
RED:
GREEN:
The same block exact-asserts two rows,
26.00backend-seconds,100.00%, one NULL-safe compare residual with delta0.00, and the exact summary label/value.2.
rollup_1htop-100 compactionRED:
GREEN:
The fixture stores 100 named rows / 300 seconds after hourly top-set truncation while wait totals remain 400 seconds. The final result is 101 rows / 400 seconds / 100%, with an exact 100-second residual and NULL peak/p99 at hour grain.
3. Narrowed exact-query widening
RED from the broad
query_id => guardimplementation (same-minute first rollup present):GREEN:
Residual cost measurement
PostgreSQL 17.9;
shared_buffers=128MB,work_mem=4MB, JIT off. Fixture: 43,200rollup_1mrows (30 days × one database, 86 MiB table), each with 100 query pairs, 32 wait pairs, and a 75-count residual. Each variant was fully consumed with count/sum and run three times underEXPLAIN (ANALYZE, BUFFERS, TIMING OFF).Naive #150 spilled 2,628,179 temp blocks read / 2,629,970 written (about 20.05 / 20.07 GiB; shared hit/read 800/20,928). Final spilled 10,172 / 10,192 temp blocks (about 79.5 / 79.6 MiB; shared hit/read 531/10,384). Naive and final outputs were identical: 4,363,200 rows and total count 29,160,000.
This is relevant to #156's rollup-prefix cost class, but it changes only the dimensional query-array work; it does not resolve or close #156.
Validation
git diff --check: GREEN.Exact touched line ranges
Relative to #182 head, final-file line numbers:
.github/workflows/test.yml: 4206-4892README.md: 231-246blueprints/AAS_API.md: 38-42, 124-125, 132-139, 172, 180-181, 219-240, 244-249, 251, 255-256, 289-293, 462, 464-478blueprints/AAS_EXAMPLES.md: 240-253, 320-322, 325, 328-330, 337-338, 341blueprints/AAS_USER_STORIES.md: 96-99, 169-170, 227-231sql/ash-install.sql: 191-192, 2574, 3496, 3500-3516, 3528, 3585-3589, 3611-3620, 3640-3732, 3756, 3760-3764, 3780-3783, 3793-3797, 3893-3896, 4021-4022, 4062-4063, 4086, 4088, 4090-4119, 4132, 4235-4237, 4274-4275, 4305-4306, 4308-4324, 4503-4506, 4716-4733, 4735-4774, 4776-4784, 4833-4836, 4886-4887, 4924-4933, 4935-4951, 5231, 5348-5355, 5357, 5463-5466, 6512, 6514, 6533, 6536, 6539, 6542No sibling-owned reader-window validation, privilege, rotation, or production-container surface was changed.