perf(storage): converge Wave 2 hot-path optimizations (#6065)

* perf(get): share inline shards and lock clients

Co-Authored-By: heihutu <heihutu@gmail.com>

* perf(ecstore): converge PUT encoding on contiguous blocks

Co-Authored-By: heihutu <heihutu@gmail.com>

* perf(get): cache codec streaming gate config

Co-Authored-By: heihutu <heihutu@gmail.com>

* fix(sse): redact projected customer headers

Co-Authored-By: heihutu <heihutu@gmail.com>

* perf(ecstore): collapse GET metadata snapshots

Co-Authored-By: heihutu <heihutu@gmail.com>

* perf(ecstore): reuse decode stripe scratch

Co-Authored-By: heihutu <heihutu@gmail.com>

* refactor(ecstore): trim decode scratch adapters

Co-Authored-By: heihutu <heihutu@gmail.com>

* test(ecstore): adapt transition checks to metadata snapshots

Co-Authored-By: heihutu <heihutu@gmail.com>

* perf(get): release metadata snapshots at ownership boundary

Co-Authored-By: heihutu <heihutu@gmail.com>

* refactor(ecstore): close cumulative fast-path findings

Co-Authored-By: heihutu <heihutu@gmail.com>

* fix(storage): preserve lock and header invariants

Co-Authored-By: heihutu <heihutu@gmail.com>

* test(ecstore): adapt cumulative paths after rebase

Co-Authored-By: heihutu <heihutu@gmail.com>

* fix(rio-v2): adapt generated metadata fixture

Co-Authored-By: heihutu <heihutu@gmail.com>

---------

Co-authored-by: heihutu <heihutu@gmail.com>
This commit is contained in:
houseme
2026-08-13 16:34:28 +08:00
committed by GitHub
parent 36deab8670
commit d2b1003612
24 changed files with 1233 additions and 697 deletions
+15 -30
View File
@@ -134,6 +134,8 @@ use rustfs_s3_ops::{S3Operation, delete_event_name_for_marker, put_event_name_fo
use rustfs_s3select_api::object_store::bytes_stream;
use rustfs_targets::{EventName, get_request_host, get_request_port, get_request_user_agent};
use rustfs_utils::CompressionAlgorithm;
#[cfg(test)]
use rustfs_utils::http::headers::{SSEC_ALGORITHM_HEADER, SSEC_KEY_HEADER, SSEC_KEY_MD5_HEADER};
use rustfs_utils::http::{
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_REPLICA_STATUS, SUFFIX_REPLICA_TIMESTAMP,
@@ -147,9 +149,8 @@ use rustfs_utils::http::{
AMZ_RUSTFS_SNOWBALL_IGNORE_ERRORS, AMZ_RUSTFS_SNOWBALL_PREFIX, 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,
SSEC_ALGORITHM_HEADER, SSEC_KEY_HEADER, SSEC_KEY_MD5_HEADER,
},
insert_str, remove_str,
insert_str, project_ssec_transport_headers, remove_str,
};
use rustfs_utils::path::{encode_dir_object, is_dir_object, path_join_buf};
use rustfs_utils::retry::{DEFAULT_RETRY_CAP, DEFAULT_RETRY_UNIT, MAX_JITTER, RetryTimer};
@@ -2045,18 +2046,6 @@ struct GetObjectResumeContext {
identity: GetObjectResumeIdentity,
}
fn get_object_store_headers(request_headers: &HeaderMap) -> HeaderMap {
let mut headers = HeaderMap::new();
for name in [SSEC_ALGORITHM_HEADER, SSEC_KEY_HEADER, SSEC_KEY_MD5_HEADER] {
if let Some(value) = request_headers.get(name) {
let mut value = value.clone();
value.set_sensitive(true);
headers.insert(name, value);
}
}
headers
}
impl GetObjectResumeContext {
#[allow(clippy::too_many_arguments)]
fn new(
@@ -2076,7 +2065,7 @@ impl GetObjectResumeContext {
}
// Store spans record their header argument at debug level. Retain only
// the SSE-C inputs needed to reopen the reader and keep them redacted.
let ssec_headers = get_object_store_headers(request_headers);
let ssec_headers = project_ssec_transport_headers(request_headers);
Self {
store,
bucket: bucket.to_string(),
@@ -4469,7 +4458,7 @@ impl DefaultObjectUsecase {
) -> S3Result<GetObjectPreparedRead> {
let read_start = std::time::Instant::now();
let read_stage_start = rustfs_io_metrics::get_stage_metrics_enabled().then_some(read_start);
let store_headers = get_object_store_headers(&req.headers);
let store_headers = project_ssec_transport_headers(&req.headers);
let cache_adapter = self.object_data_cache();
if cache_adapter.is_disabled() || !cache_adapter.materialize_fill_enabled() {
let io_planning = Self::acquire_get_object_io_planning(
@@ -4484,7 +4473,7 @@ impl DefaultObjectUsecase {
.await?;
let reader = track_object_read_setup(
object_traffic_health.as_deref(),
store.get_object_reader(bucket, key, rs.clone(), store_headers.clone(), opts),
store.get_object_reader(bucket, key, rs.clone(), store_headers, opts),
)
.await
.map_err(map_get_object_reader_error)?;
@@ -4773,12 +4762,10 @@ impl DefaultObjectUsecase {
let io_planning = metadata_admission
.take()
.ok_or_else(|| s3_error!(InternalError, "prepared metadata admission is unavailable"))?;
let reader = track_object_read_setup(
object_traffic_health.as_deref(),
prepared.with_headers(store_headers.clone()).into_reader(),
)
.await
.map_err(map_get_object_reader_error)?;
let reader =
track_object_read_setup(object_traffic_health.as_deref(), prepared.with_headers(store_headers).into_reader())
.await
.map_err(map_get_object_reader_error)?;
(io_planning, reader)
} else {
let io_planning = Self::acquire_get_object_io_planning(
@@ -4798,12 +4785,9 @@ impl DefaultObjectUsecase {
)
.await
.map_err(map_get_object_reader_error)?;
track_object_read_setup(
object_traffic_health.as_deref(),
prepared.with_headers(store_headers.clone()).into_reader(),
)
.await
.map_err(map_get_object_reader_error)?
track_object_read_setup(object_traffic_health.as_deref(), prepared.with_headers(store_headers).into_reader())
.await
.map_err(map_get_object_reader_error)?
} else {
track_object_read_setup(
object_traffic_health.as_deref(),
@@ -13823,11 +13807,12 @@ mod tests {
request_headers.insert(SSEC_KEY_MD5_HEADER, HeaderValue::from_static("bWQ1"));
request_headers.insert(http::header::AUTHORIZATION, HeaderValue::from_static("AWS4-HMAC-SHA256 Credential=test"));
request_headers.insert("x-amz-security-token", HeaderValue::from_static("session-token"));
let store_headers = get_object_store_headers(&request_headers);
let store_headers = project_ssec_transport_headers(&request_headers);
assert_eq!(store_headers.len(), 3, "only store-consumed SSE-C headers are forwarded");
assert!(store_headers.values().all(HeaderValue::is_sensitive));
assert!(store_headers.get(http::header::AUTHORIZATION).is_none());
assert!(store_headers.get("x-amz-security-token").is_none());
assert!(!format!("{store_headers:?}").contains("dGVzdC1rZXk="));
let plain_info = ObjectInfo {
size: 11,
..Default::default()
@@ -103,6 +103,7 @@ fn load_file_info(case_dir: &Path, manifest: &ManifestRecord) -> FileInfo {
FileInfoOpts {
data: true,
include_free_versions: true,
include_part_checksums: false,
},
)
.unwrap_or_else(|err| panic!("decode {}: {err}", xl_meta_path.display()))
+21 -3
View File
@@ -1718,20 +1718,23 @@ pub(crate) fn build_ssec_read_headers(
let mut headers = HeaderMap::new();
if let Some(algorithm) = algorithm
&& let Ok(value) = HeaderValue::from_str(algorithm.as_str())
&& let Ok(mut value) = HeaderValue::from_str(algorithm.as_str())
{
value.set_sensitive(true);
headers.insert(AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_ALGORITHM, value);
}
if let Some(key) = key
&& let Ok(value) = HeaderValue::from_str(key.as_str())
&& let Ok(mut value) = HeaderValue::from_str(key.as_str())
{
value.set_sensitive(true);
headers.insert(AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY, value);
}
if let Some(key_md5) = key_md5
&& let Ok(value) = HeaderValue::from_str(key_md5.as_str())
&& let Ok(mut value) = HeaderValue::from_str(key_md5.as_str())
{
value.set_sensitive(true);
headers.insert(AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5, value);
}
@@ -3481,6 +3484,21 @@ mod tests {
};
use rustfs_utils::http::headers::SSEC_ALGORITHM_HEADER;
#[test]
fn ssec_read_headers_are_sensitive() {
let headers = super::build_ssec_read_headers(
Some(&SSECustomerAlgorithm::from("AES256".to_string())),
Some(&SSECustomerKey::from("dHJhbnNwb3J0LXNlY3JldA==".to_string())),
Some(&SSECustomerKeyMD5::from("bWQ1LXNlY3JldA==".to_string())),
);
assert_eq!(headers.len(), 3);
assert!(headers.values().all(HeaderValue::is_sensitive));
let debug = format!("{headers:?}");
assert!(!debug.contains("dHJhbnNwb3J0LXNlY3JldA=="));
assert!(!debug.contains("bWQ1LXNlY3JldA=="));
}
#[test]
fn anonymous_s3_request_builds_kms_principal() {
let mut request = s3s::S3Request {