perf(gateway): 流式处理代码层性能优化

- 将 spawn 内 buffered_body 从 Vec<u8> 改为 VecDeque<u8>, drain 前端从 O(n) 变 O(1)
- 消除 prefetched body/chunks 的不必要 clone, 改为直接 move
- Pending/Streaming 非终态候选状态写入改为 tokio::spawn fire-and-forget
- 连接池默认 max_connections 从 30 提升到 50
This commit is contained in:
fawney19
2026-04-15 11:55:45 +08:00
parent 026a77306c
commit 707d9ac274
2 changed files with 73 additions and 49 deletions

View File

@@ -112,21 +112,28 @@ pub(crate) async fn execute_execution_runtime_stream(
.usage_runtime .usage_runtime
.record_pending(state.data.as_ref(), &lifecycle_seed); .record_pending(state.data.as_ref(), &lifecycle_seed);
let candidate_started_unix_secs = current_request_candidate_unix_ms(); let candidate_started_unix_secs = current_request_candidate_unix_ms();
record_local_request_candidate_status( {
state, let state_bg = state.clone();
&plan, let plan_bg = plan.clone();
report_context.as_ref(), let report_context_bg = report_context.clone();
SchedulerRequestCandidateStatusUpdate { tokio::spawn(async move {
status: RequestCandidateStatus::Pending, record_local_request_candidate_status(
status_code: None, &state_bg,
error_type: None, &plan_bg,
error_message: None, report_context_bg.as_ref(),
latency_ms: None, SchedulerRequestCandidateStatusUpdate {
started_at_unix_ms: Some(candidate_started_unix_secs), status: RequestCandidateStatus::Pending,
finished_at_unix_ms: None, status_code: None,
}, error_type: None,
) error_message: None,
.await; latency_ms: None,
started_at_unix_ms: Some(candidate_started_unix_secs),
finished_at_unix_ms: None,
},
)
.await;
});
}
let plan_request_id_for_log = short_request_id(plan.request_id.as_str()); let plan_request_id_for_log = short_request_id(plan.request_id.as_str());
let provider_name = plan.provider_name.as_deref().unwrap_or("-"); let provider_name = plan.provider_name.as_deref().unwrap_or("-");
let endpoint_id = plan.endpoint_id.as_str(); let endpoint_id = plan.endpoint_id.as_str();
@@ -973,23 +980,31 @@ async fn execute_stream_from_frame_stream(
status_code, status_code,
prefetched_telemetry.as_ref(), prefetched_telemetry.as_ref(),
); );
record_local_request_candidate_status( {
state, let state_bg = state.clone();
&plan, let plan_bg = plan.clone();
report_context.as_ref(), let report_context_bg = report_context.clone();
SchedulerRequestCandidateStatusUpdate { let latency_ms = prefetched_telemetry
status: RequestCandidateStatus::Streaming, .as_ref()
status_code: Some(status_code), .and_then(|telemetry| telemetry.elapsed_ms);
error_type: None, tokio::spawn(async move {
error_message: None, record_local_request_candidate_status(
latency_ms: prefetched_telemetry &state_bg,
.as_ref() &plan_bg,
.and_then(|telemetry| telemetry.elapsed_ms), report_context_bg.as_ref(),
started_at_unix_ms: Some(candidate_started_unix_secs), SchedulerRequestCandidateStatusUpdate {
finished_at_unix_ms: None, status: RequestCandidateStatus::Streaming,
}, status_code: Some(status_code),
) error_type: None,
.await; error_message: None,
latency_ms,
started_at_unix_ms: Some(candidate_started_unix_secs),
finished_at_unix_ms: None,
},
)
.await;
});
}
let (tx, mut rx) = mpsc::channel::<Result<Bytes, IoError>>(16); let (tx, mut rx) = mpsc::channel::<Result<Bytes, IoError>>(16);
let state_for_report = state.clone(); let state_for_report = state.clone();
@@ -999,9 +1014,9 @@ async fn execute_stream_from_frame_stream(
let report_kind_owned = report_kind.clone(); let report_kind_owned = report_kind.clone();
let report_context_owned = report_context.clone(); let report_context_owned = report_context.clone();
let lifecycle_seed_for_report = lifecycle_seed.clone(); let lifecycle_seed_for_report = lifecycle_seed.clone();
let provider_prefetched_body_for_report = provider_prefetched_body.clone(); let provider_prefetched_body_for_report = provider_prefetched_body;
let prefetched_body_for_report = prefetched_body.clone(); let prefetched_body_for_report = prefetched_body;
let prefetched_chunks_for_body = prefetched_chunks.clone(); let prefetched_chunks_for_body = prefetched_chunks;
let initial_telemetry = prefetched_telemetry.clone(); let initial_telemetry = prefetched_telemetry.clone();
let initial_reached_eof = reached_eof; let initial_reached_eof = reached_eof;
let direct_stream_finalize_kind_owned = direct_stream_finalize_kind.clone(); let direct_stream_finalize_kind_owned = direct_stream_finalize_kind.clone();
@@ -1012,8 +1027,8 @@ async fn execute_stream_from_frame_stream(
tokio::spawn(async move { tokio::spawn(async move {
const MAX_STREAM_BODY_BUFFER_BYTES: usize = 256 * 1024; // 256KB const MAX_STREAM_BODY_BUFFER_BYTES: usize = 256 * 1024; // 256KB
let mut provider_buffered_body = provider_prefetched_body_for_report; let mut provider_buffered_body: VecDeque<u8> = provider_prefetched_body_for_report.into();
let mut buffered_body = prefetched_body_for_report; let mut buffered_body: VecDeque<u8> = prefetched_body_for_report.into();
let mut provider_body_truncated = false; let mut provider_body_truncated = false;
let mut client_body_truncated = false; let mut client_body_truncated = false;
let mut telemetry: Option<ExecutionTelemetry> = initial_telemetry.clone(); let mut telemetry: Option<ExecutionTelemetry> = initial_telemetry.clone();
@@ -1077,7 +1092,7 @@ async fn execute_stream_from_frame_stream(
continue; continue;
} }
provider_buffered_body.extend_from_slice(&chunk); provider_buffered_body.extend(chunk.iter().copied());
if provider_buffered_body.len() > MAX_STREAM_BODY_BUFFER_BYTES { if provider_buffered_body.len() > MAX_STREAM_BODY_BUFFER_BYTES {
let tail_start = let tail_start =
provider_buffered_body.len() - MAX_STREAM_BODY_BUFFER_BYTES; provider_buffered_body.len() - MAX_STREAM_BODY_BUFFER_BYTES;
@@ -1140,7 +1155,7 @@ async fn execute_stream_from_frame_stream(
continue; continue;
} }
buffered_body.extend_from_slice(&rewritten_chunk); buffered_body.extend(&rewritten_chunk);
if buffered_body.len() > MAX_STREAM_BODY_BUFFER_BYTES { if buffered_body.len() > MAX_STREAM_BODY_BUFFER_BYTES {
let tail_start = buffered_body.len() - MAX_STREAM_BODY_BUFFER_BYTES; let tail_start = buffered_body.len() - MAX_STREAM_BODY_BUFFER_BYTES;
buffered_body.drain(..tail_start); buffered_body.drain(..tail_start);
@@ -1239,7 +1254,7 @@ async fn execute_stream_from_frame_stream(
normalized_chunk normalized_chunk
}; };
if !rewritten_chunk.is_empty() { if !rewritten_chunk.is_empty() {
buffered_body.extend_from_slice(&rewritten_chunk); buffered_body.extend(&rewritten_chunk);
if tx.send(Ok(Bytes::from(rewritten_chunk))).await.is_err() { if tx.send(Ok(Bytes::from(rewritten_chunk))).await.is_err() {
warn!( warn!(
event_name = "stream_execution_downstream_flush_disconnected", event_name = "stream_execution_downstream_flush_disconnected",
@@ -1278,7 +1293,7 @@ async fn execute_stream_from_frame_stream(
if let Some(rewriter) = local_stream_rewriter.as_mut() { if let Some(rewriter) = local_stream_rewriter.as_mut() {
match rewriter.finish() { match rewriter.finish() {
Ok(flushed_chunk) if !flushed_chunk.is_empty() => { Ok(flushed_chunk) if !flushed_chunk.is_empty() => {
buffered_body.extend_from_slice(&flushed_chunk); buffered_body.extend(&flushed_chunk);
if tx.send(Ok(Bytes::from(flushed_chunk))).await.is_err() { if tx.send(Ok(Bytes::from(flushed_chunk))).await.is_err() {
warn!( warn!(
event_name = "stream_execution_downstream_rewrite_flush_disconnected", event_name = "stream_execution_downstream_rewrite_flush_disconnected",
@@ -1340,10 +1355,15 @@ async fn execute_stream_from_frame_stream(
provider_body_base64: (!provider_body_truncated provider_body_base64: (!provider_body_truncated
&& !provider_buffered_body.is_empty()) && !provider_buffered_body.is_empty())
.then(|| { .then(|| {
base64::engine::general_purpose::STANDARD.encode(&provider_buffered_body) base64::engine::general_purpose::STANDARD
.encode(provider_buffered_body.make_contiguous())
}), }),
client_body_base64: (!client_body_truncated && !buffered_body.is_empty()) client_body_base64: (!client_body_truncated && !buffered_body.is_empty()).then(
.then(|| base64::engine::general_purpose::STANDARD.encode(&buffered_body)), || {
base64::engine::general_purpose::STANDARD
.encode(buffered_body.make_contiguous())
},
),
telemetry: telemetry.clone(), telemetry: telemetry.clone(),
}, },
true, true,
@@ -1378,7 +1398,7 @@ async fn execute_stream_from_frame_stream(
if provider_body_truncated { if provider_body_truncated {
&[] &[]
} else { } else {
&provider_buffered_body provider_buffered_body.make_contiguous()
}, },
candidate_started_unix_secs_for_report, candidate_started_unix_secs_for_report,
failure, failure,
@@ -1394,9 +1414,13 @@ async fn execute_stream_from_frame_stream(
status_code, status_code,
headers: headers_for_report.clone(), headers: headers_for_report.clone(),
provider_body_base64: (!provider_body_truncated && !provider_buffered_body.is_empty()) provider_body_base64: (!provider_body_truncated && !provider_buffered_body.is_empty())
.then(|| base64::engine::general_purpose::STANDARD.encode(&provider_buffered_body)), .then(|| {
client_body_base64: (!client_body_truncated && !buffered_body.is_empty()) base64::engine::general_purpose::STANDARD
.then(|| base64::engine::general_purpose::STANDARD.encode(&buffered_body)), .encode(provider_buffered_body.make_contiguous())
}),
client_body_base64: (!client_body_truncated && !buffered_body.is_empty()).then(|| {
base64::engine::general_purpose::STANDARD.encode(buffered_body.make_contiguous())
}),
telemetry: telemetry.clone(), telemetry: telemetry.clone(),
}; };
record_stream_terminal_usage( record_stream_terminal_usage(

View File

@@ -21,7 +21,7 @@ impl Default for PostgresPoolConfig {
Self { Self {
database_url: String::new(), database_url: String::new(),
min_connections: 1, min_connections: 1,
max_connections: 30, max_connections: 50,
acquire_timeout_ms: 3_000, acquire_timeout_ms: 3_000,
idle_timeout_ms: 60_000, idle_timeout_ms: 60_000,
max_lifetime_ms: 30 * 60_000, max_lifetime_ms: 30 * 60_000,