test(object): cover zero-copy selection heuristics (#2338)

Co-authored-by: houseme <housemecn@gmail.com>
Co-authored-by: cxymds <Cxymds@qq.com>
This commit is contained in:
安正超
2026-03-31 22:07:58 +08:00
committed by GitHub
parent d960acb170
commit d3dee898ee
+72 -10
View File
@@ -95,15 +95,16 @@ use rustfs_s3select_api::{
use rustfs_s3select_query::get_global_db;
use rustfs_targets::EventName;
use rustfs_utils::http::{
AMZ_BUCKET_REPLICATION_STATUS, AMZ_CHECKSUM_MODE, AMZ_CHECKSUM_TYPE, AMZ_WEBSITE_REDIRECT_LOCATION, SUFFIX_ACTUAL_SIZE,
SUFFIX_COMPRESSION, SUFFIX_COMPRESSION_SIZE, SUFFIX_REPLICATION_STATUS, SUFFIX_REPLICATION_TIMESTAMP,
AMZ_BUCKET_REPLICATION_STATUS, AMZ_CHECKSUM_MODE, AMZ_CHECKSUM_TYPE, AMZ_WEBSITE_REDIRECT_LOCATION, CONTENT_TYPE,
SUFFIX_ACTUAL_SIZE, SUFFIX_COMPRESSION, SUFFIX_COMPRESSION_SIZE, SUFFIX_REPLICATION_STATUS, SUFFIX_REPLICATION_TIMESTAMP,
headers::{
AMZ_DECODED_CONTENT_LENGTH, AMZ_MINIO_SNOWBALL_IGNORE_DIRS, AMZ_MINIO_SNOWBALL_IGNORE_ERRORS, AMZ_MINIO_SNOWBALL_PREFIX,
AMZ_OBJECT_LOCK_LEGAL_HOLD, AMZ_OBJECT_LOCK_LEGAL_HOLD_LOWER, AMZ_OBJECT_LOCK_MODE, AMZ_OBJECT_LOCK_MODE_LOWER,
AMZ_OBJECT_LOCK_RETAIN_UNTIL_DATE, AMZ_OBJECT_LOCK_RETAIN_UNTIL_DATE_LOWER, AMZ_OBJECT_TAGGING, AMZ_RESTORE_EXPIRY_DAYS,
AMZ_RESTORE_REQUEST_DATE, AMZ_RUSTFS_SNOWBALL_IGNORE_DIRS, AMZ_RUSTFS_SNOWBALL_IGNORE_ERRORS, AMZ_RUSTFS_SNOWBALL_PREFIX,
AMZ_SERVER_SIDE_ENCRYPTION, AMZ_SERVER_SIDE_ENCRYPTION_KMS_ID, AMZ_SNOWBALL_EXTRACT, AMZ_SNOWBALL_IGNORE_DIRS,
AMZ_SNOWBALL_IGNORE_ERRORS, AMZ_SNOWBALL_PREFIX, AMZ_STORAGE_CLASS, AMZ_TAG_COUNT,
AMZ_SERVER_SIDE_ENCRYPTION, AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM, AMZ_SERVER_SIDE_ENCRYPTION_KMS_ID,
AMZ_SNOWBALL_EXTRACT, AMZ_SNOWBALL_IGNORE_DIRS, AMZ_SNOWBALL_IGNORE_ERRORS, AMZ_SNOWBALL_PREFIX, AMZ_STORAGE_CLASS,
AMZ_TAG_COUNT,
},
insert_str, remove_str,
};
@@ -314,24 +315,24 @@ impl<R: AsyncRead> AsyncRead for ExtractArchiveEtagReader<R> {
///
/// `true` if zero-copy should be used, `false` otherwise
fn should_use_zero_copy(size: i64, headers: &HeaderMap) -> bool {
// Only use zero-copy for large objects (> 1MB)
// Only use zero-copy for objects larger than 1MB
const ZERO_COPY_MIN_SIZE: i64 = 1024 * 1024;
if size < ZERO_COPY_MIN_SIZE {
if size <= ZERO_COPY_MIN_SIZE {
return false;
}
// Don't use zero-copy if encryption is requested
if headers.get("x-amz-server-side-encryption").is_some()
|| headers.get("x-amz-server-side-encryption-customer-algorithm").is_some()
|| headers.get("x-amz-server-side-encryption-aws-kms-key-id").is_some()
if headers.get(AMZ_SERVER_SIDE_ENCRYPTION).is_some()
|| headers.get(AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM).is_some()
|| headers.get(AMZ_SERVER_SIDE_ENCRYPTION_KMS_ID).is_some()
{
return false;
}
// Don't use zero-copy if compression is likely (compressible content types)
// The compression check happens later in the flow
if let Some(content_type) = headers.get("content-type")
if let Some(content_type) = headers.get(CONTENT_TYPE)
&& let Ok(ct) = content_type.to_str()
{
// Skip zero-copy for easily compressible content types
@@ -5166,6 +5167,67 @@ mod tests {
assert_eq!(normalize_extract_entry_key("top-level", None, false), "top-level");
}
#[test]
fn should_use_zero_copy_rejects_boundary_at_1mb() {
let headers = HeaderMap::new();
assert!(!should_use_zero_copy(1024 * 1024, &headers));
}
#[test]
fn should_use_zero_copy_rejects_small_objects() {
let headers = HeaderMap::new();
assert!(!should_use_zero_copy(1024 * 1024 - 1, &headers));
}
#[test]
fn should_use_zero_copy_rejects_one_megabyte() {
let headers = HeaderMap::new();
assert!(!should_use_zero_copy(1024 * 1024, &headers));
}
#[test]
fn should_use_zero_copy_rejects_encrypted_requests() {
let mut headers = HeaderMap::new();
headers.insert(AMZ_SERVER_SIDE_ENCRYPTION, HeaderValue::from_static("AES256"));
assert!(!should_use_zero_copy(2 * 1024 * 1024, &headers));
}
#[test]
fn should_use_zero_copy_rejects_encrypted_requests_with_sse_customer_algorithm() {
let mut headers = HeaderMap::new();
headers.insert(AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM, HeaderValue::from_static("AES256"));
assert!(!should_use_zero_copy(2 * 1024 * 1024, &headers));
}
#[test]
fn should_use_zero_copy_rejects_encrypted_requests_with_kms_key_id() {
let mut headers = HeaderMap::new();
headers.insert(AMZ_SERVER_SIDE_ENCRYPTION_KMS_ID, HeaderValue::from_static("test-kms-key-id"));
assert!(!should_use_zero_copy(2 * 1024 * 1024, &headers));
}
#[test]
fn should_use_zero_copy_rejects_compressible_content_types() {
let mut headers = HeaderMap::new();
headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json; charset=utf-8"));
assert!(!should_use_zero_copy(2 * 1024 * 1024, &headers));
}
#[test]
fn should_use_zero_copy_allows_large_unencrypted_binary_objects() {
let mut headers = HeaderMap::new();
headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/octet-stream"));
assert!(should_use_zero_copy(2 * 1024 * 1024, &headers));
}
#[test]
fn resolve_put_object_extract_options_defaults_when_headers_missing() {
let headers = HeaderMap::new();