docs/internals/adr/gaps/003-no-time-window-partitioning.md
Status: Open Discovered: 2026-02-19 Context: Codebase analysis during Phase 1 locality compaction design
The metrics ingestion pipeline does not partition splits by time window. When the ParquetIndexer accumulates rows over a commit interval (default 60 seconds), the resulting split may contain rows whose timestamps span multiple time windows. There is no mechanism to ensure that each split belongs to exactly one time window, and no window_start or window_duration_secs is recorded in split metadata.
Without time-window partitioning:
late_data_acceptance_window, arbitrarily late data can be ingested, potentially triggering expensive operations on old data.The codebase has a PartitionGranularity enum in quickwit-parquet-engine/src/split/partition.rs with Hour, Day, Week variants. This does not match the design requirement: the Phase 1 design calls for finer-grained, epoch-aligned windows (1-60 minutes, default 15 minutes) that evenly divide one hour. The existing partitioning is too coarse for compaction scoping and is not aligned to the compaction scope model.
No window assignment in MetricsSplitMetadata:
// quickwit-parquet-engine/src/split/metadata.rs
pub struct MetricsSplitMetadata {
pub split_id: SplitId,
pub index_id: String,
pub time_range: TimeRange, // Coarse time range, not window assignment
pub num_rows: u64,
pub size_bytes: u64,
pub metric_names: HashSet<String>,
pub low_cardinality_tags: HashMap<String, HashSet<String>>,
pub high_cardinality_tag_keys: HashSet<String>,
pub created_at: SystemTime,
pub parquet_files: Vec<String>,
// No window_start, no window_duration_secs, no sort_schema
}
No window partitioning in ParquetIndexer: The ParquetBatchAccumulator in quickwit-parquet-engine/src/ingest/accumulator.rs concatenates all pending batches into a single combined batch and writes one split. There is no grouping of rows by time window before writing.
No late data rejection at ingestion. There is no check that compares a data point's timestamp against a configurable maximum age. All data points are accepted regardless of timestamp.
Existing PartitionGranularity is too coarse:
// quickwit-parquet-engine/src/split/partition.rs
pub enum PartitionGranularity {
Hour, // 3600 seconds
Day, // 86400 seconds
Week, // 604800 seconds
}
The design requires granularities from 1 minute to 60 minutes, with the default of 15 minutes.
Time-based partitioning is universal in observability storage systems.
Option A (Proposed by ADR-003): Implement epoch-aligned time-window partitioning at ingestion. Before writing, group rows by window assignment, produce a separate Parquet file per window. Add window_start, window_duration_secs, and compaction_start_time to configuration and metadata. Implement late_data_acceptance_window to drop points older than a configurable threshold.
Option B: Extend the existing PartitionGranularity enum to support finer granularities (1m, 5m, 15m, etc.) and use it for window assignment. This reuses existing code but may require significant refactoring of the partition logic to match the epoch-aligned design.
Recommended: Option A, with the possibility of refactoring PartitionGranularity to support the required granularities if the existing code is close enough.
Metrics: Directly affected. No time-window partitioning exists for the Parquet pipeline.
Traces and logs: Not directly affected (Tantivy pipeline has time ranges on splits but no formal window partitioning). Phase 4 would benefit from formal time windowing for the same reasons.
quickwit-parquet-engine/src/ingest/accumulator.rs, quickwit-parquet-engine/src/split/metadata.rs, quickwit-indexing/src/actors/parquet_indexer.rs, index configurationwindow_start = t - (t % window_duration_seconds)ParquetBatchAccumulator (group rows by timestamp window before concatenation)window_duration configuration to index settings (default 15 minutes, valid: 1m-60m, must evenly divide 1 hour)compaction_start_time configuration (required for Phase 1 enablement)late_data_acceptance_window at ingestion (drop points older than threshold)window_start and window_duration_secs to MetricsSplitMetadatametrics_splits PostgreSQL tablequickwit-parquet-engine/src/split/partition.rs, quickwit-parquet-engine/src/ingest/accumulator.rs