Skip to content

Commit facd559

Browse files
committed
feat(api): support fast-fail tag; receive immediate response if peer is offline
1 parent 8e10918 commit facd559

2 files changed

Lines changed: 38 additions & 27 deletions

File tree

src/relay/api.rs

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::token::{self, Claims};
2-
use crate::twin::{RelayDomains, TwinDB};
2+
use crate::twin::TwinDB;
33
use crate::types::{Envelope, EnvelopeExt, Pong};
44
use anyhow::{Context, Result};
55
use futures::stream::SplitSink;
@@ -208,6 +208,16 @@ async fn federation<D: TwinDB, R: RateLimiter>(
208208
Envelope::parse_from_bytes(&body).map_err(|err| HttpError::BadRequest(err.to_string()))?;
209209

210210
let dst: SessionID = (&envelope.destination).into();
211+
212+
// fast-fail: if requested and destination is not local, return error immediately
213+
if has_fast_fail(&envelope) && !data.switch.is_local(&dst).await {
214+
// destination session doesn’t exist on this relay
215+
return Response::builder()
216+
.status(http::StatusCode::NOT_FOUND)
217+
.body(Body::from("destination offline"))
218+
.map_err(HttpError::Http);
219+
}
220+
211221
data.switch.send(&dst, &body).await?;
212222

213223
Response::builder()
@@ -216,25 +226,13 @@ async fn federation<D: TwinDB, R: RateLimiter>(
216226
.map_err(HttpError::Http)
217227
}
218228

219-
async fn update_cache_relays(envelope: &Envelope, twin_db: &impl TwinDB) -> Result<()> {
220-
if envelope.relays.is_empty() {
221-
return Ok(());
222-
}
223-
let mut twin = twin_db
224-
.get_twin(envelope.source.twin.into())
225-
.await?
226-
.ok_or_else(|| anyhow::Error::msg("unknown twin source"))?;
227-
let envelope_relays = RelayDomains::new(&envelope.relays);
228-
match twin.relay {
229-
Some(twin_relays) => {
230-
if twin_relays == envelope_relays {
231-
return Ok(());
232-
}
233-
twin.relay = Some(envelope_relays);
234-
}
235-
None => twin.relay = Some(envelope_relays),
236-
}
237-
twin_db.set_twin(twin).await
229+
#[inline]
230+
fn has_fast_fail(envelope: &Envelope) -> bool {
231+
envelope
232+
.tags
233+
.as_deref()
234+
.map(|t| t.contains("fast-fail"))
235+
.unwrap_or(false)
238236
}
239237

240238
type Writer = SplitSink<WebSocketStream<Upgraded>, Message>;
@@ -378,9 +376,10 @@ impl<M: Metrics, D: TwinDB> Session<M, D> {
378376
}
379377

380378
let is_local = self.switch.is_local(&dst).await;
381-
// it's safe to update the local cache since we already authenticated
382-
// the twin hence we trust their information.
383-
update_cache_relays(envelope, &self.twins).await?;
379+
// fast-fail: if requested and destination is not local, return error immediately
380+
if has_fast_fail(envelope) && !is_local {
381+
anyhow::bail!("destination offline");
382+
}
384383

385384
// check if the dst twin id is already connected locally
386385
// if so, we don't have to check federation and directly

src/relay/federation/router.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,17 +62,29 @@ where
6262
}
6363
};
6464

65-
if resp.status() != StatusCode::ACCEPTED {
65+
if resp.status() == StatusCode::ACCEPTED {
66+
return Ok(domain.as_ref());
67+
}
68+
69+
// Treat client errors (4xx) as non-retryable: return immediately with error
70+
if resp.status().is_client_error() {
6671
log::warn!(
67-
"received relay '{}' did not accept the message: {}",
72+
"relay '{}' rejected the message with client error: {} (non-retryable)",
6873
domain.as_ref(),
6974
resp.status()
7075
);
7176
self.ranker.downvote(domain.as_ref()).await;
72-
continue;
77+
bail!("remote relay rejected permanently: {}", resp.status());
7378
}
7479

75-
return Ok(domain.as_ref());
80+
// For other statuses (e.g., 5xx), downvote and try next candidate
81+
log::warn!(
82+
"received relay '{}' did not accept the message: {}",
83+
domain.as_ref(),
84+
resp.status()
85+
);
86+
self.ranker.downvote(domain.as_ref()).await;
87+
continue;
7688
}
7789
}
7890
bail!("relays '{:?}' was not reachable in time", domains);

0 commit comments

Comments
 (0)