Skip to content

Commit 92c9981

Browse files
committed
Fix RocksDB: Blob garbage collection rewrites every stored blob on each compaction of the blobs column family, about once per 128MB of new mail
1 parent 7d2fbd7 commit 92c9981

3 files changed

Lines changed: 31 additions & 6 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ If you are upgrading from v0.16.x, replace the binary (or run `docker pull`). If
1212
## Changed
1313

1414
## Fixed
15+
- RocksDB: Blob garbage collection rewrites every stored blob on each compaction of the blobs column family, about once per 128MB of new mail.
1516

1617
## [0.16.25] - 2026-10-05
1718

‎crates/store/src/backend/rocksdb/main.rs‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,8 @@ impl RocksDbStore {
7272
let mut cf_opts = cf_options(CfProfile::Blob, &cache, write_buffer_size);
7373
cf_opts.set_enable_blob_files(true);
7474
cf_opts.set_min_blob_size(config.blob_size);
75-
cf_opts.set_enable_blob_gc(true);
75+
cf_opts.set_enable_blob_gc(false);
7676
cf_opts.set_blob_gc_age_cutoff(1.0);
77-
cf_opts.set_blob_gc_force_threshold(0.5);
7877
cfs.push(ColumnFamilyDescriptor::new(CF_BLOBS, cf_opts));
7978

8079
// Other cfs

‎crates/store/src/backend/rocksdb/write.rs‎

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
use super::{CF_INDEXES, CF_LOGS, CfHandle, RocksDbStore, into_error};
88
use crate::{
9-
Deserialize, IndexKey, Key, LogKey, SUBSPACE_COUNTER, SUBSPACE_IN_MEMORY_COUNTER,
10-
SUBSPACE_QUOTA,
9+
Deserialize, IndexKey, Key, LogKey, SUBSPACE_BLOBS, SUBSPACE_COUNTER,
10+
SUBSPACE_IN_MEMORY_COUNTER, SUBSPACE_QUOTA,
1111
backend::deserialize_i64_le,
1212
write::{
1313
AssignedIds, Batch, MAX_COMMIT_ATTEMPTS, MAX_COMMIT_TIME, MergeResult, Operation,
@@ -16,15 +16,18 @@ use crate::{
1616
};
1717
use rand::RngExt;
1818
use rocksdb::{
19-
BoundColumnFamily, ErrorKind, IteratorMode, OptimisticTransactionDB,
20-
OptimisticTransactionOptions, WriteOptions,
19+
BottommostLevelCompaction, BoundColumnFamily, CompactOptions, ErrorKind, IteratorMode,
20+
OptimisticTransactionDB, OptimisticTransactionOptions, WriteOptions,
2121
};
2222
use std::{
2323
sync::Arc,
2424
thread::sleep,
2525
time::{Duration, Instant},
2626
};
2727

28+
const BLOB_GC_MIN_GARBAGE: u64 = 256 * 1024 * 1024;
29+
const BLOB_GC_GARBAGE_RATIO: f64 = 0.25;
30+
2831
impl RocksDbStore {
2932
pub(crate) async fn write(&self, mut batch: Batch<'_>) -> trc::Result<AssignedIds> {
3033
let db = self.db.clone();
@@ -114,6 +117,28 @@ impl RocksDbStore {
114117
}
115118
}
116119

120+
let cf = db.subspace_handle(SUBSPACE_BLOBS);
121+
let total = db
122+
.property_int_value_cf(&cf, "rocksdb.live-blob-file-size")
123+
.map_err(into_error)?
124+
.unwrap_or_default();
125+
let garbage = db
126+
.property_int_value_cf(&cf, "rocksdb.live-blob-file-garbage-size")
127+
.map_err(into_error)?
128+
.unwrap_or_default();
129+
130+
if garbage >= BLOB_GC_MIN_GARBAGE
131+
&& garbage as f64 >= total as f64 * BLOB_GC_GARBAGE_RATIO
132+
{
133+
db.set_options_cf(&cf, &[("enable_blob_garbage_collection", "true")])
134+
.map_err(into_error)?;
135+
let mut opts = CompactOptions::default();
136+
opts.set_bottommost_level_compaction(BottommostLevelCompaction::Force);
137+
db.compact_range_cf_opt(&cf, None::<&[u8]>, None::<&[u8]>, &opts);
138+
db.set_options_cf(&cf, &[("enable_blob_garbage_collection", "false")])
139+
.map_err(into_error)?;
140+
}
141+
117142
Ok(())
118143
})
119144
.await

0 commit comments

Comments
 (0)