Federated relay tier: peer flooding, per-member isolation, registry admission and relay discovery

This commit is contained in:
George Coles
2026-09-15 06:49:43 -04:00
parent d30e612be8
commit 2c97fd523f
11 changed files with 759 additions and 177 deletions
+52 -13
View File
@@ -478,38 +478,53 @@ async fn stale_envelopes_are_rejected() {
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn backpressure_is_visible_and_recoverable() {
async fn lagging_member_is_isolated_and_reported() {
let relay_url = spawn_relay_with_capacity(1).await;
let http = client();
let member = Keypair::generate();
let active = Keypair::generate();
let slow = Keypair::generate();
let publisher = Keypair::generate();
register(&http, &relay_url, &member).await;
register(&http, &relay_url, &active).await;
register(&http, &relay_url, &slow).await;
let first = query_envelope(&publisher, &publisher.public_hex(), "one", 5);
assert_eq!(
publish(&http, &relay_url, &first).await.status(),
reqwest::StatusCode::ACCEPTED
);
assert_eq!(
poll_messages(&http, &relay_url, &active, 300).await.len(),
1
);
let second = query_envelope(&publisher, &publisher.public_hex(), "two", 5);
let response = publish(&http, &relay_url, &second).await;
assert_eq!(
publish(&http, &relay_url, &second).await.status(),
reqwest::StatusCode::ACCEPTED,
"a lagging subscriber must not stall publishers"
);
assert_eq!(
poll_messages(&http, &relay_url, &active, 300).await.len(),
1
);
let response = poll(&http, &relay_url, &slow, 300).await;
assert_eq!(response.status(), reqwest::StatusCode::TOO_MANY_REQUESTS);
assert_eq!(
response
.headers()
.get("retry-after")
.and_then(|v| v.to_str().ok()),
.and_then(|value| value.to_str().ok()),
Some("1")
);
assert_eq!(
poll_messages(&http, &relay_url, &member, 300).await.len(),
1
let payload: Value = response.json().await.unwrap();
assert!(
payload.get("missed").and_then(Value::as_u64).unwrap_or(0) >= 1,
"lagging member must be told how much it missed: {payload}"
);
assert_eq!(
poll_messages(&http, &relay_url, &publisher, 300)
.await
.len(),
1
poll(&http, &relay_url, &slow, 50).await.status(),
reqwest::StatusCode::NO_CONTENT
);
let third = query_envelope(&publisher, &publisher.public_hex(), "three", 5);
@@ -517,6 +532,30 @@ async fn backpressure_is_visible_and_recoverable() {
publish(&http, &relay_url, &third).await.status(),
reqwest::StatusCode::ACCEPTED
);
assert_eq!(poll_messages(&http, &relay_url, &slow, 300).await.len(), 1);
let fourth = query_envelope(&publisher, &publisher.public_hex(), "four", 5);
assert_eq!(
publish(&http, &relay_url, &fourth).await.status(),
reqwest::StatusCode::ACCEPTED
);
let fifth = query_envelope(&publisher, &publisher.public_hex(), "five", 5);
assert_eq!(
publish(&http, &relay_url, &fifth).await.status(),
reqwest::StatusCode::ACCEPTED
);
let direct = test_envelope(
&publisher,
TYPE_RESPONSE,
json!({"qid": "q", "results": [], "truncated": false, "more_available": 0, "cursor": null}),
);
assert_eq!(
unicast(&http, &relay_url, &slow.public_hex(), &direct)
.await
.status(),
reqwest::StatusCode::TOO_MANY_REQUESTS,
"unicast to a lagging member must be visibly rejected"
);
}
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]