Photo by Kier in Sight Archives on Unsplash
Compaction Windows: Managing I/O Interference in Production Databases
Most production databases running LSM-tree storage engines face a persistent tension: compaction is essential for long-term performance, but it consumes disk I/O and CPU cycles that compete with user queries. The solution isn’t to eliminate compaction—it’s to control when and how aggressively it runs.
Compaction windows represent a deliberate strategy for containing this interference, carving out specific time periods or resource budgets when background maintenance work can proceed without degrading user-facing latency.
Why Compaction Creates Contention
LSM-tree databases like RocksDB, LevelDB, and Cassandra accumulate sorted files as writes arrive. Over time, these files must be merged and rewritten to maintain read efficiency and reclaim space from deleted records. This process—compaction—involves reading multiple files, merging their contents, and writing new consolidated files back to disk.
The problem is straightforward: compaction competes for the same disk bandwidth and I/O operations per second (IOPS) that queries need. On cloud block storage or spinning disks where IOPS are constrained, heavy compaction can double or triple query latency. Even on NVMe SSDs, compaction generates memory pressure through page cache eviction and increases CPU load for compression and checksumming.
The interference isn’t uniform. Compaction impact depends on workload characteristics, storage hardware, and which levels of the LSM tree are being compacted. Lower-level compactions touch more data and run longer, creating sustained interference. Upper-level compactions are faster but more frequent.
Scheduling Strategies
The simplest approach is time-based windowing: restrict compaction to off-peak hours when query load is naturally low. Many e-commerce databases compact aggressively between 2 AM and 6 AM local time, then throttle back during business hours. This works well for workloads with predictable daily patterns but breaks down for global services with no true off-peak.
Rate-limiting offers finer control. Databases can throttle compaction by limiting bytes written per second or by introducing artificial delays between file operations. RocksDB exposes rate_limiter controls that cap background write bandwidth, ensuring compaction never consumes more than a configured fraction of disk throughput. The tradeoff is straightforward: lower limits reduce interference but allow more un-compacted data to accumulate, which eventually degrades read performance.
Adaptive strategies monitor live system metrics—query latency percentiles, disk utilization, replication lag—and dynamically adjust compaction aggressiveness. If P99 latency crosses a threshold, compaction slows down. If replication lag grows, indicating the database is keeping up with writes, compaction can accelerate. This requires careful tuning to avoid oscillation, where compaction repeatedly speeds up and slows down in response to its own interference.
The Debt Model
Some operators think of un-compacted data as technical debt. Skipping compaction during peak hours defers I/O cost but doesn’t eliminate it. The debt compounds: more overlapping files mean slower reads, more disk space consumption, and eventually, larger compactions that are harder to schedule.
Databases running near their write capacity can enter a failure mode where compaction never catches up. Write amplification grows as data gets rewritten through more levels, which increases the compaction workload, which requires more throttling, which allows more un-compacted data to accumulate. The system spirals toward write stalls, where the database refuses new writes until compaction makes progress.
Effective windowing strategies account for this. They establish minimum compaction rates even during peak hours, ensuring debt doesn’t accumulate faster than it can be repaid during off-peak windows. Some systems reserve a small IOPS budget exclusively for compaction, treating it as non-negotiable overhead rather than optional background work.
Configuring in Practice
Most LSM databases expose compaction scheduling through a combination of thread pool sizes, rate limits, and triggering thresholds. Reducing compaction thread count lowers parallelism and interference but extends compaction duration. Tightening rate limits has a similar effect with finer granularity.
Triggering thresholds—how many files or how much space triggers compaction—let operators defer compaction until absolutely necessary. Higher thresholds mean less frequent compaction but more interference when it does run, and worse read performance in the interim.
The right configuration depends on workload priorities. Latency-sensitive applications favor aggressive throttling and off-peak windows, accepting higher disk usage and occasional read slowdowns. Write-heavy systems that can tolerate latency variance often run compaction continuously at moderate rates, preventing debt accumulation.
Cloud environments add another dimension: burst IOPS credits and tiered storage performance. Compaction windows can exploit burst credits during scheduled maintenance periods, then throttle back to baseline IOPS for the rest of the day.
Compaction interference isn’t a problem to solve once—it’s an ongoing balancing act between write throughput, read performance, and resource utilization. Windowing strategies give operators the controls to manage that balance in production.