fix(storage): harden rebalance decommission state (#3515)

This commit is contained in:
cxymds
2026-06-22 12:03:13 +08:00
committed by GitHub
parent 2dcc32db5a
commit 2f25cf606e
43 changed files with 10151 additions and 835 deletions
+113
View File
@@ -314,6 +314,24 @@ impl ReadPlan {
rs = http_range_spec_from_object_info(oi, part_number);
}
if opts.raw_data_movement_read {
let (visible_offset, visible_length) = if let Some(rs) = rs {
rs.get_offset_length(oi.size)?
} else {
(0, oi.size)
};
return Ok(Self {
storage_offset: visible_offset,
storage_length: visible_length,
object_size: oi.size,
transform: ReadTransform::Plain {
visible_offset,
visible_length,
},
});
}
let mut is_encrypted = oi.is_encrypted();
let (algo, compression_backend, mut is_compressed) = oi.compression_read_plan()?;
@@ -2162,6 +2180,101 @@ mod tests {
));
}
#[tokio::test]
async fn test_raw_data_movement_read_plan_bypasses_compression_transform() {
let object_info = ObjectInfo {
size: 3_000_000,
user_defined: Arc::new(HashMap::from([
("x-minio-internal-compression".to_string(), "klauspost/compress/s2".to_string()),
("x-minio-internal-actual-size".to_string(), "4194304".to_string()),
])),
..Default::default()
};
let opts = ObjectOptions {
raw_data_movement_read: true,
..Default::default()
};
let plan = ReadPlan::build(None, &object_info, &opts, &HeaderMap::new())
.await
.expect("raw data movement read should bypass compression planning");
assert_eq!(plan.storage_offset, 0);
assert_eq!(plan.storage_length, object_info.size);
assert_eq!(plan.object_size, object_info.size);
assert!(matches!(
plan.transform,
ReadTransform::Plain {
visible_offset: 0,
visible_length: 3_000_000
}
));
}
#[tokio::test]
async fn test_raw_data_movement_read_plan_bypasses_encryption_transform() {
let object_info = ObjectInfo {
size: 128,
user_defined: Arc::new(HashMap::from([
("X-Amz-Server-Side-Encryption".to_string(), "aws:kms".to_string()),
("X-Amz-Server-Side-Encryption-Iv".to_string(), "AAAAAAAAAAAAAAAA".to_string()),
("X-Amz-Server-Side-Encryption-Key".to_string(), BASE64_STANDARD.encode([7_u8; 32])),
("x-rustfs-encryption-original-size".to_string(), "64".to_string()),
])),
..Default::default()
};
let opts = ObjectOptions {
raw_data_movement_read: true,
..Default::default()
};
let plan = ReadPlan::build(None, &object_info, &opts, &HeaderMap::new())
.await
.expect("raw data movement read should not require decryption material");
assert_eq!(plan.storage_offset, 0);
assert_eq!(plan.storage_length, object_info.size);
assert_eq!(plan.object_size, object_info.size);
assert!(matches!(
plan.transform,
ReadTransform::Plain {
visible_offset: 0,
visible_length: 128
}
));
}
#[tokio::test]
async fn test_raw_data_movement_read_plan_bypasses_ssec_header_resolution() {
let object_info = ObjectInfo {
size: 256,
user_defined: Arc::new(HashMap::from([
(SSEC_ALGORITHM_HEADER.to_string(), "AES256".to_string()),
(SSEC_KEY_MD5_HEADER.to_string(), "stored-key-md5".to_string()),
])),
..Default::default()
};
let opts = ObjectOptions {
raw_data_movement_read: true,
..Default::default()
};
let plan = ReadPlan::build(None, &object_info, &opts, &HeaderMap::new())
.await
.expect("raw data movement read should not require SSE-C request headers");
assert_eq!(plan.storage_offset, 0);
assert_eq!(plan.storage_length, object_info.size);
assert_eq!(plan.object_size, object_info.size);
assert!(matches!(
plan.transform,
ReadTransform::Plain {
visible_offset: 0,
visible_length: 256
}
));
}
#[tokio::test]
async fn test_get_object_reader_allows_encrypted_full_object_passthrough() {
async_with_vars([("__RUSTFS_SSE_SIMPLE_CMK", Some(BASE64_STANDARD.encode([0u8; 32])))], async {
+1
View File
@@ -25,6 +25,7 @@ pub struct ObjectOptions {
pub skip_free_version: bool,
pub data_movement: bool,
pub raw_data_movement_read: bool,
pub src_pool_idx: usize,
pub user_defined: HashMap<String, String>,
pub preserve_etag: Option<String>,