Skip to content

Commit 8bc00db

Browse files
authored
Merge pull request #24 from matrixarkai/small-storage-batch-coalesce
Avoid hash coalescing for small storage batches
2 parents 2138795 + 2a6d8bd commit 8bc00db

1 file changed

Lines changed: 125 additions & 15 deletions

File tree

src/runtime/storage_engines.rs

Lines changed: 125 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,40 @@ pub struct CacheBuffer {
543543
handle: Option<CachePinnedHandle>,
544544
}
545545

546+
const STORAGE_BATCH_LINEAR_SCAN_MAX_KEYS: usize = 64;
547+
548+
fn coalesce_storage_batch_keys(keys: &[String]) -> (Vec<String>, Vec<usize>) {
549+
if keys.len() <= STORAGE_BATCH_LINEAR_SCAN_MAX_KEYS {
550+
let mut unique_keys = Vec::<String>::with_capacity(keys.len());
551+
let mut output_positions = Vec::with_capacity(keys.len());
552+
for key in keys {
553+
if let Some(position) = unique_keys.iter().position(|existing| existing == key) {
554+
output_positions.push(position);
555+
} else {
556+
output_positions.push(unique_keys.len());
557+
unique_keys.push(key.clone());
558+
}
559+
}
560+
return (unique_keys, output_positions);
561+
}
562+
563+
let mut unique_keys = Vec::<String>::with_capacity(keys.len());
564+
let mut unique_positions = HashMap::<String, usize>::with_capacity(keys.len());
565+
let mut output_positions = Vec::with_capacity(keys.len());
566+
for key in keys {
567+
let position = if let Some(position) = unique_positions.get(key).copied() {
568+
position
569+
} else {
570+
let position = unique_keys.len();
571+
unique_positions.insert(key.clone(), position);
572+
unique_keys.push(key.clone());
573+
position
574+
};
575+
output_positions.push(position);
576+
}
577+
(unique_keys, output_positions)
578+
}
579+
546580
impl CacheBuffer {
547581
pub fn new(value: impl Into<Vec<u8>>) -> Self {
548582
Self {
@@ -2352,20 +2386,7 @@ impl StorageEngineRocksDb {
23522386
if keys.is_empty() {
23532387
return Ok(Vec::new());
23542388
}
2355-
let mut unique_keys = Vec::<String>::new();
2356-
let mut unique_positions = HashMap::<String, usize>::new();
2357-
let mut output_positions = Vec::with_capacity(keys.len());
2358-
for key in keys {
2359-
let position = if let Some(position) = unique_positions.get(key).copied() {
2360-
position
2361-
} else {
2362-
let position = unique_keys.len();
2363-
unique_positions.insert(key.clone(), position);
2364-
unique_keys.push(key.clone());
2365-
position
2366-
};
2367-
output_positions.push(position);
2368-
}
2389+
let (unique_keys, output_positions) = coalesce_storage_batch_keys(keys);
23692390
#[cfg(feature = "rocksdb-ssd")]
23702391
let unique_values = {
23712392
self.rocksdb()?
@@ -2997,6 +3018,96 @@ impl StorageEngineMultiSsd {
29973018
self.ssdcache_type
29983019
}
29993020

3021+
pub fn get_batch(&self, keys: &[String]) -> Result<Vec<Option<Vec<u8>>>, CacheError> {
3022+
if !self.initialized || self.storages.is_empty() {
3023+
return Err(CacheError::Stopped);
3024+
}
3025+
if keys.is_empty() {
3026+
return Ok(Vec::new());
3027+
}
3028+
3029+
let (unique_keys, output_positions) = coalesce_storage_batch_keys(keys);
3030+
3031+
let mut routed = vec![Vec::<(usize, String)>::new(); self.storages.len()];
3032+
for (unique_index, key) in unique_keys.iter().enumerate() {
3033+
let storage_index = Self::hash(key) as usize % self.storages.len();
3034+
routed[storage_index].push((unique_index, key.clone()));
3035+
}
3036+
3037+
let mut unique_values = vec![None; unique_keys.len()];
3038+
for (storage_index, entries) in routed.into_iter().enumerate() {
3039+
if entries.is_empty() {
3040+
continue;
3041+
}
3042+
let storage_keys = entries
3043+
.iter()
3044+
.map(|(_, key)| key.clone())
3045+
.collect::<Vec<_>>();
3046+
let storage_values = self.storages[storage_index].get_batch(&storage_keys)?;
3047+
for ((unique_index, _), value) in entries.into_iter().zip(storage_values) {
3048+
unique_values[unique_index] = value;
3049+
}
3050+
}
3051+
3052+
Ok(output_positions
3053+
.into_iter()
3054+
.map(|position| unique_values[position].clone())
3055+
.collect())
3056+
}
3057+
3058+
pub fn put_batch(&mut self, entries: Vec<(String, Vec<u8>)>) -> Result<usize, CacheError> {
3059+
if !self.initialized || self.storages.is_empty() {
3060+
return Err(CacheError::Stopped);
3061+
}
3062+
if entries.is_empty() {
3063+
return Ok(0);
3064+
}
3065+
3066+
let count = entries.len();
3067+
let mut routed = vec![Vec::<(String, Vec<u8>)>::new(); self.storages.len()];
3068+
for (key, value) in entries {
3069+
let storage_index = Self::hash(&key) as usize % self.storages.len();
3070+
routed[storage_index].push((key, value));
3071+
}
3072+
3073+
for (storage_index, entries) in routed.into_iter().enumerate() {
3074+
if entries.is_empty() {
3075+
continue;
3076+
}
3077+
self.storages[storage_index].put_batch(entries)?;
3078+
}
3079+
3080+
Ok(count)
3081+
}
3082+
3083+
pub fn delete_batch(&mut self, keys: &[String]) -> Result<usize, CacheError> {
3084+
if !self.initialized || self.storages.is_empty() {
3085+
return Err(CacheError::Stopped);
3086+
}
3087+
if keys.is_empty() {
3088+
return Ok(0);
3089+
}
3090+
3091+
let mut routed = vec![Vec::<String>::new(); self.storages.len()];
3092+
let mut seen = HashSet::<String>::new();
3093+
for key in keys {
3094+
if !seen.insert(key.clone()) {
3095+
continue;
3096+
}
3097+
let storage_index = Self::hash(key) as usize % self.storages.len();
3098+
routed[storage_index].push(key.clone());
3099+
}
3100+
3101+
let mut deleted = 0usize;
3102+
for (storage_index, keys) in routed.into_iter().enumerate() {
3103+
if keys.is_empty() {
3104+
continue;
3105+
}
3106+
deleted = deleted.saturating_add(self.storages[storage_index].delete_batch(&keys)?);
3107+
}
3108+
Ok(deleted)
3109+
}
3110+
30003111
#[cfg(feature = "rocksdb-ssd")]
30013112
pub fn recover_view_data<C>(&mut self, callback: &mut C) -> Result<(), CacheError>
30023113
where
@@ -3701,4 +3812,3 @@ impl StringViewBuffer {
37013812
self.size()
37023813
}
37033814
}
3704-

0 commit comments

Comments
 (0)