feat : support for out-of-orderness tolerance - #2245
Open
sherali42 wants to merge 5 commits into
Open
Conversation
sherali42
force-pushed
the
out-of-orderness-rebased
branch
from
July 15, 2026 16:59
8af39d6 to
23ab46d
Compare
Adds a time-bucketed aggregation layer for time series partitions to accept samples within a configurable out-of-order tolerance window. Samples are aggregated in memory by bucket and persisted to chunks once the event-time watermark passes them. Core components: - AggregatingTimeSeriesPartition (extends TimeSeriesPartition) routes ingest through BucketAggregationState; query through AggregatingRangeVector - BucketAggregationState manages per-bucket aggregators using a TreeMap with object pooling to limit allocation pressure - Aggregator trait + Sum/Min/Max/Last/First/Count/HistogramSum/HistogramLast with type-specialized add paths (no boxing on Double/Long) - BinaryHistogram.addValuesTo enables in-place histogram accumulation without per-sample allocation - AggregatingRangeVector + MergingRangeVectorCursor merge finalized chunk rows with active in-memory bucket data, with a per-cursor scratch pool for histogram snapshots - Watermark-based Kafka offset hold in TimeSeriesShard.prepareFlushGroup prevents commit past samples still held in active buckets, so crash recovery via Kafka replay is safe - Schema-level config: aggregators (e.g. dSum(1), hSum(3)), aggregation- interval, aggregation-ooo-tolerance — single source of truth at the schema level rather than per-column The model is purely event-time. No wall-clock dependency in tolerance checks, finalization, or query visibility — see docs/specs/ooo-sample-ingestion.md for the full semantic. Co-Authored-By: Claude Opus 4.7 (1M context via Établi <noreply@anthropic.com>
OOOAggregationBenchmark with scalar and histogram-typed scenarios for ingest, query, and finalization throughput / allocation. Test coverage across BucketAggregationStateSpec, AggregatorSpec, AggregatingTimeSeries PartitionSpec, AggregatingRangeVectorSpec, EventTimeWatermarkSpec. Co-Authored-By: Claude Opus 4.7 (1M context via Établi <noreply@anthropic.com>
- docs/specs/ooo-sample-ingestion.md: canonical feature reference - doc/adr-aggregating-buffers.md: design rationale (ADR) - docs/plans/2026-04-30-agg-buffer-heap-optimization-tier1-tier2-design.md: heap optimization design (Tier 1 implemented, Tier 2 rejected after benchmarking) - doc/ingestion.md: section on OOO ingestion behavior - README.md: feature mention Co-Authored-By: Claude Opus 4.7 (1M context via Établi <noreply@anthropic.com>
sherali42
force-pushed
the
out-of-orderness-rebased
branch
from
July 20, 2026 00:43
b1a1e18 to
352606f
Compare
…interpolation Extends Histogram.quantile's evenDistribution mode to use the order-statistic percentile convention (rank = (N-1)*q + 1) instead of the Prometheus q*N rank. Within each bucket, samples are reconstructed as evenly spaced at k*width/(count+1), and the quantile interpolates between the two samples straddling the rank — which may live in different buckets, so interpolation can cross a bucket boundary. The top fractional sample snaps toward `max` (only when it shares a bucket with its lower neighbour or past the last observation). Scheme-aware bucket edges handle the GeometricBuckets minusOne (integer power-of-2) case where bucket 0's lower edge is 1, not 0. The default Prometheus linear/exponential path is unchanged. Adds a value-pinned test with a 64-bucket Base2 exponential histogram covering interior quantiles, cross-bucket-boundary interpolation (p25), tail max-snap, and an assertion that the even and default paths diverge at the tail. Applied via `git apply --3way` (clean, no conflicts). Only Histogram.scala and HistogramTest.scala changed. `sbt core/compile` succeeds; all 27 HistogramTest cases pass (1 pre-existing ignored). scalastyle method.length / cyclomatic.complexity are suppressed around quantile as the branch is now substantially longer. Co-Authored-By: Claude Opus 4.8 (1M context) via Établi <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) via Établi <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.
Pull Request checklist
Summary
Adds out-of-order (OOO) sample ingestion to FiloDB: time-bucketed,
per-partition aggregation that accepts samples whose timestamps fall behind
the latest seen timestamp by up to a configurable tolerance window. Standard
TimeSeriesPartitionrejects such samples outright; this change replaces thehard rejection with bounded in-memory aggregation, finalizing each bucket to a
single chunk row once an event-time watermark passes it.
Opt-in per schema. No on-disk format change. Cassandra, downsampling, query
planners, and persistence are unaffected. Full design in
docs/specs/ooo-sample-ingestion.md;ADR in
doc/adr-aggregating-buffers.md.Motivation
Standard
TimeSeriesPartition.ingestenforces strict in-order arrival —anything older than the latest chunk's
endTimeis dropped and counted onoutOfOrderDropped. In production this loses real data:the in-order watermark by seconds to minutes.
hole — it permanently distorts every downstream
rate()/delta()thatcrosses the gap, because nothing else re-sums the missing delta.
Design (one paragraph)
AggregatingTimeSeriesPartitionextendsTimeSeriesPartitionand isselected at partition-creation time when the schema declares aggregators.
Incoming samples are aggregated into fixed-width event-time buckets keyed by
ceilToBucket(sampleTs, intervalMs). A watermark — the latest sampletimestamp on the partition — advances forward only; buckets whose
timestamps fall more than
oooToleranceMsbehind the watermark arefinalized and handed to the existing
TimeSeriesPartition.ingestpipelineas a single row. Active (not-yet-finalized) buckets are queryable via
AggregatingRangeVector, which merges in-memory bucket rows with finalizedchunk rows. Kafka commit offsets are held back to the earliest active
bucket so crash recovery never loses samples that were aggregated but not
yet flushed.
The model is purely event-time. No wall-clock dependency in tolerance
checks, finalization, or query visibility — replay is deterministic.
What's in the change
Per-schema config (additive)
aggregatorsname(colId), e.g.["dSum(1)", "hSum(3)"]aggregation-interval1m)aggregation-ooo-tolerance2m)A non-empty
aggregatorslist is the only trigger. Schemas withoutaggregators are completely unaffected. Canonical example schema
aggregating-delta-histogram-v2is registered infilodb-defaults.conf.Core components
AggregatingTimeSeriesPartition— routes ingest throughBucketAggregationState; routes query throughAggregatingRangeVectorBucketAggregationState— TreeMap of per-bucket aggregators with objectpooling to limit allocation pressure
Aggregatortrait +Sum/Min/Max/Last/First/Count/HistogramSum/HistogramLastwith type-specialized add paths (no boxing on Double/Long)BinaryHistogram.addValuesTo— in-place histogram accumulation withoutper-sample allocation (the only new public API on existing types)
AggregatingRangeVector+MergingRangeVectorCursor— merge finalizedchunk rows with active in-memory bucket data, with a per-cursor scratch
pool for histogram snapshots
TimeSeriesShard.prepareFlushGroup—prevents commit past samples still held in active buckets
Out of scope (intentionally)
counted on
outOfOrderDropped)duplicates)
Tests
core/src/test/scala/filodb.core/memstore/aggregation/and
core/src/test/scala/filodb.core/{memstore,query}/:BucketAggregationStateSpec,AggregatorSpec,ColumnAggregatorSpec,AggregatingTimeSeriesPartitionSpec,AggregatingRangeVectorSpec,EventTimeWatermarkSpecColumnSpecandSchemasSpecPeriodicSamplesMapperSpecupdated forAggregatingRangeVectorquery pathOOOAggregationBenchmark(JMH) — scalar and histogram-typed scenarios foringest, query, and finalization throughput / allocation
AggregationHotPathBenchmark— micro for the hot ingest pathRollout / risk
aggregators. Existingschemas behave identically.
are re-ingested on replay; aggregation is deterministic and idempotent
under replay.
Test plan
aggregating-delta-histogram-v2schema andverify OOO samples (vs. previous
outOfOrderDroppedbehavior)(JMH locally; production canary in a low-traffic shard)
in-memory data merged with finalized chunks