goodhistogram: base error bound on true worst-case bucket error - #11
Merged
Conversation
The relative error bound was selected using DDSketch's midpoint error, (γ-1)/(γ+1). That figure only holds when a bucket is always reported by its midpoint, which is what DDSketch does. goodhistogram instead uses trapezoidal interpolation over the observed distribution, so a reported quantile can land anywhere in a bucket [b, γ·b] — including the edges. The true worst case is a value whose real location is the bucket start b reported at the end γ·b, a relative error of γ-1, roughly twice the midpoint figure. The consequence was that a histogram configured for a given error bound could report quantiles that visibly exceeded it: a 10% bound selected schema 2 (midpoint error 8.6%) whose reported quantiles could drift up to γ-1 = 18.9%, as seen in #9's pMax errors of +17.5%/+15.9%. Change schemaRelativeError to return γ-1 so schema selection honors the bound quantiles actually deliver. A given ErrorBound now selects a finer schema than before (10% picks schema 3, not schema 2), trading a modest amount of memory for a guarantee that holds. Add three full-range resolution presets differentiated only by accuracy and memory: - CoarseParams schema 1 ~41.4% error 126 buckets ~1 KB/histogram - StandardParams schema 2 ~18.9% error 252 buckets ~2 KB/histogram - FineParams schema 3 ~9.05% error 504 buckets ~4 KB/histogram Co-Authored-By: roachdev-claude <roachdev-claude-bot@cockroachlabs.com>
Co-Authored-By: roachdev-claude <roachdev-claude-bot@cockroachlabs.com>
alyshanjahani-crl
approved these changes
Aug 18, 2026
alyshanjahani-crl
left a comment
There was a problem hiding this comment.
LGTM, the only review agent finding i think is worth calling out is:
withDefaults() (histogram.go:275) maps a zero ErrorBound to 0.10, and none of the existing latency/size presets set ErrorBound (HiResLatencyParams, IOLatencyParams, ResponseTimeParams, LongRunningParams, DataSizeParams, MemoryUsageParams). So this PR silently moves all of them — and every caller relying on the default — from schema 2 to schema 3, doubling their per-histogram memory.
But IIUC that is something you'd probably do in a PR in cockroach repo whenever you update the goodhistogram version?
Contributor
Author
|
yep, that's the plan! |
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.
Problem
goodhistogram's error bounding is inspired by DDSketch: the user specifies a relative error bound, and the coarsest Prometheus schema whose error fits is chosen. But the error formula it used,
(γ-1)/(γ+1), is DDSketch's midpoint error — it only holds when a bucket is always reported by its midpoint, which is exactly what DDSketch does.goodhistogram diverges here: it approximates quantiles using the shape of the observed distribution (trapezoidal interpolation), so a reported quantile can land anywhere in a bucket
[b, γ·b], edges included. The true worst case is a value whose real location is the bucket startbreported at the endγ·b— a relative error ofγ-1, roughly twice the midpoint figure.So a histogram configured for, say, 10% error selected schema 2 (midpoint error 8.6%) while its reported quantiles could drift up to
γ-1= 18.9%, silently violating the configured bound. This is what users hit in #9, whose log showspMaxerrors of +17.5% and +15.9% under a 10%ErrorBound— right at the schema-2γ-1ceiling.Fix
As requested, this fixes the bound, not the quantile calculation.
schemaRelativeErrornow returnsγ-1— the error the estimator can actually produce — so schema selection honors the bound quantiles really deliver. A givenErrorBoundnow picks a finer schema than before (10% → schema 3, not schema 2), trading a modest amount of memory for a guarantee that holds.New resolution presets
Three full-range presets (
[1, math.MaxInt64]) differentiated only by accuracy and memory:CoarseParamsStandardParamsFineParamsMemory is the per-histogram counts array; the boundary/lookup tables live in a single shared, cached config, so allocating many histograms from one preset doesn't multiply that overhead.
Notes
γ-1vs(γ-1)/(γ+1)distinction and a Resolution presets table.TestPickSchema,TestNewConfig,TestWindowedSchema) plus a newTestResolutionPresetslocking in schema, bucket count, and bound for each preset.schemaRelativeError(benchSchema)so they stay comparable regardless of the bound arithmetic.Related: #9