Repository navigation
Conversation
The decoder reads the three __asap_row_sampled_* attributes into ObservationValue::row_sample and keeps them out of series identity. Count-Min and Count Sketch add 1/p only to the admitted rows; DDSketch inserts the value with weight 1/p. A point whose row count, mask or p does not fit the sketch is rejected as InvalidRowSample and counted in dropped_row_sample. KLL, HLL and Count-Min with heap reject row-sampled points, as the Go collector does.
A new series was inserted before its first observation was applied, so an observation the observer rejected left an empty series behind (and under EvictOldest evicted another one). Observe into the fresh sketch first and insert it only on success.
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.
Ingest SDK row-sampled points in the Rust collector
Why
The Go SDK patch (and the Rust SDK fork in progress) can sample each measurement per sketch row and send every admitted measurement as one Gauge point. The raw value is not pre-weighted, and three reserved attributes carry the sampling decision:
__asap_row_sampled_admitted_rows__asap_row_sampled_rowsdthe SDK sampled for__asap_row_sampled_sample_ppThe Go collector (
asapedgeprocessor/ingest.go,warm_sketch.goobserveRowSampled) applies these. The Rust collector dropped them, because a non-string attribute that is not a reserved key was silently discarded. Each sampled point was then counted as a plain weight-1 sample, so CMS and Count Sketch counts and DDSketch counts came out aboutptimes too small.What
ObservationValue::row_sampleand keeps them out of the labels (series identity).1/ponly to the admitted rows.1/p. With the tail split, values above tau arrive atp = 1and count once.StatsSnapshot::dropped_row_sample. The checks are: row count equals the sketch'sd, mask is nonzero and fits indbits,0 < p <= 1, and1/pis an integer.EvictOldestevicted another one). Now it changes no series.How
1/padded to each admitted row's cell1/p, no envelope rescale)±count·1/padded to each admitted row's celld = 1, mask1)+= 1/ppon the envelope, requires onepper windowpstamped on the envelope) cannot express. Per-point weights can.i64cells,u64DDSketch buckets), so1/pmust be an integer, for examplep = 2^-k.admission_ratestays 1, because the counters already hold the weighted counts.sketches::regular_path_celltherefore copies sketchlib's column and sign rule (hash64_seeded(row, key), low 32 bits mod cols, bit 63 = sign). Theupdate_rows_match_sketchlib_updatetests pin it to sketchlib's ownupdate_weighted.Key code interfaces added / modified
observation::RowSample { admitted_rows: u64, rows: u32, sample_p: f64 }, with:admitted_row_weight(sketch_rows) -> Result<i64, PrecomputeError>admits(row)ObservationValue::row_sample: Option<RowSample>. It is skipped in serde when absent.observation::reject_row_sample(obs, sketch)PrecomputeError::InvalidRowSample(String)andStatsSnapshot::dropped_row_sampleCMSWrapper::update_rows,CountSketchWrapper::update_rows,DDSketchWrapper::update_weightedotap::ATTR_ROW_SAMPLED_{ADMITTED_ROWS,ROWS,SAMPLE_P}Usage:
Major algorithm changes
1/p, so each row's cell is an unbiased count.estimatealready takes the median. Its regular-path Count-Minestimatetakes the minimum, which is biased low on row-sampled data. This PR leaves the library andCMSWrapper::estimate_countunchanged, so a consumer of a row-sampled CMS has to read the median of rows.Before this PR
An SDK sends 32 000 occurrences of key
k, sampled per row atp = 1/8,d = 4. About 13 000 points arrive. The CMS forkholds about 13 000 in every row, against a true count of 32 000.After this PR
Each row holds about 32 000 (each admitted row ≈ 4 000 × 8). The test
row_sampled_stream_counts_are_unbiasedchecks that the median over rows is within 5% for every key and that the mean error over keys is under 1%.Verification
cargo fmt --check: cleancargo clippy --all-targets --all-features: no warningscargo test --all-features: all pass (lib 110 passed / 1 ignored, integration suites pass)observation::tests::admitted_row_weight_validates_against_the_sketchotap::codec::tests::row_sampled_keys_are_parsed_and_kept_out_of_series_identitysketches::{cms,countsketch}::tests::{update_rows_match_sketchlib_update, row_sampled_stream_*_are_unbiased, unsampled_tail_points_count_with_weight_one}sketches::cms::tests::row_count_mismatch_is_rejectedsketches::ddsketch::tests::{row_sampled_body_with_unsampled_tail_matches_full_stream, unsampled_tail_points_count_with_weight_one}tests/runtime.rs::invalid_row_sample_is_counted_and_opens_no_series. This one fails on the oldwindow.rs(left: (1, 1) right: (1, 0)) and passes with the fix.