mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-13 16:46:55 +00:00
refactor(ecstore): add mmap copy disk read name (#4061)
This commit is contained in:
@@ -1708,11 +1708,10 @@ impl DiskAPI for RemoteDisk {
|
||||
.await
|
||||
}
|
||||
|
||||
/// Zero-copy read for remote disks falls back to efficient network read.
|
||||
/// Note: True zero-copy is not possible over network, but we avoid extra copies
|
||||
/// by reading directly into Bytes.
|
||||
/// Buffered read for remote disks.
|
||||
/// The transport stream is collected into owned Bytes for caller sharing.
|
||||
#[tracing::instrument(level = "debug", skip(self))]
|
||||
async fn read_file_zero_copy(&self, volume: &str, path: &str, offset: usize, length: usize) -> Result<Bytes> {
|
||||
async fn read_file_mmap_copy(&self, volume: &str, path: &str, offset: usize, length: usize) -> Result<Bytes> {
|
||||
// For remote disks, use the regular reader and read into Bytes
|
||||
let reader = self.read_file_stream(volume, path, offset, length).await?;
|
||||
|
||||
|
||||
@@ -1361,9 +1361,9 @@ impl DiskAPI for LocalDiskWrapper {
|
||||
.await
|
||||
}
|
||||
|
||||
async fn read_file_zero_copy(&self, volume: &str, path: &str, offset: usize, length: usize) -> Result<bytes::Bytes> {
|
||||
async fn read_file_mmap_copy(&self, volume: &str, path: &str, offset: usize, length: usize) -> Result<bytes::Bytes> {
|
||||
self.track_disk_health(
|
||||
|| async { self.disk.read_file_zero_copy(volume, path, offset, length).await },
|
||||
|| async { self.disk.read_file_mmap_copy(volume, path, offset, length).await },
|
||||
get_max_timeout_duration(),
|
||||
)
|
||||
.await
|
||||
|
||||
@@ -2750,13 +2750,12 @@ impl DiskAPI for LocalDisk {
|
||||
Ok(Box::new(StallTimeoutReader::new(reader, get_object_disk_read_timeout())))
|
||||
}
|
||||
|
||||
/// Zero-copy file read using memory mapping (Unix) or efficient read (non-Unix).
|
||||
/// Returns Bytes that can be shared without copying.
|
||||
/// File read using mmap-then-copy on Unix or efficient read on non-Unix.
|
||||
// SAFETY: Unix unsafe calls in this function only query page size and mmap
|
||||
// a read-only file region after bounds and alignment are validated.
|
||||
#[allow(unsafe_code)]
|
||||
#[tracing::instrument(level = "debug", skip(self))]
|
||||
async fn read_file_zero_copy(&self, volume: &str, path: &str, offset: usize, length: usize) -> Result<Bytes> {
|
||||
async fn read_file_mmap_copy(&self, volume: &str, path: &str, offset: usize, length: usize) -> Result<Bytes> {
|
||||
let volume_dir = self.get_bucket_path(volume)?;
|
||||
if !skip_access_checks(volume) {
|
||||
access(&volume_dir)
|
||||
@@ -2784,7 +2783,7 @@ impl DiskAPI for LocalDisk {
|
||||
offset,
|
||||
length,
|
||||
actual_size = meta.len(),
|
||||
reason = "read_file_zero_copy_out_of_bounds",
|
||||
reason = "read_file_mmap_copy_out_of_bounds",
|
||||
"Disk local read fallback failed"
|
||||
);
|
||||
return Err(DiskError::FileCorrupt);
|
||||
@@ -4871,7 +4870,26 @@ mod test {
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_read_file_zero_copy_rejects_offset_length_overflow() {
|
||||
async fn test_read_file_mmap_copy_rejects_offset_length_overflow() {
|
||||
use tempfile::tempdir;
|
||||
|
||||
let dir = tempdir().expect("operation should succeed");
|
||||
let endpoint =
|
||||
Endpoint::try_from(dir.path().to_str().expect("operation should succeed")).expect("operation should succeed");
|
||||
let disk = LocalDisk::new(&endpoint, false).await.expect("operation should succeed");
|
||||
|
||||
disk.make_volume("test-volume").await.expect("operation should succeed");
|
||||
disk.write_all("test-volume", "test-file.txt", Bytes::from_static(b"test"))
|
||||
.await
|
||||
.expect("operation should succeed");
|
||||
|
||||
let result = disk.read_file_mmap_copy("test-volume", "test-file.txt", usize::MAX, 1).await;
|
||||
assert!(matches!(result, Err(DiskError::FileCorrupt)));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[allow(deprecated)]
|
||||
async fn test_read_file_zero_copy_legacy_alias_rejects_offset_length_overflow() {
|
||||
use tempfile::tempdir;
|
||||
|
||||
let dir = tempdir().expect("operation should succeed");
|
||||
|
||||
@@ -291,10 +291,10 @@ impl DiskAPI for Disk {
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(self))]
|
||||
async fn read_file_zero_copy(&self, volume: &str, path: &str, offset: usize, length: usize) -> Result<Bytes> {
|
||||
async fn read_file_mmap_copy(&self, volume: &str, path: &str, offset: usize, length: usize) -> Result<Bytes> {
|
||||
match self {
|
||||
Disk::Local(local_disk) => local_disk.read_file_zero_copy(volume, path, offset, length).await,
|
||||
Disk::Remote(remote_disk) => remote_disk.read_file_zero_copy(volume, path, offset, length).await,
|
||||
Disk::Local(local_disk) => local_disk.read_file_mmap_copy(volume, path, offset, length).await,
|
||||
Disk::Remote(remote_disk) => remote_disk.read_file_mmap_copy(volume, path, offset, length).await,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -558,11 +558,17 @@ pub trait DiskAPI: Debug + Send + Sync + 'static {
|
||||
async fn read_file(&self, volume: &str, path: &str) -> Result<FileReader>;
|
||||
async fn read_file_stream(&self, volume: &str, path: &str, offset: usize, length: usize) -> Result<FileReader>;
|
||||
|
||||
/// Zero-copy file read using memory mapping (Unix) or efficient read (non-Unix).
|
||||
/// Returns Bytes that can be shared without copying.
|
||||
/// On Unix, this uses mmap for true zero-copy access.
|
||||
/// On other platforms, falls back to efficient read operations.
|
||||
async fn read_file_zero_copy(&self, volume: &str, path: &str, offset: usize, length: usize) -> Result<Bytes>;
|
||||
/// File read using mmap-then-copy on Unix or an efficient read on non-Unix.
|
||||
async fn read_file_mmap_copy(&self, volume: &str, path: &str, offset: usize, length: usize) -> Result<Bytes>;
|
||||
|
||||
/// Historical name for `read_file_mmap_copy`.
|
||||
#[deprecated(
|
||||
since = "1.0.0-beta.8",
|
||||
note = "use read_file_mmap_copy; this path copies mmap data into owned Bytes"
|
||||
)]
|
||||
async fn read_file_zero_copy(&self, volume: &str, path: &str, offset: usize, length: usize) -> Result<Bytes> {
|
||||
self.read_file_mmap_copy(volume, path, offset, length).await
|
||||
}
|
||||
|
||||
async fn append_file(&self, volume: &str, path: &str) -> Result<FileWriter>;
|
||||
async fn create_file(&self, origvolume: &str, volume: &str, path: &str, file_size: i64) -> Result<FileWriter>;
|
||||
|
||||
@@ -149,7 +149,7 @@ async fn open_disk_reader(
|
||||
) -> disk::error::Result<FileReader> {
|
||||
if use_mmap_read && disk.is_local() {
|
||||
let start = Instant::now();
|
||||
match disk.read_file_zero_copy(bucket, path, offset, length).await {
|
||||
match disk.read_file_mmap_copy(bucket, path, offset, length).await {
|
||||
Ok(bytes) => {
|
||||
let duration_ms = start.elapsed().as_secs_f64() * 1000.0;
|
||||
|
||||
@@ -201,7 +201,7 @@ fn bitrot_encoded_range(offset: usize, length: usize, shard_size: usize, checksu
|
||||
/// * `shard_size` - Shard size for erasure coding
|
||||
/// * `checksum_algo` - Hash algorithm for bitrot verification
|
||||
/// * `skip_verify` - If true, skip checksum verification
|
||||
/// * `use_mmap_read` - If true, use zero-copy read (mmap on Unix)
|
||||
/// * `use_mmap_read` - If true, use mmap-copy read (mmap on Unix)
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub async fn create_bitrot_reader(
|
||||
inline_data: Option<&[u8]>,
|
||||
@@ -360,7 +360,7 @@ mod tests {
|
||||
let shard_size = 16;
|
||||
let checksum_algo = HashAlgorithm::HighwayHash256S;
|
||||
|
||||
// Test with zero-copy enabled (should work the same for inline data)
|
||||
// Test with mmap-copy enabled (should work the same for inline data)
|
||||
let result = create_bitrot_reader(
|
||||
Some(test_data),
|
||||
None,
|
||||
|
||||
Reference in New Issue
Block a user