mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-11 03:39:49 +08:00
refactor(ws): 拆分 LogicalTurn 与 ProviderAttempt,结算改表驱动
评审第 5 条:一个 ResponsesWebSocketTurn 同时代表 logical turn 和 provider attempt,finalize() 又用 outcome.cancelled() 一个布尔驱动 billing、candidate 状态和供应商效果,于是 provider 终态已经到达、只是最后一跳写客户端失败时, 供应商事实会被 Cancelled 覆盖掉。 - ResponsesWebSocketTurn → ResponsesProviderAttempt, ActiveResponsesWebSocketTurn → ActiveProviderAttempt:类型名字明确它只代表 一次上游执行,logical turn 由 C1 落地的 LogicalTurn 承担。 - 新增 settlement.rs:AttemptProviderOutcome × AttemptClientDelivery 两个正交 事实,classify_attempt_settlement 一张表推出 status_code / billing / candidate 状态 / candidate 错误分类 / 供应商效果 / 是否提交 execution report。 - attempt 观察到 provider 终态即记录 provider_outcome。结算信号 ResponsesWebSocketTurnOutcome 只回答「为什么现在结算」:ProviderTerminal 与 Failure 对 provider 是权威的,Cancelled 只描述客户端/连接层面的停止,不再 覆盖已观察到的 provider 事实。 - candidate 状态与 candidate 错误分类分开输出:现状存在 「missing_terminal=true 而记账层判 Success」的组合(report kind 不要求观察到 终态事件时),会写出 status=Success + error_type=stream_missing_terminal_event, 这个组合必须原样保留。 classify_responses_websocket_turn_effect 的判定表原样搬入 settlement.rs,分支 和顺序均未改动,两个既有不变量测试随之迁移。 行为等价。结算表当前口径与拆分前完全一致:客户端投递失败仍与「供应商声明取消」 落在同一侧(作废账单、candidate 记 Cancelled、只释放 lease、不提交 execution report),即使 provider 终态已经到达——这一行由 settlement_table_row_client_delivery_failure_currently_voids_a_reached_terminal 锁住现状,修正它是下一步独立的行为修正。 新增 15 个测试:outcome → 双事实映射表逐行(含 stream_timeout 只在 504 失败一族 成立、provider 终态即使 504 也不投射流式超时)、结算表逐行、投递失败时 forced_error 必须为 None、已观察终态不被 Cancelled 覆盖、以及跨整张表的 「每个分支都释放 pool key lease」「作废账单一律不提交 report」不变量。
This commit is contained in:
@@ -12,7 +12,7 @@ use super::client::{adapter_drain_ready, forward_client_message, RelayDispositio
|
||||
use super::frame::ParsedResponsesWebSocketFrame;
|
||||
use super::lifecycle::{
|
||||
await_pending_adapter_observation, finalize_active_turn, queue_turn_finalization,
|
||||
ActiveResponsesWebSocketTurn,
|
||||
ActiveProviderAttempt,
|
||||
};
|
||||
use super::quota::{
|
||||
active_continuation_can_retry_from_full_input, detach_exhausted_upstream,
|
||||
@@ -25,7 +25,7 @@ use super::relay_policy::{
|
||||
};
|
||||
use super::state::BoundResponsesConnection;
|
||||
use super::turn::{
|
||||
ResponsesWebSocketTurn, ResponsesWebSocketTurnObservation, ResponsesWebSocketTurnOutcome,
|
||||
ResponsesProviderAttempt, ResponsesWebSocketTurnObservation, ResponsesWebSocketTurnOutcome,
|
||||
};
|
||||
use super::upstream::{close_bound_upstream, receive_optional_upstream};
|
||||
use crate::handlers::proxy::websocket::ingress::WebSocketRequestContext;
|
||||
@@ -383,7 +383,7 @@ pub(super) async fn relay_bound_connection(
|
||||
let mut quota_relay_action = classify_quota_relay(quota_facts);
|
||||
if matches!(quota_relay_action, QuotaRelayAction::AttemptTransparentRetry) {
|
||||
// detach_attempt 保留 logical turn:重试是同一轮请求的下一个 attempt。
|
||||
let mut retry_turn = bound.turn_state.detach_attempt().map(ActiveResponsesWebSocketTurn::disarm);
|
||||
let mut retry_turn = bound.turn_state.detach_attempt().map(ActiveProviderAttempt::disarm);
|
||||
if let Some(turn) = retry_turn.as_mut() {
|
||||
turn.release_admission().await;
|
||||
}
|
||||
@@ -404,7 +404,7 @@ pub(super) async fn relay_bound_connection(
|
||||
if let Some(turn) = retry_turn {
|
||||
let restored = bound
|
||||
.turn_state
|
||||
.resume(ActiveResponsesWebSocketTurn::new(state, turn));
|
||||
.resume(ActiveProviderAttempt::new(state, turn));
|
||||
debug_assert!(
|
||||
restored.is_ok(),
|
||||
"a failed transparent retry must be able to restore its attempt"
|
||||
@@ -433,7 +433,7 @@ pub(super) async fn relay_bound_connection(
|
||||
error_code = "previous_response_not_found",
|
||||
"gateway will ask the client to retry the continuation with complete input"
|
||||
);
|
||||
let mut turn = bound.turn_state.end().map(ActiveResponsesWebSocketTurn::disarm);
|
||||
let mut turn = bound.turn_state.end().map(ActiveProviderAttempt::disarm);
|
||||
if let Some(active_turn) = turn.as_mut() {
|
||||
active_turn.release_admission().await;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user