perf(ecstore): improve erasure write diagnostics and single-block performance (#3280)

* docs(object-capacity): add localized crate docs

* fix(ecstore): improve quorum and transport diagnostics

* perf(ecstore): add safe single-block write fast path

* refactor(ecstore): collapse layered small write paths

* chore(docs): keep issue 662 design note tracked

* fix(docs): restore issue 662 design note

* chore(docs): keep issue 662 design local only

* feat(obs): add internode reliability metrics and dashboard

* feat(obs): extend internode diagnostics and service logging

* fix(docs): use AGENTS guide filename

* perf(ecstore): reuse owned buffer in small encode

* fix(ecstore): tighten small write diagnostics

---------

Co-authored-by: cxymds <[email protected]>
This commit is contained in:
houseme
2026-06-08 09:45:56 +00:00
committed by GitHub
co-authored by cxymds
parent ddd35badad
commit ed73952cb6
16 changed files with 2455 additions and 123 deletions
+44 -4
View File
@@ -280,18 +280,30 @@ async fn handle_put_file(req: Request<Incoming>) -> Response<Body> {
let mut file = if query.append {
match disk.append_file(&query.volume, &query.path).await {
Ok(file) => file,
Err(e) => return response_with_status(StatusCode::INTERNAL_SERVER_ERROR, format!("append file err {e}")),
Err(e) => {
let message = put_file_stage_error_message("append", &query, &e);
warn!("{message}");
return response_with_status(StatusCode::INTERNAL_SERVER_ERROR, message);
}
}
} else {
match disk.create_file("", &query.volume, &query.path, query.size).await {
Ok(file) => file,
Err(e) => return response_with_status(StatusCode::INTERNAL_SERVER_ERROR, format!("create file err {e}")),
Err(e) => {
let message = put_file_stage_error_message("create", &query, &e);
warn!("{message}");
return response_with_status(StatusCode::INTERNAL_SERVER_ERROR, message);
}
}
};
let copied = match write_body_chunks_to_writer(req.into_body().into_data_stream(), &mut file).await {
Ok(copied) => copied,
Err(e) => return response_with_status(StatusCode::INTERNAL_SERVER_ERROR, format!("write file err {e}")),
Err(e) => {
let message = put_file_stage_error_message("write_body", &query, &e);
warn!("{message}");
return response_with_status(StatusCode::INTERNAL_SERVER_ERROR, message);
}
};
global_internode_metrics().record_incoming_request_for_operation_and_backend(
@@ -305,7 +317,9 @@ async fn handle_put_file(req: Request<Incoming>) -> Response<Body> {
);
if let Err(e) = file.flush().await {
return response_with_status(StatusCode::INTERNAL_SERVER_ERROR, format!("write file err {e}"));
let message = put_file_stage_error_message("flush", &query, &e);
warn!("{message}");
return response_with_status(StatusCode::INTERNAL_SERVER_ERROR, message);
}
empty_ok()
@@ -364,6 +378,13 @@ fn response_with_status(status: StatusCode, message: impl Into<String>) -> Respo
.expect("failed to build rpc error response")
}
fn put_file_stage_error_message(stage: &str, query: &PutFileQuery, err: &dyn std::fmt::Display) -> String {
format!(
"{stage} file err {err} [disk={}, volume={}, path={}, append={}, size={}]",
query.disk, query.volume, query.path, query.append, query.size
)
}
#[cfg(test)]
mod tests {
use super::*;
@@ -404,6 +425,25 @@ mod tests {
assert_eq!(response.status(), StatusCode::FORBIDDEN);
}
#[test]
fn put_file_stage_error_message_includes_stage_and_request_context() {
let query = PutFileQuery {
disk: "disk-a".to_string(),
volume: ".rustfs.sys/tmp".to_string(),
path: "tmp/object/part.1".to_string(),
append: false,
size: 1024,
};
let msg = put_file_stage_error_message("write_body", &query, &"connection reset");
assert!(msg.contains("write_body"));
assert!(msg.contains("disk=disk-a"));
assert!(msg.contains("volume=.rustfs.sys/tmp"));
assert!(msg.contains("path=tmp/object/part.1"));
assert!(msg.contains("append=false"));
assert!(msg.contains("size=1024"));
}
#[tokio::test]
async fn write_body_chunks_to_writer_streams_all_chunks() {
let (mut reader, mut writer) = tokio::io::duplex(64);