feat(downsampling): persist decimated blocks and interpolate rate-like functions over them - #481
Draft
u-veles-a wants to merge 141 commits into
Draft
feat(downsampling): persist decimated blocks and interpolate rate-like functions over them#481u-veles-a wants to merge 141 commits into
u-veles-a wants to merge 141 commits into
Conversation
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
Signed-off-by: Alexandr Yudin <57181751+u-veles-a@users.noreply.github.com>
…on block min-time instead of query duration/retention
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.
Why
Prom++ keeps raw samples only for
--storage.tsdb.retention.time; everything older is deleted. Long-range graphs (weeks, months) are therefore either empty or force an oversized raw retention, which costs memory and disk.This branch introduces downsampling of persisted blocks: when a head is persisted, a decimated block is written next to the raw one, and wide-range queries are served from it instead of from raw data.
Decimation has an unavoidable side effect: when the decimation interval is comparable to the window of a range function,
rate(m[2m])may not catch two samples in the window and returnsNaN— the data is there, just sparser than the window needs. The read path is therefore extended with an upsampler: for a narrow allow-list of functions the missing samples are restored by linear interpolation.The feature is off by default: without
downsamplinginPROMPP_FEATURESnone of the paths below activate, and neither behavior nor the on-disk block format changes.What
Configuration
PROMPP_FEATURES=downsampling=<duration>(pp-pkg/featuresflags). On startup the value is validated to be larger thanscrape_interval, otherwise the process exits with code 2.pp_storage.Options.Downsampling,manager.Options.DownsamplingMSand the newpp_pkg_storage.AdapterOptions{RetentionMS, DownsamplingMS}.Write path
pp/go/storage/block.Writerwrites a second block for the same interval withThanos.Downsample.Resolution = downsamplingMs(the decimation itself is done by the existingDownsamplingDecodeIteratorin the C++ core).Read path — source selection
Manager.Querier/ChunkQuerierpick blocks by query width (skipBlock): withneedDownsampling(maxt-mint)only downsampled blocks are selected, otherwise only raw ones. In the downsampling case the left border is extended back by one resolution, so that the first sample of the window has a predecessor to interpolate from.pp/go/storage/querier): newactiveHeadfield andWouldDownsample(); for allow-list functions the active head stops applying its own query-time downsampling and returns raw samples.ChunkQuerierreceivedretentionMS/downsamplingMSfor symmetry.Read path — interpolation
pp-pkg/blocks/upsampler:Querier/SeriesSet/Series/Iterator. Synthetic samples are injected only for{rate, increase, delta, deriv, irate, idelta}, with a step ofRange/2, and only into gaps betweenRange/2andresolutionMS*2— a wider gap is a real outage and is left alone. Histogram samples pass through untouched.pp/go/storage/upsampler.NeedsUpsampling— the shared allow-list predicate, placed inpp/gobecausepp/gocannot importpp-pkg.upsampler.ResolutionQuerier— a transparent wrapper a storage uses to declare its nominal resolution upwards.pp-pkg/blocks/fanout— a fork of the vanillastorage/fanout.go: it collects the maximum resolution across all sources of the query and wraps the merged querier inupsampler.Querieronce. Otherwise interpolation breaks at the storage boundary: the anchor pair "last sample of a block → first sample of the head" lives in two separate wrappers and no synthetic sample appears there. The fork needs its own noop querier, sostorage/remote/read.gonow returnsfanout.NoopQuerier()/NoopSeriesSet().web/api/v1: queries reaching beyond retention get2*downsamplingadded toLookbackDelta— without it an instant selection finds no sample on sparse data.Observability and tooling
prometheus_tsdb_blocks_loaded_by_durationnow counts raw blocks only; decimated ones go to the newprometheus_tsdb_downsampled_blocks_loaded_by_durationwith the sameduration_minuteslabel. The name of the existing metric is unchanged.resolution.prompptool blocks— new command listing the blocks on disk (ulid, interval, size, resolution), with-r/--human-readable.Tests
Go
pp/go/storage/upsampler— the predicate:nilhints,Range <= 0, every allow-list function and functions outside it.pp-pkg/blocks/upsampler—Iterator: no gap, gap, gap at the end of the slice, both bounds (step,resolution*2) and their independence fromRange, histogram passthrough,Reset;Seek: before the first sample, exactly on a real one, into the middle of a gap, past the last sample, a repeated seek inside a partially yielded window, theseekAdvanceBasepath. PlusQuerier.shouldWrap,SeriesSet/Series, and benchmarks for the seek-heavy and the non-triggering paths.pp-pkg/blocks/fanout— a port of the upstream fanout tests (merge, primary/secondary error propagation) and the fork specifics: max resolution across sources, no wrapper when no resolution is declared, noop secondary skipped, queriers opened before a failure being closed. Separately the regressionTestInterpolationAcrossStorageBoundary: a synthetic sample inside the gap between the blocks and the head (with per-storage wrapping there is none).pp-pkg/blocks/manager— block selection by query width,ResolutionQuerierreturned on a downsampling range, split raw/downsampled metrics.pp-pkg/storage—adapter_upsampler_test(theWouldDownsamplegate),adapter_promql_test(rateover a gap yields a value instead ofNaN, plus the regression that behavior is unchanged when downsampling is off),adapter_bench_test.pp/go/storage/querier—downsamplingMSis zeroed only for allow-list functions, the result comes from the raw path,WouldDownsample(), updated optimize tests.Manual check: run with
PROMPP_FEATURES=downsampling=5mand a short--storage.tsdb.retention.time, wait for blocks to be persisted, and evaluaterate(metric[2m])over a range beyond raw retention — previously empty/NaN, now a series with interpolated samples.