fix(store): release rejected proxy import locks before returning

Explicitly unlock rejected import reservations before returning their error so immediate proxy operations do not race connection teardown. Cover repeated rejection followed by writes with PostgreSQL integration tests.

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Zyy
2026-09-17 05:36:00 +08:00
co-authored by Codex
parent 9cfcd371d6
commit 590c4a163b
2 changed files with 53 additions and 4 deletions
@@ -441,10 +441,18 @@ impl ProxyStore for PgProxyRepository {
if !acquired {
return Err(store_error(conflict(id)));
}
let record = self.get(id).await?;
if !record.last_test.is_some_and(|test| test.success) {
return Err(store_error(conflict(id)));
}
let record = match self.get(id).await {
Ok(record) if record.last_test.as_ref().is_some_and(|test| test.success) => record,
result => {
// 拒绝预留时先等待数据库释放锁,避免连接关闭尚未生效就误挡后续代理操作。
sqlx::query("select pg_advisory_unlock_shared(hashtextextended($1, 739219))")
.bind(id)
.execute(&mut connection)
.await
.map_err(|_| store_error(unavailable()))?;
return Err(result.err().unwrap_or_else(|| store_error(conflict(id))));
}
};
Ok(ProxyImportReservation {
binding: ImportProxyBinding {
id: record.id,
@@ -473,6 +473,47 @@ async fn proxy_accounts_paginate_thousands_of_accounts_and_search_without_loadin
database.close().await;
}
#[tokio::test]
async fn rejected_import_reservations_release_proxy_lock_before_returning() {
let Some(database) = TestDatabase::create("rejected_proxy_import").await else {
return;
};
let store = PgProxyRepository::new(database.pool.clone());
let other_process = PgProxyRepository::new(database.pool.clone());
let context = context();
let saved = store
.create(
NewProxy {
location: None,
name: "未通过检测的出口".to_owned(),
proxy: OutboundProxy::parse("http://127.0.0.1:8080").unwrap(),
},
&context,
)
.await
.unwrap()
.record;
// 每次失败后立即写入检测结果,不等待连接关闭或重试锁冲突。
for _ in 0..64 {
let error = store.reserve_import(&saved.id).await.err().unwrap();
assert_eq!(error.kind(), AdminStoreErrorKind::Conflict);
other_process
.record_test(
&saved.id,
saved.revision,
ProxyTestResult {
success: false,
..success()
},
&context,
)
.await
.unwrap();
}
database.close().await;
}
#[tokio::test]
async fn import_reservation_blocks_proxy_mutations_until_rotated_credentials_are_committed() {
use gateway_store::postgres::{