fix(client): signup-publish-retry-flexible (#120)

* feat(client): signup-publish-retry-flexible

* fix wasm

* Retry on known query errors fixed 3 attemps 1s

* simplify
This commit is contained in:
SHAcollision
2025-05-08 13:27:11 +02:00
committed by GitHub
parent 64d003c235
commit 00aafb2163
3 changed files with 60 additions and 11 deletions

13
Cargo.lock generated
View File

@@ -1152,6 +1152,18 @@ version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
[[package]]
name = "gloo-timers"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbb143cf96099802033e0d4f4963b19fd2e0b728bcf076cd9cf7f6634f092994"
dependencies = [
"futures-channel",
"futures-core",
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "governor"
version = "0.8.0"
@@ -2280,6 +2292,7 @@ dependencies = [
"futures-util",
"getrandom 0.2.15",
"getrandom 0.3.1",
"gloo-timers",
"js-sys",
"log",
"mainline",

View File

@@ -49,6 +49,7 @@ wasm-bindgen = "0.2.100"
wasm-bindgen-futures = "0.4.50"
console_log = { version = "1.0.0", features = ["color"] }
log = "0.4.25"
gloo-timers = { version = "0.3", features = ["futures"] }
js-sys = "0.3.77"
web-sys = "0.3.77"

View File

@@ -1,6 +1,7 @@
use anyhow::Result;
use pkarr::{
dns::rdata::{RData, SVCB},
errors::QueryError,
Keypair, SignedPacket, Timestamp,
};
use std::convert::TryInto;
@@ -8,6 +9,28 @@ use std::time::Duration;
use super::super::Client;
// sleep for native
#[cfg(not(wasm_browser))]
use tokio::time::sleep;
// sleep for wasm
#[cfg(wasm_browser)]
use gloo_timers::future::sleep;
/// Helper returns true if this error (or any of its sources) is one of our
/// three recoverable `QueryError`s with simple retrial.
fn should_retry(err: &anyhow::Error) -> bool {
err.chain()
.filter_map(|cause| cause.downcast_ref::<QueryError>())
.any(|q| {
matches!(
q,
QueryError::Timeout
| QueryError::NoClosestNodes
| QueryError::DhtErrorResponse(_, _)
)
})
}
/// The strategy to decide whether to (re)publish a homeserver record.
pub(crate) enum PublishStrategy {
/// Always publish a new record (used on signup).
@@ -30,16 +53,16 @@ impl Client {
host: Option<&str>,
strategy: PublishStrategy,
) -> Result<()> {
// Resolve the most recent record.
// 1) Resolve the most recent record.
let existing = self.pkarr.resolve_most_recent(&keypair.public_key()).await;
// Determine which host we should be using.
// 2) Determine which host we should be using.
let host_str = match Self::determine_host(host, existing.as_ref()) {
Some(host) => host,
None => return Ok(()),
};
// Calculate the age of the existing record.
// 3) Calculate age of the existing record.
let packet_age = match existing {
Some(ref record) => {
let elapsed = Timestamp::now() - record.timestamp();
@@ -48,15 +71,27 @@ impl Client {
None => Duration::from_secs(u64::MAX), // Use max duration if no record exists.
};
// Determine if we should publish based on the given strategy.
let should_publish = match strategy {
PublishStrategy::Force => true,
PublishStrategy::IfOlderThan => packet_age > self.max_record_age,
};
// 4) Should we publish?
let should_publish =
matches!(strategy, PublishStrategy::Force) || packet_age > self.max_record_age;
if should_publish {
self.publish_homeserver_inner(keypair, &host_str, existing)
.await?;
if !should_publish {
return Ok(());
}
// 5) Retry loop: up to 3 attempts, 1s back-off, only on specific QueryErrors.
for attempt in 1..=3 {
match self
.publish_homeserver_inner(keypair, &host_str, existing.clone())
.await
{
Ok(()) => break,
Err(e) if should_retry(&e) && attempt < 3 => {
sleep(Duration::from_secs(1)).await;
continue;
}
Err(e) => return Err(e),
}
}
Ok(())