Skip to content

Commit 591dff0

Browse files
committed
perf(sqlite-native): avoid cloning cached read chunks
1 parent 6a36007 commit 591dff0

1 file changed

Lines changed: 28 additions & 23 deletions

File tree

  • rivetkit-typescript/packages/sqlite-native/src

rivetkit-typescript/packages/sqlite-native/src/vfs.rs

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -393,20 +393,18 @@ unsafe extern "C" fn kv_io_read(
393393
let end_chunk = (offset + requested_length - 1) / kv::CHUNK_SIZE;
394394

395395
let mut chunk_keys_to_fetch = Vec::new();
396-
let mut buffered_chunks: HashMap<usize, Vec<u8>> = HashMap::new();
396+
let mut buffered_chunks: HashMap<usize, &[u8]> = HashMap::new();
397+
// Skip fetching chunks already present in the dirty buffer (batch mode) or read cache.
397398
for chunk_idx in start_chunk..=end_chunk {
398-
// Check dirty buffer first (batch mode writes).
399399
if state.batch_mode {
400-
if let Some(buffered) = state.dirty_buffer.get(&(chunk_idx as u32)) {
401-
buffered_chunks.insert(chunk_idx, buffered.clone());
400+
if state.dirty_buffer.contains_key(&(chunk_idx as u32)) {
402401
continue;
403402
}
404403
}
405-
// Check read cache.
406404
let key = kv::get_chunk_key(file.file_tag, chunk_idx as u32);
407405
if let Some(read_cache) = state.read_cache.as_ref() {
408406
if let Some(cached) = read_cache.get(key.as_slice()) {
409-
buffered_chunks.insert(chunk_idx, cached.clone());
407+
buffered_chunks.insert(chunk_idx, cached.as_slice());
410408
continue;
411409
}
412410
}
@@ -420,29 +418,26 @@ unsafe extern "C" fn kv_io_read(
420418
}
421419
} else {
422420
match ctx.kv_get(chunk_keys_to_fetch) {
423-
Ok(resp) => {
424-
if let Some(read_cache) = state.read_cache.as_mut() {
425-
for (key, value) in resp.keys.iter().zip(resp.values.iter()) {
426-
if !value.is_empty() {
427-
read_cache.insert(key.clone(), value.clone());
428-
}
429-
}
430-
}
431-
resp
432-
}
421+
Ok(resp) => resp,
433422
Err(_) => return SQLITE_IOERR_READ,
434423
}
435424
};
436425
let value_map = build_value_map(&resp);
437426

438427
for chunk_idx in start_chunk..=end_chunk {
439-
let chunk_data: Option<&[u8]> = buffered_chunks
440-
.get(&chunk_idx)
441-
.map(|v| v.as_slice())
442-
.or_else(|| {
443-
let key = kv::get_chunk_key(file.file_tag, chunk_idx as u32);
444-
value_map.get(key.as_slice()).copied()
445-
});
428+
let chunk_data = if state.batch_mode {
429+
state
430+
.dirty_buffer
431+
.get(&(chunk_idx as u32))
432+
.map(|buffered| buffered.as_slice())
433+
} else {
434+
None
435+
}
436+
.or_else(|| buffered_chunks.get(&chunk_idx).copied())
437+
.or_else(|| {
438+
let chunk_key = kv::get_chunk_key(file.file_tag, chunk_idx as u32);
439+
value_map.get(chunk_key.as_slice()).copied()
440+
});
446441
let chunk_offset = chunk_idx * kv::CHUNK_SIZE;
447442
let read_start = offset.saturating_sub(chunk_offset);
448443
let read_end = std::cmp::min(kv::CHUNK_SIZE, offset + requested_length - chunk_offset);
@@ -465,6 +460,16 @@ unsafe extern "C" fn kv_io_read(
465460
}
466461
}
467462

463+
// `resp` is empty when every chunk was served from the dirty buffer or read cache.
464+
// In that case this loop is a no-op.
465+
if let Some(read_cache) = state.read_cache.as_mut() {
466+
for (key, value) in resp.keys.iter().zip(resp.values.iter()) {
467+
if !value.is_empty() {
468+
read_cache.insert(key.clone(), value.clone());
469+
}
470+
}
471+
}
472+
468473
let actual_bytes = std::cmp::min(requested_length, file_size - offset);
469474
if actual_bytes < requested_length {
470475
buf[actual_bytes..].fill(0);

0 commit comments

Comments
 (0)