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 <noreply@anthropic.com>
This commit is contained in:
Rayhaan JaufeerallyandClaude Opus 5.5 committed 2026-10-05 16:08:19 +02:00
1 parent 9362ba9e34
commit 52593bb2c2
2 files changed
+55 -8

No files matched your search

+50 -8
View File
@@ -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);
}
}