db,collections: columnar two-pass top-K (ORDER BY <numeric> LIMIT k at MAX speed) - #223
Merged
Conversation
…t MAX speed)
ORDER BY ClusterId DESC LIMIT 1 WHERE ProcId < 5 took 9.1s while MAX(ClusterId)
WHERE ProcId < 5 took 2.5s -- the same answer. TopK fed QueryProject, building a
classad.Value for every matching row (~1M) and heaping them to keep k; MAX reduces
raw column values with no per-row allocation.
Two-pass:
- collections.TopKOrderThreshold scans the order column like the numeric aggregate
(block-zone pruning, MVCC visibility, cold-tail escape, active-segment row
fallback) but keeps the best k VALUES and returns the k-th (the cutoff) -- raw
floats, never materializing a record. Same availability gate as NumStatsQuery.
- db.TopK re-runs the projection with appended, which
columns and zone maps narrow to ~k rows (plus ties); only those are projected.
Falls back to the original full scan when the cutoff is unavailable or there are
<=k matches, so it is never worse; capped at k<=65536. Archive and mutable paths.
TestArchiveTopKColumnarMatchesBrute: two-pass == brute-force top-K across desc/asc,
k in {1,3,50,>matches}, four filters (incl. one excluding the global-max row), on
columnarized AND fallback archives; argmax reassembly of a non-order column;
mutation-checked. TestTopKOrderThreshold pins the cutoff + match count. db suite +
columnar collections suite green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…sibility) A top-K query was invisible to EXPLAIN ANALYZE: the opTopK op returned rows with no scan-stats trailer, so the diagnostic showed only fetch/rows and no scan breakdown. The cutoff scan (TopKOrderThreshold) now fills a ScanStats -- records visited, column-decided (columnar sealed segments) vs reassembled (active-segment fallback), and matched -- reusing the same ScanStats the projected scan reports. db.TopKStats returns it; a new opTopKStats op streams the k rows plus a stStreamStats trailer (reusing putScanStats/readScanStats); Client.TopKStats reads it. Separate opcode so an older server rejects it and the client falls back to opTopK (rows, no breakdown). TestTopKStatsOverRPC asserts the trailer round-trips (visited>0, matched == the filtered set) over a columnarized archive; db TopKStats assertion added. Full db and dbrpc suites green. Follow-up (htcondordb): wire EXPLAIN ANALYZE of a top-K-routed query to Client.TopKStats and render via the existing scanStatsLines. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Makes
ORDER BY <numeric attr> {DESC|ASC} LIMIT kas fast as the equivalentMAX/MIN. Based onmain.Problem
db.TopKfedQueryProject— it built aclassad.Valuefor every matching row (e.g. ~1M forProcId < 5), heaped them, and kept k. On a production archiveORDER BY ClusterId DESC LIMIT 1took 9.1s while the logically identicalMAX(ClusterId) WHERE ProcId < 5took 2.5s, becauseMAXreduces raw column values with no per-row allocation and TopK allocated a million Values to discard.Fix: two-pass columnar top-K
Collection.TopKOrderThresholdscans the order column the same way the numeric aggregate does — block-zone pruning, MVCC visibility, cold-tail escape handling, active-segment row fallback — but keeps the best k values and returns the k-th (the cutoff). It reads only floats; it never materializes a record. Same availability gate asNumStatsQuery(accelerator on, numeric schema field, conjunction of scalar numeric comparisons).db.TopKre-runs the projection withorderAttr >= cutoff(desc) /<= cutoff(asc) appended. The columns and zone maps narrow that to ~k rows (plus ties), so only those are projected and heaped.Falls back to the original full-scan path unchanged when the cutoff is unavailable (accelerator off, non-numeric order key, non-columnar constraint) or when there are ≤k matching rows, so behavior is never worse than before. Capped at k ≤ 65536 (beyond that "top-k" ≈ full sort). Both the archive and mutable-table paths use it.
Expected:
ORDER BY ClusterId DESC LIMIT 1 WHERE ProcId < 5drops from ~9s to ~MAXspeed.Correctness
db.TestArchiveTopKColumnarMatchesBrutecompares the two-pass to an independent brute-force top-K across desc/asc, k ∈ {1, 3, 50, >matches}, four filters (including one that excludes the row holding the global max, forcing the cutoff to reflect the filtered max), on both a columnarized archive and a non-columnarized one — they must agree with each other and with brute. It also checks argmax reassembly (selecting a non-order column with LIMIT 1 returns that column from the actual winning row). Mutation-checked: perturbing the cutoff makes the columnar case fail, proving the fast path is exercised, not silently bypassed.collections.TestTopKOrderThresholdpins the cutoff value + match count directly. Fulldbsuite and the columnarcollectionssuite pass.🤖 Generated with Claude Code