P2: Streaming read optimization (64KB chunk cache)
Some checks failed
Test / test (push) Has been cancelled
Test / build (push) Has been cancelled

- Add read_cache + read_cache_offset fields to VfsDavFile
- Read-ahead 64KB chunks to reduce VFS calls
- Serve from cache when data is available
- Invalidate cache on seek()
- Reduces memory allocations and VFS syscall overhead

Tests: 289 passed, 0 failed
This commit is contained in:
Warren
2026-06-21 19:16:12 +08:00
parent 9c82830959
commit ed55c6050e

View File

@@ -333,6 +333,8 @@ pub struct VfsDavFile {
is_write: bool,
versioning: Option<Arc<WebDavVersioning>>,
flushed: bool,
read_cache: Vec<u8>,
read_cache_offset: u64,
}
impl std::fmt::Debug for VfsDavFile {
@@ -359,6 +361,8 @@ impl VfsDavFile {
is_write: false,
versioning: None,
flushed: true,
read_cache: Vec::new(),
read_cache_offset: 0,
}
}
@@ -386,6 +390,8 @@ impl VfsDavFile {
is_write: true,
versioning,
flushed: false,
read_cache: Vec::new(),
read_cache_offset: 0,
}
}
}
@@ -455,21 +461,59 @@ impl DavFile for VfsDavFile {
}
fn read_bytes(&'_ mut self, count: usize) -> FsFuture<'_, Bytes> {
const CHUNK_SIZE: usize = 64 * 1024; // 64KB read-ahead
if let Some(vfs_file_mutex) = &self.vfs_file {
// Check if requested data is in cache
let cache_start = self.read_cache_offset as usize;
let cache_end = cache_start + self.read_cache.len();
let req_start = self.position as usize;
let req_end = req_start + count;
if req_start >= cache_start && req_end <= cache_end {
// Data in cache - return directly
let offset_in_cache = req_start - cache_start;
let bytes = Bytes::copy_from_slice(
&self.read_cache[offset_in_cache..offset_in_cache + count.min(self.read_cache.len() - offset_in_cache)]
);
self.position += bytes.len() as u64;
return Box::pin(std::future::ready(Ok(bytes)));
}
// Need to read new chunk
if let Ok(mut vfs_file) = vfs_file_mutex.lock() {
let mut buf = vec![0u8; count];
match vfs_file.read(&mut buf) {
Ok(0) => return Box::pin(std::future::ready(Ok(Bytes::new()))),
// Seek to current position if needed
let current_pos = vfs_file.seek(std::io::SeekFrom::Current(0)).unwrap_or(self.position);
if current_pos != self.position {
if vfs_file.seek(std::io::SeekFrom::Start(self.position)).is_err() {
return Box::pin(std::future::ready(Err(FsError::GeneralFailure)));
}
}
// Read larger chunk
let read_size = std::cmp::max(count, CHUNK_SIZE);
self.read_cache.resize(read_size, 0);
self.read_cache_offset = self.position;
match vfs_file.read(&mut self.read_cache) {
Ok(0) => {
self.read_cache.clear();
return Box::pin(std::future::ready(Ok(Bytes::new())));
}
Ok(n) => {
buf.truncate(n);
self.position += n as u64;
return Box::pin(std::future::ready(Ok(Bytes::from(buf))));
self.read_cache.truncate(n);
// Return requested portion
let result_len = count.min(n);
let bytes = Bytes::copy_from_slice(&self.read_cache[..result_len]);
self.position += result_len as u64;
return Box::pin(std::future::ready(Ok(bytes)));
}
Err(_) => {}
}
}
Box::pin(std::future::ready(Err(FsError::NotImplemented)))
} else {
// Write mode - read from self.data buffer
let start = self.position as usize;
let end = std::cmp::min(start + count, self.data.len());
@@ -489,6 +533,8 @@ impl DavFile for VfsDavFile {
match vfs_file.seek(pos) {
Ok(new_pos) => {
self.position = new_pos;
self.read_cache.clear(); // Invalidate cache on seek
self.read_cache_offset = 0;
return Box::pin(std::future::ready(Ok(new_pos)));
}
Err(_) => {}