Skip to content

Commit 860f522

Browse files
committed
Optimize RocksDB prefix searches
1 parent 976b05c commit 860f522

File tree

1 file changed

+49
-17
lines changed

1 file changed

+49
-17
lines changed

linera-views/src/backends/rocks_db.rs

Lines changed: 49 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use std::{
1414
};
1515

1616
use linera_base::ensure;
17-
use rocksdb::{BlockBasedOptions, Cache, DBCompactionStyle};
17+
use rocksdb::{BlockBasedOptions, Cache, DBCompactionStyle, SliceTransform};
1818
use serde::{Deserialize, Serialize};
1919
use sysinfo::{CpuRefreshKind, MemoryRefreshKind, RefreshKind, System};
2020
use tempfile::TempDir;
@@ -166,25 +166,37 @@ impl RocksDbStoreExecutor {
166166
Ok(entries.into_iter().collect::<Result<_, _>>()?)
167167
}
168168

169+
fn get_find_prefix_read_opts(&self, prefix: Vec<u8>) -> rocksdb::ReadOptions {
170+
// Configure ReadOptions optimized for SSDs and iterator performance
171+
let mut read_opts = rocksdb::ReadOptions::default();
172+
// Enable async I/O for better concurrency
173+
read_opts.set_async_io(true);
174+
175+
// Set precise upper bound to minimize key traversal
176+
let upper_bound = get_upper_bound_option(&prefix);
177+
if let Some(upper_bound) = upper_bound {
178+
read_opts.set_iterate_upper_bound(upper_bound);
179+
}
180+
read_opts.set_iterate_lower_bound(prefix);
181+
read_opts
182+
}
183+
169184
fn find_keys_by_prefix_internal(
170185
&self,
171186
key_prefix: Vec<u8>,
172187
) -> Result<Vec<Vec<u8>>, RocksDbStoreInternalError> {
173188
check_key_size(&key_prefix)?;
189+
174190
let mut prefix = self.start_key.clone();
175191
prefix.extend(key_prefix);
176192
let len = prefix.len();
177-
let mut iter = self.db.raw_iterator();
193+
194+
let read_opts = self.get_find_prefix_read_opts(prefix);
195+
let mut iter = self.db.raw_iterator_opt(read_opts);
178196
let mut keys = Vec::new();
179-
iter.seek(&prefix);
180-
let mut next_key = iter.key();
181-
while let Some(key) = next_key {
182-
if !key.starts_with(&prefix) {
183-
break;
184-
}
197+
while let Some(key) = iter.key() {
185198
keys.push(key[len..].to_vec());
186199
iter.next();
187-
next_key = iter.key();
188200
}
189201
Ok(keys)
190202
}
@@ -198,20 +210,16 @@ impl RocksDbStoreExecutor {
198210
let mut prefix = self.start_key.clone();
199211
prefix.extend(key_prefix);
200212
let len = prefix.len();
201-
let mut iter = self.db.raw_iterator();
213+
214+
let read_opts = self.get_find_prefix_read_opts(prefix);
215+
let mut iter = self.db.raw_iterator_opt(read_opts);
202216
let mut key_values = Vec::new();
203-
iter.seek(&prefix);
204-
let mut next_key = iter.key();
205-
while let Some(key) = next_key {
206-
if !key.starts_with(&prefix) {
207-
break;
208-
}
217+
while let Some(key) = iter.key() {
209218
if let Some(value) = iter.value() {
210219
let key_value = (key[len..].to_vec(), value.to_vec());
211220
key_values.push(key_value);
212221
}
213222
iter.next();
214-
next_key = iter.key();
215223
}
216224
Ok(key_values)
217225
}
@@ -373,8 +381,32 @@ impl RocksDbStoreInternal {
373381
total_ram / 4,
374382
HYPER_CLOCK_CACHE_BLOCK_SIZE,
375383
));
384+
385+
// Configure bloom filters for prefix iteration optimization
386+
block_options.set_bloom_filter(10.0, false);
387+
block_options.set_whole_key_filtering(false);
388+
389+
// 32KB blocks instead of default 4KB - reduces iterator seeks
390+
block_options.set_block_size(32 * 1024);
391+
// Use latest format for better compression and performance
392+
block_options.set_format_version(5);
393+
376394
options.set_block_based_table_factory(&block_options);
377395

396+
// Configure prefix extraction for bloom filter optimization
397+
// Use 8 bytes: ROOT_KEY_DOMAIN (1 byte) + BCS variant (1-2 bytes) + identifier start (4-5 bytes)
398+
let prefix_extractor = SliceTransform::create_fixed_prefix(8);
399+
options.set_prefix_extractor(prefix_extractor);
400+
401+
// 12.5% of memtable size for bloom filter
402+
options.set_memtable_prefix_bloom_ratio(0.125);
403+
// Skip bloom filter for memtable when key exists
404+
options.set_optimize_filters_for_hits(true);
405+
// Use memory-mapped files for faster reads
406+
options.set_allow_mmap_reads(true);
407+
// Don't use random access pattern since we do prefix scans
408+
options.set_advise_random_on_open(false);
409+
378410
let db = DB::open(&options, path_buf)?;
379411
let executor = RocksDbStoreExecutor {
380412
db: Arc::new(db),

0 commit comments

Comments
 (0)