Skip to content

Commit d102699

Browse files
committed
fix(db): avoid long flush stall on restart
enable_auto_compaction() was lowering level0_stop_writes_trigger from the bulk-load value (512) to the RocksDB default (36) on every update() call. At DB open the bulk-load triggers are applied by DB::open, so on any restart L0 can legitimately hold more than 36 files. When the first post-restart update() called enable_auto_compaction(), the trigger tightening instantly put the DB into pre-flush stall territory, and the end-of-batch db.flush() that follows parked inside WaitUntilFlushWouldNotStallWrites waiting for background compaction to bring L0 below 36. On production testnet this reliably cost 77 minutes of indexer freeze per restart (verified by 'Manual flush start' → 'Manual flush finished' in the RocksDB LOG). The actual memtable flush took 62 ms once unblocked; the rest was wait. Split enable_auto_compaction() into the minimal flag-flip and a new apply_steady_state_triggers() that holds the L0 trigger / pending-bytes- limit reset. Invoke the latter exactly once per DB lifetime, inside the F-sentinel gate in start_auto_compactions(), immediately after full_compaction() has drained L0. On DBs where F is already set (steady- state restart), triggers stay at bulk-load values — the comment in DB::open already argues that configuration is fine for steady-state reads given the prefix bloom filters.
1 parent ccdb399 commit d102699

2 files changed

Lines changed: 30 additions & 8 deletions

File tree

src/new_index/db.rs

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -205,22 +205,38 @@ impl DB {
205205
}
206206

207207
pub fn enable_auto_compaction(&self) {
208-
// Reset L0 triggers and pending-compaction stall thresholds to RocksDB
209-
// defaults, so that steady-state operation compacts promptly and avoids
210-
// unbounded compaction backlogs that cause read latency spikes.
211-
// RocksDB defaults (stable since v5.x through v10.4.2). Hardcoded because
212-
// set_options() doesn't return previous values and the Rust bindings lack getters.
208+
// Flip the enabled flag only. Trigger/stall-threshold tightening is handled
209+
// once per DB lifetime in apply_steady_state_triggers(), gated on a prior
210+
// full_compaction() draining L0 first. See the comment there for why the
211+
// two are split.
212+
let opts = [("disable_auto_compactions", "false")];
213+
self.db.set_options(&opts).unwrap();
214+
}
213215

216+
/// One-time transition from bulk-load compaction triggers (set at DB open in
217+
/// [`DB::open`]) to RocksDB defaults, for lower read amplification in
218+
/// steady-state operation.
219+
///
220+
/// Must be called only after a compaction has drained L0 and any level-size
221+
/// imbalance — otherwise the tightened `level0_stop_writes_trigger` parks
222+
/// foreground flushes and sync writes in `WaitUntilFlushWouldNotStallWrites`
223+
/// until background compaction catches up. On a mature DB with an hour-long
224+
/// bottommost compaction in flight, that wait can exceed 70 minutes.
225+
///
226+
/// Gated by the `F` sentinel key in `start_auto_compactions`, so it runs at
227+
/// most once per DB lifetime, immediately after `full_compaction()`.
228+
pub fn apply_steady_state_triggers(&self) {
229+
// RocksDB defaults (stable since v5.x through v10.4.2). Hardcoded because
230+
// set_options() doesn't return previous values and the Rust bindings lack getters.
214231
let soft_limit = (64u64 << 30).to_string(); // 64 GiB
215232
let hard_limit = (256u64 << 30).to_string(); // 256 GiB
216233

217234
let opts = [
218-
("disable_auto_compactions", "false"),
219235
("level0_file_num_compaction_trigger", "4"),
220236
("level0_slowdown_writes_trigger", "20"),
221237
("level0_stop_writes_trigger", "36"),
222-
("soft_pending_compaction_bytes_limit", &soft_limit),
223-
("hard_pending_compaction_bytes_limit", &hard_limit),
238+
("soft_pending_compaction_bytes_limit", soft_limit.as_str()),
239+
("hard_pending_compaction_bytes_limit", hard_limit.as_str()),
224240
];
225241
self.db.set_options(&opts).unwrap();
226242
}

src/new_index/schema.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,12 @@ impl Indexer {
286286
let key = b"F".to_vec();
287287
if db.get(&key).is_none() {
288288
db.full_compaction();
289+
// Tighten L0 triggers and pending-compaction stall thresholds now that
290+
// full_compaction() has drained L0. Running this before full_compaction
291+
// (or on every restart, as the code previously did) parks the end-of-batch
292+
// flush in WaitUntilFlushWouldNotStallWrites for as long as it takes
293+
// background compaction to bring L0 below the new stop_writes_trigger.
294+
db.apply_steady_state_triggers();
289295
db.put_sync(&key, b"");
290296
assert!(db.get(&key).is_some());
291297
}

0 commit comments

Comments
 (0)