fix(runtime): preserve typed fetch timeout for late inner results

This commit is contained in:
ldm0
2026-10-11 18:06:50 +08:00
parent 38604ee703
commit 9e5e78ebef
+22 -2
View File
@@ -137,9 +137,11 @@ impl FetchDeadline {
where
F: Future<Output = Result<T>>,
{
// Tokio polls the inner future first. A result queued after the deadline
// must not replace the typed timeout when both are ready on the next poll.
match tokio::time::timeout_at(self.at, future).await {
Ok(result) => result,
Err(_) => Err(anyhow::Error::new(FetchReadinessTimeout::new(
Ok(result) if tokio::time::Instant::now() < self.at => result,
_ => Err(anyhow::Error::new(FetchReadinessTimeout::new(
self.timeout,
phase,
))),
@@ -193,6 +195,24 @@ mod tests {
Ok(())
}
#[tokio::test]
async fn expired_deadline_wins_over_a_ready_inner_result() -> Result<()> {
for result in [Ok(()), Err(anyhow::anyhow!("inner operation timed out"))] {
let deadline = FetchDeadline::new(Duration::ZERO)?;
let error = deadline
.wait(
FetchTimeoutPhase::WaitingForScript,
std::future::ready(result),
)
.await
.unwrap_err();
let timeout = error.downcast_ref::<FetchReadinessTimeout>().unwrap();
assert_eq!(timeout.timeout(), Duration::ZERO);
assert_eq!(timeout.phase(), FetchTimeoutPhase::WaitingForScript);
}
Ok(())
}
#[test]
fn impossible_deadline_range_is_an_anyhow_error() {
let error = FetchDeadline::new(Duration::MAX).unwrap_err();