Repository navigation
refactor!: summary kernels on asap_sketchlib main (de15d66) - #783
Merged
Merged
Conversation
UnivMon fields rows/cols/layers/total_weight/layer_heaps, new/clear/ update_weighted/estimate_*, OwnedDataInput, UNIVMON_MAX_LAYERS; Hydra rows/cols and HydraCounter::CountMinSketch; HLL update_with_hash. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ap_sketchlib types The portable HllSketch, KllSketch, DdSketch and CountMinSketch are gone from asap_sketchlib. HLL is a classic HyperLogLogImpl at each precision 4..=18 (register types from impl_hll_registers!); KLL is Kll::with_k; DDSketch the native DdSketch (get_value_at_quantile, get_count); plain Count-Min a regular-path CountMinF64 that adds every weight, zero and negative ones included; KMV the native Kmv. Memory charges come from Kll::memory_bytes, Kmv::memory_bytes and CountMinSketch::memory_bytes. The DDSketch ratio tests interpolate PromQL quantiles from the sketch's order statistics. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ount Sketch asap_sketchlib drops WeightedFrequency. The Planner kernel keeps its API over CountMinSketchWithHeap / CountSketchWithHeap (capacity > 0) or a plain CountMinSketch / CountSketch (capacity 0), all Vector2D<f64> on the regular path. An identity tuple is one TupleKey, hashed and retained as bytes. Planner checks what sketchlib does not: finite weights (and nonnegative for Count-Min), finite Float64 identities, an odd Count Sketch depth, and a total weight magnitude within f64. Top-k rows are the heap's keys at their current estimate, descending, ties by key bytes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HydraKllSketch is gone from asap_sketchlib. The kernel is a one-column Hydra over HydraCounter::Kll, read by Hydra's median of the cells' KLL quantiles, and charged the cells' Kll::memory_bytes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Its heap makes it far larger than the other group-state kinds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ntiles Kll allocates its item buffer as items arrive, so each stored-pane state takes 1,100 samples to hold its full buffer (about 11 kB) again; with one sample the merge fit the 4 MiB limit. DdSketch reads its minimum and maximum exactly, so the cross-zero ratio fixture interpolates interior order statistics: [-2, -1, 1.011, 2] instead of [-1, 1.011]. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
asap_sketchlib main, with ProjectASAP/asap_sketchlib#242 merged. UnivMon builds any row count its 128-bit key hash covers, so the UnivMon shape tests probe that budget instead of a 20-row limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
sketchlib main's UnivMon::check_shape accepts any row count within the 128-bit key hash, but its ASAPv1 encoder still refuses more than MATRIX_MAX_ROWS rows. Keep the executor's builds and the Planner's hash_independent at 20 rows, as on the old pin, so every UnivMon state the executor builds can be stored.
Kll::memory_bytes and the heap sketches' memory_bytes already include their own struct; adding size_of::<Self>() on top counted it twice.
eb04e7f is the 0.4.0 release commit on top of de15d66; the library code is the same, only the version, CHANGELOG and README change.
Contributor
|
I added four commits on top of this branch so that it also covers #785. None of the existing commits were changed.
|
zzylol
marked this pull request as ready for review
October 21, 2026 01:52
…main # Conflicts: # crates/plan-selection/src/lib.rs
Planner main fails clippy (-D warnings) and one runtime_install test since #787, #789, #795 and the joint search commit: - summary_coverage.rs: a Window alias for the relative time window (type_complexity). - workload_candidates.rs: an array instead of vec! (useless_vec). - query_candidates.rs: OperatorNode::reachable instead of a closure. - stage1_to_stage3.rs: allow too_many_arguments on search_stage2_and_stage3, as lib.rs already does once. - joint_search_equivalence.rs: struct initializer for the settings, and slice::from_ref instead of a clone. - runtime_install.rs: the merged-pane DDSketch candidate of the p99 avg workload is now P3-m1 (was P5-m1); every other assertion of that test passes unchanged. Co-Authored-By: Claude Opus 5.5 <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.
Closes #643. Closes #644.
Why
ASAPPlanner pins asap_sketchlib at 3f44cf1c. asap_sketchlib main (de15d66,
ProjectASAP/asap_sketchlib#242) removes the types the executor's kernels wrap:
the portable
HllSketch,KllSketch,DdSketch,CountMinSketchandHydraKllSketch, andWeightedFrequency. It also renames the UnivMon, Hydraand HLL APIs. Staying on the old pin keeps the Planner on wrapper types that
no longer exist upstream.
What
rev = "de15d664264a628e63bd5a67494aa53a62791e9f".types (Use the asap_sketchlib sketch types #643).
WeightedFrequency) is now a Planner type over f64CountMinSketchWithHeap/CountSketchWithHeap, or plainCountMinSketch/CountSketchat capacity 0. The weight and identity checks live in Planner.Hydrawith one key column overHydraCounter::Kllcells (HydraKLL on asap_sketchlib Hydra #644).255, UnivMon L2 up to 19 rows. sketchlib rows past 20 now hash independently,
and UnivMon accepts any row count that fits its 128-bit key hash.
Results change where the native sketches differ from the wrappers they replace:
qofnvalues (quantile_over_timeand friends)⌊q(n−1)⌋get_value_at_quantile(q): rank⌈q·n⌉, as KLL already reads;q = 0andq = 1give the exact min and max. Neither interpolatesHllVariant::Regular)4608 + 32kbytes (11,008 at k = 200)Kll::memory_bytes, which grows as items arriveCountMinSketch::memory_bytes(struct + rows × cols × 8)Kll::memory_bytesKMV::approx_memory_bytesKmv::memory_bytescapacityrows()reads every held key at its current estimate, as beforeTupleKeybytesf64::MAX, so Count Sketch now refuses large cancelling weights it used to acceptHydraKllSketch's own hashingHow
hll_sketch.rsdeclares oneHyperLogLogImpl<Classic, HllRegistersP{p}>perprecision 4..=18 with
impl_hll_registers!. A privateRegistersenum holdswhichever one was built. States at different precisions do not merge.
TupleKey(
-0.0and0.0encode the same), hashed withDataInput::Bytes, and theheap keeps those bytes. Planner checks what sketchlib does not: finite
weights (nonnegative for Count-Min), finite Float64 identities, an odd Count
Sketch depth, and a total weight magnitude that stays within f64. Shapes are
checked with
CountMinSketch::check_shape/CountSketch::check_shape.Hydra::with_schema(rows, cols, ["key"], HydraCounter::Kll(..)), as HydraKLL on asap_sketchlib Hydra #644 names it.The key's joined label values are the single key column. A quantile is the
median over rows of the cells' KLL quantiles.
GroupStates::Keyed::emptyis boxed becausethe heap makes it much larger than the other group-state kinds (clippy
large_enum_variant).Key code interfaces added / modified
Links are at the branch head be071a6 and resolve once it is pushed.
crates/executor/src/summary_kernels/weighted_frequency.rscrates/executor/src/summary_kernels/hll_sketch.rscrates/executor/src/summary_kernels/count_min_sketch.rs,crates/executor/src/summary_kernels/factory.rscrates/executor/src/summary_kernels/hydra_kll.rs,datasketches_kll.rs,dd_sketch.rs,kmv.rsUsage. For
topk(2, sum by (job) (rate(data_total[5m])))withdata_totaldeclared a counter, plan selection picks a Count-Min-with-heapcandidate. The executor's keyed build (
StateColumnUpdater) then makes oneWeightedFrequencyper group. These numbers come from running the code atbe071a6 in a scratch test:
Before this PR
The executor wrapped asap_sketchlib's portable types and
WeightedFrequency.None of them exist on asap_sketchlib main, so the Planner could not move its
pin past 3f44cf1c.
After this PR
The Planner builds and tests against asap_sketchlib main. Every kernel runs on
a native sketchlib type, and the keyed top-k state's weight rules are Planner
code. For four values 1, 2, 3, 4 at α = 0.01, the DDSketch kernel now returns
1, ~1.994, 4, 4 at q = 0, 0.5, 0.9, 1 (
dd_sketch::quantile_reads_rank_ceil_q_n).Before, q = 0.9 read the third value. An HLL at precision 12 over 1,000
distinct items reads 998, an integer (
hll_sketch::cardinality_is_an_integer).Evidence
at be071a6. The DDSketch and HLL figures above are asserted by the tests
they name.
Verification
cargo fmt --all -- --check: clean.cargo clippy --workspace --all-targets --all-features --locked -- -D warnings: clean.cargo check -p asap-planner --tests --locked: clean.cargo test --workspace --locked --no-fail-fast: 1,772 passed, 0 failed, 34 ignored.tools/verify_metricsql_external_consumer.sh,tools/verify_metricsql_vendored_baseline.py: pass.New tests:
count_min_sketch::non_positive_weights_are_added: a zero weight adds nothing and a negative one subtracts. It fails with the old skip (5.0, expected 3.0).hll_sketch::precision_range_and_mismatched_merge: precisions 4..=18 build 2^p registers, 3 and 19 fail, and different precisions do not merge.hydra_kll::quantiles_per_key_survive_merge: a key's quantile reads its own values, and a merge keeps both inputs'.count_min_sketch::merge_rejects_a_different_shape: states with different rows or columns do not merge.datasketches_kll::merge_rejects_a_different_k: KLL states with different k do not merge.dd_sketch::quantile_reads_rank_ceil_q_n: the quantile at q reads rank ⌈q·n⌉, and q = 0 / q = 1 read the exact min / max (1, ~1.994, 4, 4 for 1..4).hll_sketch::cardinality_is_an_integer: the cardinality has no fractional part (998 for 1,000 items at p = 12).weighted_frequency::{signed_weights_and_typed_identities, invalid_updates_leave_the_state_unchanged, signed_zero_and_shape_rules}: Count Sketch ranks signed estimates and typed identities do not collide; refused weights and identities leave the state unchanged; signed zeros are one identity; even Count Sketch depths and zero dimensions are refused; depth 32 Count-Min and depth 255 Count Sketch build.weighted_frequency::deep_states_update_merge_and_read: Count-Min with a heap at 272×32×4 and Count Sketch at 64×21 (with and without a heap), all deeper than ASAPv1's 20 rows, build, update, merge and read.Changed expectations:
executor::univmon::adoption_rejects_terminal_or_invalid_sketches: the misshapen state claims 26 rows of 16 columns (130 hash bits), was 21 rows. sketchlib now builds 21.estimators::univmon::univmon_past_twenty_rows_is_not_hash_independent→univmon_rows_are_bounded_by_the_hash_budget: 21 rows × 32 columns (126 bits) is hash independent with an L2 guarantee; 22 × 32 (132 bits) is not. It was "21 rows is never independent".stored_pane_memory_limit: each state takes 1,100 samples, was 1. Native KLL allocates its buffer as items arrive, so it needs those samples to be charged about 11 kB again. The limits and assertions are unchanged.promql_numeric_regressions::checked_ratio_must_not_certify_cross_zero_interpolation: values [-2, -1, 1.011, 2], was [-1, 1.011]. The native sketch reads min and max exactly, so the counterexample has to interpolate interior order statistics. The test's reference PromQL interpolation is computed by a test helper from the sketch's order statistics, because the portablequantile_interpolatedis gone. The executor itself does not interpolate; it answersquantile_over_timewithget_value_at_quantile(q), rank ⌈q·n⌉, as it does for KLL. The assertion is unchanged.promql_to_post_asap::ddsketch_ratio_bound_holds_for_signed_pinned_sketch_evaluations: same interpolation helper; the bound is unchanged.state_column_updaterand the row-reference harness: a weighted frequency state's fingerprint is its counters plusrows(usize::MAX)(held keys at current estimates). It was the serde JSON of the old state, which also held each candidate's stored score. Stored heap counts are no longer compared; counters and every held key's current estimate still are.Architectural decisions
WeightedFrequencystays a Planner type over sketchlib matrices and heaps.Its weight and identity rules belong to the executor's input contract;
sketchlib only provides the sketches.
runtime-precision HLL would have to be a new sketchlib type.
Limitations and follow-up
refreshed only when that key is updated or the state is merged. Those stale
counts decide which key is evicted;
rows()rescores every held key, so theestimates it returns are current.
PromQL's interpolated quantile. Measured on
[1, 1, 1, 30, 100]at α = 0.0049, q0.9 / q0.5 = 99.1 against the exactinterpolated 72. The old ⌊q(n−1)⌋ rule gave 30, so the gap predates this PR.
approx_memory_bytestwice per keyedupdate, and each call walks the heap, O(heap).
approx_memory_byteswalks every cell, O(rows · cols).sketchlib's
to_asapv1refuses them.CountMinSketch<Vector2D<i32>, FastPath>, unchanged.Human review — do not complete with an agent
🤖 Generated with Claude Code