Fresh ingest produces lots of small, loosely-packed segments. Compaction merges them into fewer, denser ones — but has to do it without blocking reads.

Data lands from ingestion in small, frequently-flushed segments — good for low write latency, bad for long-term query efficiency, since scanning 200 small segments costs more open/seek overhead than scanning 10 well-packed ones covering the same data. Compaction exists to close that gap, continuously, in the background.

The compaction tiers

Helix runs three compaction tiers: L0 merges raw 15-minute segments into 2-hour segments within the first few hours after ingest; L1 merges those into 1-day segments after 24 hours; L2 merges a week of L1 segments into a single monthly segment once data has aged past the point where late arrivals are expected. Each tier roughly halves segment count while improving average chunk density.

Staying out of the query path

Compaction reads source segments and writes a new merged segment without touching the originals until the merge is verified complete, then atomically swaps a segment-manifest pointer. Queries in flight against the old segments finish against them; new queries pick up the compacted version — there's no lock, no stall, just a brief window where both versions exist on disk.

compaction:
  tiers:
    - name: l0_recent
      source_window: 15m
      target_window: 2h
      trigger_after: 3h
    - name: l1_daily
      source_window: 2h
      target_window: 24h
      trigger_after: 24h
  max_concurrent_jobs: 4
  io_priority: below_normal   # never contends with query reads

The cost of falling behind

If compaction falls behind — usually from a sudden ingest spike or a compaction-worker outage — queries against the affected window degrade gracefully but measurably: we've seen a query touching an under-compacted 24-hour window take 3-4x longer than the same query against a fully compacted equivalent, purely from segment-open overhead. This is why `io_priority: below_normal` still gets a floor: compaction can be deprioritized during load spikes but never starved indefinitely.

What we measured in steady state

Across our fleet, L0 compaction typically completes within 15 minutes of its trigger, keeping the "hot" window (data under 3 hours old) at roughly 4-6 segments per shard instead of the 12+ it would otherwise accumulate. That density difference is worth about a 2.3x improvement in query latency for dashboards refreshing on recent data, which is most of them.

  • Three compaction tiers progressively merge 15-min segments into 2-hour, then daily, then monthly segments.
  • Compaction never locks readers — it swaps a manifest pointer atomically after a verified merge.
  • Falling behind on compaction costs 3-4x query latency on affected windows from segment-open overhead alone.