refactor: 移除独立 hub/proxy/executor/gateway crate,统一为 gateway tunnel 架构

- 删除 aether-hub、aether-proxy 独立项目及其 Dockerfile/配置
- 删除 crates/aether-executor 和 crates/aether-gateway 全部模块
- 新增 apps/ 目录作为应用入口
- 将 hub 概念重构为 gateway tunnel transport
- 将 executor 重构为 execution runtime
- 新增 tunnel.rs 合约定义和 testkit tunnel/execution_runtime 模块
- 更新 Python 服务层和测试适配新架构命名
This commit is contained in:
fawney19
2026-04-03 14:59:58 +08:00
parent ddf18fed9a
commit 8f26e1a31f
983 changed files with 103097 additions and 105836 deletions
@@ -0,0 +1,158 @@
use super::*;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub(crate) struct PendingCleanupSummary {
pub(crate) failed: usize,
pub(crate) recovered: usize,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) struct StalePendingUsageRow {
pub(super) id: String,
pub(super) request_id: String,
pub(super) status: String,
pub(super) billing_status: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub(super) struct FailedPendingUsageRow {
pub(super) id: String,
pub(super) error_message: String,
pub(super) should_void_billing: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub(super) struct PendingCleanupBatchPlan {
pub(super) recovered_usage_ids: Vec<String>,
pub(super) recovered_request_ids: Vec<String>,
pub(super) failed_usage_rows: Vec<FailedPendingUsageRow>,
pub(super) failed_request_ids: Vec<String>,
}
pub(crate) async fn cleanup_stale_pending_requests_once(
data: &GatewayDataState,
) -> Result<PendingCleanupSummary, aether_data::DataLayerError> {
let Some(pool) = data.postgres_pool() else {
return Ok(PendingCleanupSummary::default());
};
let timeout_minutes = pending_cleanup_timeout_minutes(data).await?;
let batch_size = pending_cleanup_batch_size(data).await?;
let cutoff_time =
Utc::now() - chrono::Duration::minutes(i64::try_from(timeout_minutes).unwrap_or(i64::MAX));
let active_statuses = vec!["pending", "streaming"];
let mut summary = PendingCleanupSummary::default();
loop {
let mut tx = pool.begin().await?;
let stale_rows = sqlx::query(SELECT_STALE_PENDING_USAGE_BATCH_SQL)
.bind(active_statuses.clone())
.bind(cutoff_time)
.bind(i64::try_from(batch_size).unwrap_or(i64::MAX))
.fetch_all(&mut *tx)
.await?;
if stale_rows.is_empty() {
tx.rollback().await?;
break;
}
let stale_rows = stale_rows
.into_iter()
.map(|row| {
Ok(StalePendingUsageRow {
id: row.try_get::<String, _>("id")?,
request_id: row.try_get::<String, _>("request_id")?,
status: row.try_get::<String, _>("status")?,
billing_status: row.try_get::<String, _>("billing_status")?,
})
})
.collect::<Result<Vec<_>, sqlx::Error>>()?;
let request_ids = stale_rows
.iter()
.map(|row| row.request_id.clone())
.collect::<Vec<_>>();
let completed_request_ids = if request_ids.is_empty() {
HashSet::new()
} else {
sqlx::query(SELECT_COMPLETED_PENDING_REQUEST_IDS_SQL)
.bind(request_ids)
.fetch_all(&mut *tx)
.await?
.into_iter()
.filter_map(|row| row.try_get::<String, _>("request_id").ok())
.collect::<HashSet<_>>()
};
let plan = plan_pending_cleanup_batch(stale_rows, &completed_request_ids, timeout_minutes);
let now = Utc::now();
for usage_id in &plan.recovered_usage_ids {
sqlx::query(UPDATE_RECOVERED_STALE_USAGE_SQL)
.bind(usage_id)
.execute(&mut *tx)
.await?;
}
for failed_row in &plan.failed_usage_rows {
if failed_row.should_void_billing {
sqlx::query(UPDATE_FAILED_VOID_STALE_USAGE_SQL)
.bind(&failed_row.id)
.bind(&failed_row.error_message)
.bind(now)
.execute(&mut *tx)
.await?;
} else {
sqlx::query(UPDATE_FAILED_STALE_USAGE_SQL)
.bind(&failed_row.id)
.bind(&failed_row.error_message)
.execute(&mut *tx)
.await?;
}
}
if !plan.recovered_request_ids.is_empty() {
sqlx::query(UPDATE_RECOVERED_STREAMING_CANDIDATES_SQL)
.bind(plan.recovered_request_ids.clone())
.bind(now)
.execute(&mut *tx)
.await?;
}
if !plan.failed_request_ids.is_empty() {
sqlx::query(UPDATE_FAILED_PENDING_CANDIDATES_SQL)
.bind(plan.failed_request_ids.clone())
.bind(now)
.bind(active_statuses.clone())
.execute(&mut *tx)
.await?;
}
tx.commit().await?;
summary.failed += plan.failed_usage_rows.len();
summary.recovered += plan.recovered_usage_ids.len();
}
Ok(summary)
}
pub(super) fn plan_pending_cleanup_batch(
stale_rows: Vec<StalePendingUsageRow>,
completed_request_ids: &HashSet<String>,
timeout_minutes: u64,
) -> PendingCleanupBatchPlan {
let mut plan = PendingCleanupBatchPlan::default();
for row in stale_rows {
if completed_request_ids.contains(&row.request_id) {
plan.recovered_usage_ids.push(row.id);
plan.recovered_request_ids.push(row.request_id);
continue;
}
plan.failed_request_ids.push(row.request_id);
plan.failed_usage_rows.push(FailedPendingUsageRow {
id: row.id,
error_message: format!(
"请求超时: 状态 '{}' 超过 {} 分钟未完成",
row.status, timeout_minutes
),
should_void_billing: row.billing_status == "pending",
});
}
plan
}