From 52593bb2c29f78d3afa5a86d3355061845f2f3ef Mon Sep 17 00:00:00 2001 From: Rayhaan Jaufeerally Date: Mon, 5 Oct 2026 16:08:19 +0200 Subject: [PATCH] RPKI: ask a cache without data yet again soon A validator that is still running its first validation answers a query with No Data Available (error code 2). That ended the session, and the next attempt came after the retry interval: 600 seconds when the cache had not yet said otherwise, which it cannot have before it has data. For those ten minutes after a validator started, nothing was validated. No Data Available is now asked again after 30 seconds, or after retry_seconds if that is shorter, and logged as information rather than as a warning. Other failures wait as before, after trying the next cache if there is one. Co-Authored-By: Claude Opus 5.5 --- crates/rpki/src/client.rs | 58 +++++++++++++++++++++++++++----- docs/content/docs/guides/rpki.md | 5 +++ 2 files changed, 55 insertions(+), 8 deletions(-) diff --git a/crates/rpki/src/client.rs b/crates/rpki/src/client.rs index ae6d48a..ef31d69 100644 --- a/crates/rpki/src/client.rs +++ b/crates/rpki/src/client.rs @@ -54,6 +54,12 @@ const MAX_RECORDS: usize = 8_000_000; /// How long to wait before trying the next cache after one has failed. const NEXT_CACHE_DELAY: Duration = Duration::from_secs(1); +/// How long to wait before asking a cache again that said it has no data +/// yet, rather than the retry interval: it is up and validating, which +/// takes minutes after it starts, and the retry interval is ten unless it +/// has said otherwise, which it has not had the chance to. +const NO_DATA_RETRY: Duration = Duration::from_secs(30); + /// How long a cache that is being left is given to take the reason. const ERROR_REPORT_TIMEOUT: Duration = Duration::from_secs(1); @@ -419,23 +425,29 @@ impl Client { self.protocol_version = version; continue; } - warn!(%cache, err = %ended, "RPKI cache session ended"); + let no_data = matches!( + ended, + SessionError::Reported { + code: error_code::NO_DATA_AVAILABLE, + .. + } + ); + if no_data { + info!(%cache, "RPKI cache has no data yet; asking again soon"); + } else { + warn!(%cache, err = %ended, "RPKI cache session ended"); + } self.status(|status| { status.state = CacheState::Connecting; status.last_error = Some(ended.to_string()); }); - // The next cache at once; when all have been tried, a rest. attempt += 1; // The next cache, or this one when it is back, may speak a // newer version. self.protocol_version = VERSION_2; - let wait = if attempt.is_multiple_of(self.config.caches.len()) { - self.retry() - } else { - NEXT_CACHE_DELAY - }; - self.wait(wait).await; + let all_tried = attempt.is_multiple_of(self.config.caches.len()); + self.wait(rest(all_tried, no_data, self.retry())).await; } } @@ -889,3 +901,33 @@ enum Answer { /// With a request to ask for everything instead. Reset, } + +/// How long to rest after a session ended: the next cache at once; when +/// all have been tried, the retry interval, or less if the last one is +/// only still validating. +fn rest(all_tried: bool, no_data: bool, retry: Duration) -> Duration { + match (all_tried, no_data) { + (false, _) => NEXT_CACHE_DELAY, + (true, true) => NO_DATA_RETRY.min(retry), + (true, false) => retry, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + /// A cache that is still validating after it started is asked again + /// in half a minute, not the ten the retry interval is by default. + #[test] + fn a_cache_without_data_yet_is_asked_again_soon() { + let ten_minutes = Duration::from_secs(600); + assert_eq!(rest(true, true, ten_minutes), NO_DATA_RETRY); + assert_eq!(rest(true, false, ten_minutes), ten_minutes); + // Never later than the retry interval says. + let short = Duration::from_secs(5); + assert_eq!(rest(true, true, short), short); + // Another cache first, whatever this one said. + assert_eq!(rest(false, true, ten_minutes), NEXT_CACHE_DELAY); + } +} diff --git a/docs/content/docs/guides/rpki.md b/docs/content/docs/guides/rpki.md index c67a2d1..e5a7f0f 100644 --- a/docs/content/docs/guides/rpki.md +++ b/docs/content/docs/guides/rpki.md @@ -64,6 +64,11 @@ rpki { When the data expires, Ferrix discards it, and no route is considered invalid until new data is received. +A validator that has just started reports that it has no data available +until its first validation run is complete, which takes several minutes. +Ferrix then asks again after 30 seconds, or after `retry_seconds` if that +is shorter, rather than after the retry interval. + ### Running a Validator The following command starts Routinator with an RTR server on port 3323: