20260620 优化delta长尾控制面和PP cache快路径
This commit is contained in:
@@ -72,6 +72,44 @@ pub fn discover_root_ca_instance_from_tal_url_with_strict_name(
|
||||
)
|
||||
}
|
||||
|
||||
pub fn discover_root_ca_instance_from_tal_url_with_fetchers(
|
||||
http_fetcher: &dyn Fetcher,
|
||||
rsync_fetcher: &dyn RsyncFetcher,
|
||||
tal_url: &str,
|
||||
) -> Result<DiscoveredRootCaInstance, FromTalError> {
|
||||
let tal_bytes = http_fetcher
|
||||
.fetch(tal_url)
|
||||
.map_err(FromTalError::TalFetch)?;
|
||||
let tal = Tal::decode_bytes(&tal_bytes)?;
|
||||
discover_root_ca_instance_from_tal_with_fetchers_impl(
|
||||
http_fetcher,
|
||||
rsync_fetcher,
|
||||
tal,
|
||||
Some(tal_url.to_string()),
|
||||
false,
|
||||
TaUriOrder::HttpFirst,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn discover_root_ca_instance_from_tal_url_with_fetchers_strict_name(
|
||||
http_fetcher: &dyn Fetcher,
|
||||
rsync_fetcher: &dyn RsyncFetcher,
|
||||
tal_url: &str,
|
||||
) -> Result<DiscoveredRootCaInstance, FromTalError> {
|
||||
let tal_bytes = http_fetcher
|
||||
.fetch(tal_url)
|
||||
.map_err(FromTalError::TalFetch)?;
|
||||
let tal = Tal::decode_bytes(&tal_bytes)?;
|
||||
discover_root_ca_instance_from_tal_with_fetchers_impl(
|
||||
http_fetcher,
|
||||
rsync_fetcher,
|
||||
tal,
|
||||
Some(tal_url.to_string()),
|
||||
true,
|
||||
TaUriOrder::HttpFirst,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn discover_root_ca_instance_from_tal(
|
||||
http_fetcher: &dyn Fetcher,
|
||||
tal: Tal,
|
||||
@@ -150,6 +188,7 @@ pub fn discover_root_ca_instance_from_tal_with_fetchers(
|
||||
tal,
|
||||
tal_url,
|
||||
false,
|
||||
TaUriOrder::RsyncFirst,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -165,15 +204,23 @@ pub fn discover_root_ca_instance_from_tal_with_fetchers_strict_name(
|
||||
tal,
|
||||
tal_url,
|
||||
true,
|
||||
TaUriOrder::RsyncFirst,
|
||||
)
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
enum TaUriOrder {
|
||||
HttpFirst,
|
||||
RsyncFirst,
|
||||
}
|
||||
|
||||
fn discover_root_ca_instance_from_tal_with_fetchers_impl(
|
||||
http_fetcher: &dyn Fetcher,
|
||||
rsync_fetcher: &dyn RsyncFetcher,
|
||||
tal: Tal,
|
||||
tal_url: Option<String>,
|
||||
strict_name: bool,
|
||||
ta_uri_order: TaUriOrder,
|
||||
) -> Result<DiscoveredRootCaInstance, FromTalError> {
|
||||
if tal.ta_uris.is_empty() {
|
||||
return Err(FromTalError::NoTaUris);
|
||||
@@ -181,7 +228,7 @@ fn discover_root_ca_instance_from_tal_with_fetchers_impl(
|
||||
|
||||
let mut last_err: Option<String> = None;
|
||||
let mut ta_uris = tal.ta_uris.clone();
|
||||
ta_uris.sort_by_key(|uri| if uri.scheme() == "rsync" { 0 } else { 1 });
|
||||
ta_uris.sort_by_key(|uri| ta_uri_priority(uri, ta_uri_order));
|
||||
for ta_uri in ta_uris.iter() {
|
||||
let ta_der = match fetch_ta_der(http_fetcher, rsync_fetcher, ta_uri) {
|
||||
Ok(b) => b,
|
||||
@@ -221,6 +268,16 @@ fn discover_root_ca_instance_from_tal_with_fetchers_impl(
|
||||
})))
|
||||
}
|
||||
|
||||
fn ta_uri_priority(uri: &Url, order: TaUriOrder) -> u8 {
|
||||
match (order, uri.scheme()) {
|
||||
(TaUriOrder::HttpFirst, "https" | "http") => 0,
|
||||
(TaUriOrder::HttpFirst, "rsync") => 1,
|
||||
(TaUriOrder::RsyncFirst, "rsync") => 0,
|
||||
(TaUriOrder::RsyncFirst, "https" | "http") => 1,
|
||||
_ => 2,
|
||||
}
|
||||
}
|
||||
|
||||
fn fetch_ta_der(
|
||||
http_fetcher: &dyn Fetcher,
|
||||
rsync_fetcher: &dyn RsyncFetcher,
|
||||
@@ -250,41 +307,9 @@ fn fetch_ta_der_via_rsync(
|
||||
rsync_fetcher: &dyn RsyncFetcher,
|
||||
ta_rsync_uri: &str,
|
||||
) -> Result<Vec<u8>, String> {
|
||||
let base = rsync_parent_uri(ta_rsync_uri)?;
|
||||
let objects = rsync_fetcher
|
||||
.fetch_objects(&base)
|
||||
.map_err(|e| e.to_string())?;
|
||||
objects
|
||||
.into_iter()
|
||||
.find(|(uri, _)| uri == ta_rsync_uri)
|
||||
.map(|(_, bytes)| bytes)
|
||||
.ok_or_else(|| format!("TA rsync object not found in fetched subtree: {ta_rsync_uri}"))
|
||||
}
|
||||
|
||||
fn rsync_parent_uri(ta_rsync_uri: &str) -> Result<String, String> {
|
||||
let url = Url::parse(ta_rsync_uri).map_err(|e| e.to_string())?;
|
||||
if url.scheme() != "rsync" {
|
||||
return Err(format!("not an rsync URI: {ta_rsync_uri}"));
|
||||
}
|
||||
let host = url
|
||||
.host_str()
|
||||
.ok_or_else(|| format!("missing host in rsync URI: {ta_rsync_uri}"))?;
|
||||
let segments = url
|
||||
.path_segments()
|
||||
.ok_or_else(|| format!("missing path in rsync URI: {ta_rsync_uri}"))?
|
||||
.collect::<Vec<_>>();
|
||||
if segments.is_empty() || segments.last().copied().unwrap_or_default().is_empty() {
|
||||
return Err(format!(
|
||||
"rsync URI must reference a file object: {ta_rsync_uri}"
|
||||
));
|
||||
}
|
||||
let parent_segments = &segments[..segments.len() - 1];
|
||||
let mut parent = format!("rsync://{host}/");
|
||||
if !parent_segments.is_empty() {
|
||||
parent.push_str(&parent_segments.join("/"));
|
||||
parent.push('/');
|
||||
}
|
||||
Ok(parent)
|
||||
rsync_fetcher
|
||||
.fetch_object(ta_rsync_uri)
|
||||
.map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
pub fn discover_root_ca_instance_from_tal_and_ta_der(
|
||||
@@ -334,6 +359,20 @@ pub fn canonical_tal_rsync_uri_from_bytes(tal_bytes: &[u8]) -> Result<Url, FromT
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::fetch::rsync::LocalDirRsyncFetcher;
|
||||
use std::collections::HashMap;
|
||||
|
||||
struct MapHttpFetcher {
|
||||
map: HashMap<String, Vec<u8>>,
|
||||
}
|
||||
|
||||
impl Fetcher for MapHttpFetcher {
|
||||
fn fetch(&self, uri: &str) -> Result<Vec<u8>, String> {
|
||||
self.map
|
||||
.get(uri)
|
||||
.cloned()
|
||||
.ok_or_else(|| format!("no fixture mapped for {uri}"))
|
||||
}
|
||||
}
|
||||
|
||||
struct FailingHttpFetcher;
|
||||
|
||||
@@ -392,6 +431,84 @@ mod tests {
|
||||
.starts_with("rsync://")
|
||||
);
|
||||
}
|
||||
|
||||
struct PanicRsyncFetcher;
|
||||
|
||||
impl RsyncFetcher for PanicRsyncFetcher {
|
||||
fn fetch_objects(
|
||||
&self,
|
||||
rsync_base_uri: &str,
|
||||
) -> crate::fetch::rsync::RsyncFetchResult<Vec<(String, Vec<u8>)>> {
|
||||
panic!("rsync should not be used when HTTPS TA URI is available: {rsync_base_uri}")
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn discover_root_ca_instance_from_tal_url_with_fetchers_prefers_https_ta_uri() {
|
||||
let tal_url = "https://example.test/apnic.tal";
|
||||
let tal_bytes = std::fs::read(
|
||||
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("tests/fixtures/tal/apnic-rfc7730-https.tal"),
|
||||
)
|
||||
.unwrap();
|
||||
let ta_der = std::fs::read(
|
||||
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("tests/fixtures/ta/apnic-ta.cer"),
|
||||
)
|
||||
.unwrap();
|
||||
let https_uri = "https://rpki.apnic.net/repository/apnic-rpki-root-iana-origin.cer";
|
||||
let mut map = HashMap::new();
|
||||
map.insert(tal_url.to_string(), tal_bytes);
|
||||
map.insert(https_uri.to_string(), ta_der);
|
||||
let discovery = discover_root_ca_instance_from_tal_url_with_fetchers(
|
||||
&MapHttpFetcher { map },
|
||||
&PanicRsyncFetcher,
|
||||
tal_url,
|
||||
)
|
||||
.expect("discover via HTTPS TA URI");
|
||||
assert_eq!(
|
||||
discovery.trust_anchor.resolved_ta_uri.unwrap().as_str(),
|
||||
https_uri
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn discover_root_ca_instance_from_tal_url_with_fetchers_supports_rsync_only_tal() {
|
||||
let tal_url = "https://example.test/apnic-rsync-only.tal";
|
||||
let tal_bytes = std::fs::read_to_string(
|
||||
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("tests/fixtures/tal/apnic-rfc7730-https.tal"),
|
||||
)
|
||||
.unwrap();
|
||||
let rsync_uri = "rsync://rpki.apnic.net/repository/apnic-rpki-root-iana-origin.cer";
|
||||
let key_material = tal_bytes
|
||||
.split_once("\n\n")
|
||||
.map(|(_, key)| key)
|
||||
.expect("fixture contains TAL key material");
|
||||
let rsync_only_tal = format!("{rsync_uri}\n\n{key_material}");
|
||||
let ta_der = std::fs::read(
|
||||
std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("tests/fixtures/ta/apnic-ta.cer"),
|
||||
)
|
||||
.unwrap();
|
||||
let td = tempfile::tempdir().unwrap();
|
||||
let mirror_root = td.path().join("repository");
|
||||
std::fs::create_dir_all(&mirror_root).unwrap();
|
||||
std::fs::write(mirror_root.join("apnic-rpki-root-iana-origin.cer"), ta_der).unwrap();
|
||||
|
||||
let mut map = HashMap::new();
|
||||
map.insert(tal_url.to_string(), rsync_only_tal.into_bytes());
|
||||
let discovery = discover_root_ca_instance_from_tal_url_with_fetchers(
|
||||
&MapHttpFetcher { map },
|
||||
&LocalDirRsyncFetcher::new(mirror_root),
|
||||
tal_url,
|
||||
)
|
||||
.expect("discover via rsync-only TAL");
|
||||
assert_eq!(
|
||||
discovery.trust_anchor.resolved_ta_uri.unwrap().as_str(),
|
||||
rsync_uri
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn run_root_from_tal_url_once(
|
||||
@@ -402,7 +519,8 @@ pub fn run_root_from_tal_url_once(
|
||||
rsync_fetcher: &dyn crate::fetch::rsync::RsyncFetcher,
|
||||
validation_time: time::OffsetDateTime,
|
||||
) -> Result<RunFromTalOutput, FromTalError> {
|
||||
let discovery = discover_root_ca_instance_from_tal_url(http_fetcher, tal_url)?;
|
||||
let discovery =
|
||||
discover_root_ca_instance_from_tal_url_with_fetchers(http_fetcher, rsync_fetcher, tal_url)?;
|
||||
|
||||
let run = run_publication_point_once(
|
||||
store,
|
||||
|
||||
@@ -27,8 +27,8 @@ use crate::validation::from_tal::{
|
||||
DiscoveredRootCaInstance, FromTalError, canonical_tal_rsync_uri_from_bytes,
|
||||
discover_root_ca_instance_from_tal_and_ta_der,
|
||||
discover_root_ca_instance_from_tal_and_ta_der_with_strict_name,
|
||||
discover_root_ca_instance_from_tal_url,
|
||||
discover_root_ca_instance_from_tal_url_with_strict_name,
|
||||
discover_root_ca_instance_from_tal_url_with_fetchers,
|
||||
discover_root_ca_instance_from_tal_url_with_fetchers_strict_name,
|
||||
discover_root_ca_instance_from_tal_with_fetchers,
|
||||
discover_root_ca_instance_from_tal_with_fetchers_strict_name,
|
||||
};
|
||||
@@ -348,9 +348,17 @@ fn root_discovery_from_tal_input(
|
||||
match &tal_input.source {
|
||||
TalSource::Url(url) => {
|
||||
if strict_name {
|
||||
discover_root_ca_instance_from_tal_url_with_strict_name(http_fetcher, url)
|
||||
discover_root_ca_instance_from_tal_url_with_fetchers_strict_name(
|
||||
http_fetcher,
|
||||
rsync_fetcher,
|
||||
url,
|
||||
)
|
||||
} else {
|
||||
discover_root_ca_instance_from_tal_url(http_fetcher, url)
|
||||
discover_root_ca_instance_from_tal_url_with_fetchers(
|
||||
http_fetcher,
|
||||
rsync_fetcher,
|
||||
url,
|
||||
)
|
||||
}
|
||||
}
|
||||
TalSource::DerBytes {
|
||||
@@ -414,12 +422,17 @@ fn root_discovery_from_tal_input(
|
||||
fn discover_root_ca_instance_from_tal_url_with_policy(
|
||||
policy: &crate::policy::Policy,
|
||||
http_fetcher: &dyn Fetcher,
|
||||
rsync_fetcher: &dyn crate::fetch::rsync::RsyncFetcher,
|
||||
tal_url: &str,
|
||||
) -> Result<DiscoveredRootCaInstance, FromTalError> {
|
||||
if policy.strict.name {
|
||||
discover_root_ca_instance_from_tal_url_with_strict_name(http_fetcher, tal_url)
|
||||
discover_root_ca_instance_from_tal_url_with_fetchers_strict_name(
|
||||
http_fetcher,
|
||||
rsync_fetcher,
|
||||
tal_url,
|
||||
)
|
||||
} else {
|
||||
discover_root_ca_instance_from_tal_url(http_fetcher, tal_url)
|
||||
discover_root_ca_instance_from_tal_url_with_fetchers(http_fetcher, rsync_fetcher, tal_url)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -544,8 +557,12 @@ pub fn run_tree_from_tal_url_serial(
|
||||
validation_time: time::OffsetDateTime,
|
||||
config: &TreeRunConfig,
|
||||
) -> Result<RunTreeFromTalOutput, RunTreeFromTalError> {
|
||||
let discovery =
|
||||
discover_root_ca_instance_from_tal_url_with_policy(policy, http_fetcher, tal_url)?;
|
||||
let discovery = discover_root_ca_instance_from_tal_url_with_policy(
|
||||
policy,
|
||||
http_fetcher,
|
||||
rsync_fetcher,
|
||||
tal_url,
|
||||
)?;
|
||||
|
||||
let runner = make_live_runner(
|
||||
store,
|
||||
@@ -585,8 +602,12 @@ pub fn run_tree_from_tal_url_serial_audit(
|
||||
validation_time: time::OffsetDateTime,
|
||||
config: &TreeRunConfig,
|
||||
) -> Result<RunTreeFromTalAuditOutput, RunTreeFromTalError> {
|
||||
let discovery =
|
||||
discover_root_ca_instance_from_tal_url_with_policy(policy, http_fetcher, tal_url)?;
|
||||
let discovery = discover_root_ca_instance_from_tal_url_with_policy(
|
||||
policy,
|
||||
http_fetcher,
|
||||
rsync_fetcher,
|
||||
tal_url,
|
||||
)?;
|
||||
|
||||
let download_log = DownloadLogHandle::new();
|
||||
let runner = make_live_runner(
|
||||
@@ -648,8 +669,12 @@ pub fn run_tree_from_tal_url_serial_audit_with_timing(
|
||||
timing: &TimingHandle,
|
||||
) -> Result<RunTreeFromTalAuditOutput, RunTreeFromTalError> {
|
||||
let _tal = timing.span_phase("tal_bootstrap");
|
||||
let discovery =
|
||||
discover_root_ca_instance_from_tal_url_with_policy(policy, http_fetcher, tal_url)?;
|
||||
let discovery = discover_root_ca_instance_from_tal_url_with_policy(
|
||||
policy,
|
||||
http_fetcher,
|
||||
rsync_fetcher,
|
||||
tal_url,
|
||||
)?;
|
||||
drop(_tal);
|
||||
|
||||
let download_log = DownloadLogHandle::new();
|
||||
@@ -935,8 +960,12 @@ where
|
||||
H: Fetcher + Clone + 'static,
|
||||
R: crate::fetch::rsync::RsyncFetcher + Clone + 'static,
|
||||
{
|
||||
let discovery =
|
||||
discover_root_ca_instance_from_tal_url_with_policy(policy, http_fetcher, tal_url)?;
|
||||
let discovery = discover_root_ca_instance_from_tal_url_with_policy(
|
||||
policy,
|
||||
http_fetcher,
|
||||
rsync_fetcher,
|
||||
tal_url,
|
||||
)?;
|
||||
run_single_root_parallel_audit_inner(
|
||||
store,
|
||||
policy,
|
||||
@@ -1051,8 +1080,12 @@ where
|
||||
H: Fetcher + Clone + 'static,
|
||||
R: crate::fetch::rsync::RsyncFetcher + Clone + 'static,
|
||||
{
|
||||
let discovery =
|
||||
discover_root_ca_instance_from_tal_url_with_policy(policy, http_fetcher, tal_url)?;
|
||||
let discovery = discover_root_ca_instance_from_tal_url_with_policy(
|
||||
policy,
|
||||
http_fetcher,
|
||||
rsync_fetcher,
|
||||
tal_url,
|
||||
)?;
|
||||
run_single_root_parallel_audit_inner(
|
||||
store,
|
||||
policy,
|
||||
@@ -1086,8 +1119,12 @@ where
|
||||
H: Fetcher + Clone + 'static,
|
||||
R: crate::fetch::rsync::RsyncFetcher + Clone + 'static,
|
||||
{
|
||||
let discovery =
|
||||
discover_root_ca_instance_from_tal_url_with_policy(policy, http_fetcher, tal_url)?;
|
||||
let discovery = discover_root_ca_instance_from_tal_url_with_policy(
|
||||
policy,
|
||||
http_fetcher,
|
||||
rsync_fetcher,
|
||||
tal_url,
|
||||
)?;
|
||||
run_single_root_parallel_audit_inner(
|
||||
store,
|
||||
policy,
|
||||
|
||||
@@ -405,6 +405,8 @@ struct RepoDrainMetrics {
|
||||
duration_ms: u64,
|
||||
}
|
||||
|
||||
const REPO_RESULT_DRAIN_MAX_EVENTS: usize = 64;
|
||||
|
||||
fn elapsed_ms(started: Instant) -> u64 {
|
||||
started.elapsed().as_millis() as u64
|
||||
}
|
||||
@@ -1184,6 +1186,11 @@ fn stage_ready_publication_point(
|
||||
) {
|
||||
ParallelObjectsPrepare::Complete(mut objects) => {
|
||||
metrics.prepare_ms = elapsed_ms(prepare_started);
|
||||
runner.record_publication_point_step_ms(
|
||||
&ready.node.handle.manifest_rsync_uri,
|
||||
"fresh_objects_prepare",
|
||||
metrics.prepare_ms,
|
||||
);
|
||||
metrics.complete_count = 1;
|
||||
metrics.roa_tasks = objects.stats.roa_total;
|
||||
metrics.aspa_objects = objects.stats.aspa_total;
|
||||
@@ -1210,15 +1217,30 @@ fn stage_ready_publication_point(
|
||||
metrics.direct_finalize_ms = finalize_metrics
|
||||
.finalize_ms
|
||||
.max(elapsed_ms(direct_finalize_started));
|
||||
runner.record_publication_point_step_ms(
|
||||
&metrics.manifest_rsync_uri.clone().unwrap_or_default(),
|
||||
"fresh_direct_finalize",
|
||||
metrics.direct_finalize_ms,
|
||||
);
|
||||
}
|
||||
ParallelObjectsPrepare::Staged(objects_stage) => {
|
||||
metrics.prepare_ms = elapsed_ms(prepare_started);
|
||||
runner.record_publication_point_step_ms(
|
||||
&ready.node.handle.manifest_rsync_uri,
|
||||
"fresh_objects_prepare",
|
||||
metrics.prepare_ms,
|
||||
);
|
||||
metrics.staged_count = 1;
|
||||
metrics.locked_files = objects_stage.locked_file_count();
|
||||
metrics.aspa_objects = objects_stage.aspa_task_count();
|
||||
let build_tasks_started = Instant::now();
|
||||
objects_stage.append_roa_tasks_to(pending_roa_dispatch);
|
||||
metrics.build_roa_tasks_ms = elapsed_ms(build_tasks_started);
|
||||
runner.record_publication_point_step_ms(
|
||||
&ready.node.handle.manifest_rsync_uri,
|
||||
"fresh_build_roa_tasks",
|
||||
metrics.build_roa_tasks_ms,
|
||||
);
|
||||
let task_count = objects_stage.roa_task_count();
|
||||
metrics.roa_tasks = task_count;
|
||||
if task_count == 0 {
|
||||
@@ -1275,6 +1297,12 @@ fn stage_ready_publication_point(
|
||||
}
|
||||
}
|
||||
metrics.total_ms = elapsed_ms(publication_point_started);
|
||||
if metrics.complete_count > 0 {
|
||||
runner.record_publication_point_total_ms(
|
||||
metrics.manifest_rsync_uri.as_deref().unwrap_or_default(),
|
||||
metrics.total_ms,
|
||||
);
|
||||
}
|
||||
emit_ready_publication_point_control_slow(
|
||||
metrics.manifest_rsync_uri.as_deref().unwrap_or_default(),
|
||||
metrics
|
||||
@@ -1916,10 +1944,20 @@ fn finalize_publication_point_state(
|
||||
.as_millis() as u64
|
||||
});
|
||||
let objects_processing_ms = objects_started_at.elapsed().as_millis() as u64;
|
||||
runner.record_publication_point_step_ms(
|
||||
&node.handle.manifest_rsync_uri,
|
||||
"fresh_objects_processing_lifetime",
|
||||
objects_processing_ms,
|
||||
);
|
||||
let reduce_started = Instant::now();
|
||||
let locked_files = objects_stage.locked_file_count();
|
||||
let reduce_result = reduce_parallel_roa_stage(objects_stage, results, runner.timing.as_ref());
|
||||
let reduce_ms = elapsed_ms(reduce_started);
|
||||
runner.record_publication_point_step_ms(
|
||||
&node.handle.manifest_rsync_uri,
|
||||
"fresh_roa_reduce",
|
||||
reduce_ms,
|
||||
);
|
||||
|
||||
let (result, mut metrics) = match reduce_result {
|
||||
Ok(mut objects) => {
|
||||
@@ -1985,6 +2023,25 @@ fn finalize_publication_point_state(
|
||||
};
|
||||
let finalize_worker_ms = elapsed_ms(finalize_worker_started);
|
||||
metrics.finalize_worker_ms = finalize_worker_ms;
|
||||
runner.record_publication_point_step_ms(
|
||||
&node.handle.manifest_rsync_uri,
|
||||
"fresh_finalize_worker",
|
||||
finalize_worker_ms,
|
||||
);
|
||||
runner.record_publication_point_step_ms(
|
||||
&node.handle.manifest_rsync_uri,
|
||||
"fresh_finalize_queue_wait",
|
||||
finalize_queue_wait_ms.unwrap_or(0),
|
||||
);
|
||||
runner.record_publication_point_step_ms(
|
||||
&node.handle.manifest_rsync_uri,
|
||||
"fresh_finalize",
|
||||
metrics.finalize_ms,
|
||||
);
|
||||
runner.record_publication_point_total_ms(
|
||||
&node.handle.manifest_rsync_uri,
|
||||
started_at.elapsed().as_millis() as u64,
|
||||
);
|
||||
emit_finalize_breakdown(
|
||||
"phase2_finalize_worker_breakdown",
|
||||
node.handle.manifest_rsync_uri.as_str(),
|
||||
@@ -2056,10 +2113,10 @@ fn drain_repo_events(
|
||||
) -> Result<RepoDrainMetrics, TreeRunError> {
|
||||
let started = Instant::now();
|
||||
let mut metrics = RepoDrainMetrics::default();
|
||||
if let Some(event) = repo_runtime
|
||||
.recv_repo_result_timeout(timeout)
|
||||
.map_err(TreeRunError::Runner)?
|
||||
{
|
||||
let events = repo_runtime
|
||||
.drain_repo_results_timeout(timeout, REPO_RESULT_DRAIN_MAX_EVENTS)
|
||||
.map_err(TreeRunError::Runner)?;
|
||||
for event in events {
|
||||
metrics.event_count += 1;
|
||||
metrics.completions += event.completions.len();
|
||||
for completion in event.completions {
|
||||
|
||||
@@ -25,11 +25,12 @@ use crate::replay::archive::ReplayArchiveIndex;
|
||||
use crate::replay::delta_archive::ReplayDeltaArchiveIndex;
|
||||
use crate::report::{RfcRef, Warning};
|
||||
use crate::storage::{
|
||||
PackFile, PackTime, PublicationPointCacheChild, PublicationPointCacheProjection,
|
||||
RawByHashEntry, RocksStore, ValidatedCaInstanceResult, VcirArtifactKind, VcirArtifactRole,
|
||||
VcirArtifactValidationStatus, VcirAuditSummary, VcirCcrManifestProjection, VcirChildEntry,
|
||||
VcirInstanceGate, VcirLocalOutput, VcirLocalOutputPayload, VcirOutputType, VcirRelatedArtifact,
|
||||
VcirReplaceTimingBreakdown, VcirSourceObjectType, VcirSummary,
|
||||
PackFile, PackTime, PublicationPointCacheChild, PublicationPointCacheOutput,
|
||||
PublicationPointCacheProjection, RawByHashEntry, RocksStore, ValidatedCaInstanceResult,
|
||||
VcirArtifactKind, VcirArtifactRole, VcirArtifactValidationStatus, VcirAuditSummary,
|
||||
VcirCcrManifestProjection, VcirChildEntry, VcirInstanceGate, VcirLocalOutput,
|
||||
VcirLocalOutputPayload, VcirOutputType, VcirRelatedArtifact, VcirReplaceTimingBreakdown,
|
||||
VcirSourceObjectType, VcirSummary,
|
||||
};
|
||||
use crate::sync::repo::{
|
||||
sync_publication_point, sync_publication_point_replay, sync_publication_point_replay_delta,
|
||||
@@ -241,7 +242,7 @@ impl<'a> Rpkiv1PublicationPointRunner<'a> {
|
||||
}
|
||||
let projection = match self
|
||||
.store
|
||||
.get_publication_point_cache_projection(&ca.manifest_rsync_uri)
|
||||
.get_publication_point_cache_projection_cached(&ca.manifest_rsync_uri)
|
||||
{
|
||||
Ok(Some(projection)) => projection,
|
||||
Ok(None) => {
|
||||
@@ -526,16 +527,12 @@ impl<'a> Rpkiv1PublicationPointRunner<'a> {
|
||||
let output_reuse_count = projection.outputs.len() as u64;
|
||||
let child_reuse_count = projection.children.len() as u64;
|
||||
let related_object_reuse_count = projection.related_objects.len() as u64;
|
||||
let to_vcir_started = std::time::Instant::now();
|
||||
let mut vcir = projection.to_vcir_for_reuse(self.validation_time);
|
||||
let to_vcir_ms = self.record_publication_point_cache_phase_ms(
|
||||
"publication_point_cache_to_vcir_total",
|
||||
to_vcir_started,
|
||||
);
|
||||
vcir.parent_manifest_rsync_uri = ca.parent_manifest_rsync_uri.clone();
|
||||
let build_objects_started = std::time::Instant::now();
|
||||
let mut objects =
|
||||
build_objects_output_from_vcir(&vcir, self.validation_time, &mut warnings);
|
||||
let mut objects = build_objects_output_from_publication_point_cache_projection(
|
||||
&projection,
|
||||
self.validation_time,
|
||||
&mut warnings,
|
||||
);
|
||||
let build_objects_ms = self.record_publication_point_cache_phase_ms(
|
||||
"publication_point_cache_build_objects_total",
|
||||
build_objects_started,
|
||||
@@ -563,19 +560,18 @@ impl<'a> Rpkiv1PublicationPointRunner<'a> {
|
||||
ccr_append_started,
|
||||
);
|
||||
let audit_build_started = std::time::Instant::now();
|
||||
let audit = build_publication_point_audit_from_vcir(
|
||||
let audit = build_publication_point_audit_from_publication_point_cache_projection(
|
||||
ca,
|
||||
PublicationPointSource::PublicationPointCache,
|
||||
repo_sync_source,
|
||||
repo_sync_phase,
|
||||
Some(repo_sync_duration_ms),
|
||||
repo_sync_err,
|
||||
Some(&vcir),
|
||||
None,
|
||||
&projection,
|
||||
self.validation_time,
|
||||
&warnings,
|
||||
&objects,
|
||||
&child_audits,
|
||||
&[],
|
||||
);
|
||||
let audit_build_ms = self.record_publication_point_cache_phase_ms(
|
||||
"publication_point_cache_audit_build_total",
|
||||
@@ -614,7 +610,6 @@ impl<'a> Rpkiv1PublicationPointRunner<'a> {
|
||||
"children_reused": child_reuse_count,
|
||||
"related_objects_reused": related_object_reuse_count,
|
||||
"audit_objects_reused": audit_object_count,
|
||||
"to_vcir_ms": to_vcir_ms,
|
||||
"build_objects_ms": build_objects_ms,
|
||||
"restore_children_ms": restore_children_ms,
|
||||
"restore_children_workers": child_restore_workers,
|
||||
@@ -649,6 +644,27 @@ impl<'a> Rpkiv1PublicationPointRunner<'a> {
|
||||
nanos / 1_000_000
|
||||
}
|
||||
|
||||
pub(crate) fn record_publication_point_total_ms(&self, manifest_rsync_uri: &str, ms: u64) {
|
||||
if let Some(timing) = self.timing.as_ref() {
|
||||
timing.record_publication_point_nanos(manifest_rsync_uri, ms.saturating_mul(1_000_000));
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn record_publication_point_step_ms(
|
||||
&self,
|
||||
manifest_rsync_uri: &str,
|
||||
step: &'static str,
|
||||
ms: u64,
|
||||
) {
|
||||
if let Some(timing) = self.timing.as_ref() {
|
||||
timing.record_publication_point_step_nanos(
|
||||
manifest_rsync_uri,
|
||||
step,
|
||||
ms.saturating_mul(1_000_000),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn publication_point_cache_child_restore_worker_count(&self) -> usize {
|
||||
self.parallel_phase2_config
|
||||
.as_ref()
|
||||
@@ -772,6 +788,88 @@ impl<'a> Rpkiv1PublicationPointRunner<'a> {
|
||||
error,
|
||||
snapshot_prepare_ms,
|
||||
})?;
|
||||
if let Some(timing) = self.timing.as_ref() {
|
||||
timing.record_phase_nanos(
|
||||
"fresh_snapshot_prepare_total",
|
||||
snapshot_prepare_ms.saturating_mul(1_000_000),
|
||||
);
|
||||
timing.record_phase_nanos(
|
||||
"fresh_snapshot_manifest_load_total",
|
||||
snapshot_prepare_timing
|
||||
.manifest_load_ms
|
||||
.saturating_mul(1_000_000),
|
||||
);
|
||||
timing.record_phase_nanos(
|
||||
"fresh_snapshot_manifest_decode_total",
|
||||
snapshot_prepare_timing
|
||||
.manifest_decode_ms
|
||||
.saturating_mul(1_000_000),
|
||||
);
|
||||
timing.record_phase_nanos(
|
||||
"fresh_snapshot_replay_guard_total",
|
||||
snapshot_prepare_timing
|
||||
.replay_guard_ms
|
||||
.saturating_mul(1_000_000),
|
||||
);
|
||||
timing.record_phase_nanos(
|
||||
"fresh_snapshot_manifest_entries_total",
|
||||
snapshot_prepare_timing
|
||||
.manifest_entries_ms
|
||||
.saturating_mul(1_000_000),
|
||||
);
|
||||
timing.record_phase_nanos(
|
||||
"fresh_snapshot_pack_files_total",
|
||||
snapshot_prepare_timing
|
||||
.pack_files_ms
|
||||
.saturating_mul(1_000_000),
|
||||
);
|
||||
timing.record_phase_nanos(
|
||||
"fresh_snapshot_ee_path_validate_total",
|
||||
snapshot_prepare_timing
|
||||
.ee_path_validate_ms
|
||||
.saturating_mul(1_000_000),
|
||||
);
|
||||
timing.record_count("fresh_publication_points", 1);
|
||||
timing.record_count(
|
||||
"fresh_manifest_files_total",
|
||||
snapshot_prepare_timing.manifest_file_count as u64,
|
||||
);
|
||||
}
|
||||
self.record_publication_point_step_ms(
|
||||
&ca.manifest_rsync_uri,
|
||||
"fresh_snapshot_prepare",
|
||||
snapshot_prepare_ms,
|
||||
);
|
||||
self.record_publication_point_step_ms(
|
||||
&ca.manifest_rsync_uri,
|
||||
"fresh_snapshot_manifest_load",
|
||||
snapshot_prepare_timing.manifest_load_ms,
|
||||
);
|
||||
self.record_publication_point_step_ms(
|
||||
&ca.manifest_rsync_uri,
|
||||
"fresh_snapshot_manifest_decode",
|
||||
snapshot_prepare_timing.manifest_decode_ms,
|
||||
);
|
||||
self.record_publication_point_step_ms(
|
||||
&ca.manifest_rsync_uri,
|
||||
"fresh_snapshot_replay_guard",
|
||||
snapshot_prepare_timing.replay_guard_ms,
|
||||
);
|
||||
self.record_publication_point_step_ms(
|
||||
&ca.manifest_rsync_uri,
|
||||
"fresh_snapshot_manifest_entries",
|
||||
snapshot_prepare_timing.manifest_entries_ms,
|
||||
);
|
||||
self.record_publication_point_step_ms(
|
||||
&ca.manifest_rsync_uri,
|
||||
"fresh_snapshot_pack_files",
|
||||
snapshot_prepare_timing.pack_files_ms,
|
||||
);
|
||||
self.record_publication_point_step_ms(
|
||||
&ca.manifest_rsync_uri,
|
||||
"fresh_snapshot_ee_path_validate",
|
||||
snapshot_prepare_timing.ee_path_validate_ms,
|
||||
);
|
||||
|
||||
let child_discovery_started = std::time::Instant::now();
|
||||
let out = {
|
||||
@@ -800,6 +898,26 @@ impl<'a> Rpkiv1PublicationPointRunner<'a> {
|
||||
),
|
||||
};
|
||||
let child_discovery_ms = child_discovery_started.elapsed().as_millis() as u64;
|
||||
if let Some(timing) = self.timing.as_ref() {
|
||||
timing.record_phase_nanos(
|
||||
"fresh_child_discovery_total",
|
||||
child_discovery_ms.saturating_mul(1_000_000),
|
||||
);
|
||||
timing.record_count(
|
||||
"fresh_children_discovered",
|
||||
discovered_children.len() as u64,
|
||||
);
|
||||
timing.record_count("fresh_child_audits", child_audits.len() as u64);
|
||||
timing.record_count(
|
||||
"fresh_router_keys_discovered",
|
||||
discovered_router_keys.len() as u64,
|
||||
);
|
||||
}
|
||||
self.record_publication_point_step_ms(
|
||||
&ca.manifest_rsync_uri,
|
||||
"fresh_child_discovery",
|
||||
child_discovery_ms,
|
||||
);
|
||||
|
||||
Ok(FreshPublicationPointStage {
|
||||
fresh_point,
|
||||
@@ -829,6 +947,17 @@ impl<'a> Rpkiv1PublicationPointRunner<'a> {
|
||||
let snapshot_pack_started = std::time::Instant::now();
|
||||
let pack = fresh_point.to_publication_point_snapshot();
|
||||
let snapshot_pack_ms = snapshot_pack_started.elapsed().as_millis() as u64;
|
||||
if let Some(timing) = self.timing.as_ref() {
|
||||
timing.record_phase_nanos(
|
||||
"fresh_snapshot_pack_total",
|
||||
snapshot_pack_ms.saturating_mul(1_000_000),
|
||||
);
|
||||
}
|
||||
self.record_publication_point_step_ms(
|
||||
&ca.manifest_rsync_uri,
|
||||
"fresh_snapshot_pack",
|
||||
snapshot_pack_ms,
|
||||
);
|
||||
|
||||
let persist_vcir_started = std::time::Instant::now();
|
||||
let persist_vcir_timing = if self.persist_vcir {
|
||||
@@ -850,6 +979,64 @@ impl<'a> Rpkiv1PublicationPointRunner<'a> {
|
||||
PersistVcirTimingBreakdown::default()
|
||||
};
|
||||
let persist_vcir_ms = persist_vcir_started.elapsed().as_millis() as u64;
|
||||
if let Some(timing) = self.timing.as_ref() {
|
||||
timing.record_phase_nanos(
|
||||
"fresh_persist_vcir_total",
|
||||
persist_vcir_ms.saturating_mul(1_000_000),
|
||||
);
|
||||
timing.record_phase_nanos(
|
||||
"fresh_persist_embedded_store_total",
|
||||
persist_vcir_timing
|
||||
.embedded_store_ms
|
||||
.saturating_mul(1_000_000),
|
||||
);
|
||||
timing.record_phase_nanos(
|
||||
"fresh_persist_build_vcir_total",
|
||||
persist_vcir_timing.build_vcir_ms.saturating_mul(1_000_000),
|
||||
);
|
||||
timing.record_phase_nanos(
|
||||
"fresh_persist_replace_vcir_total",
|
||||
persist_vcir_timing
|
||||
.replace_vcir_ms
|
||||
.saturating_mul(1_000_000),
|
||||
);
|
||||
timing.record_phase_nanos(
|
||||
"fresh_persist_local_outputs_total",
|
||||
persist_vcir_timing
|
||||
.build_vcir
|
||||
.local_outputs_ms
|
||||
.saturating_mul(1_000_000),
|
||||
);
|
||||
timing.record_phase_nanos(
|
||||
"fresh_persist_child_entries_total",
|
||||
persist_vcir_timing
|
||||
.build_vcir
|
||||
.child_entries_ms
|
||||
.saturating_mul(1_000_000),
|
||||
);
|
||||
timing.record_phase_nanos(
|
||||
"fresh_persist_related_artifacts_total",
|
||||
persist_vcir_timing
|
||||
.build_vcir
|
||||
.related_artifacts_ms
|
||||
.saturating_mul(1_000_000),
|
||||
);
|
||||
}
|
||||
self.record_publication_point_step_ms(
|
||||
&ca.manifest_rsync_uri,
|
||||
"fresh_persist_vcir",
|
||||
persist_vcir_ms,
|
||||
);
|
||||
self.record_publication_point_step_ms(
|
||||
&ca.manifest_rsync_uri,
|
||||
"fresh_persist_build_vcir",
|
||||
persist_vcir_timing.build_vcir_ms,
|
||||
);
|
||||
self.record_publication_point_step_ms(
|
||||
&ca.manifest_rsync_uri,
|
||||
"fresh_persist_replace_vcir",
|
||||
persist_vcir_timing.replace_vcir_ms,
|
||||
);
|
||||
|
||||
// local_outputs_cache only exists to build/persist VCIR. Release it before the
|
||||
// publication point result is retained for the rest of the run.
|
||||
@@ -868,6 +1055,26 @@ impl<'a> Rpkiv1PublicationPointRunner<'a> {
|
||||
self.append_ccr_manifest_projection(&ccr_manifest_projection)?;
|
||||
ccr_append_ms = ccr_append_started.elapsed().as_millis() as u64;
|
||||
}
|
||||
if let Some(timing) = self.timing.as_ref() {
|
||||
timing.record_phase_nanos(
|
||||
"fresh_ccr_projection_build_total",
|
||||
ccr_projection_build_ms.saturating_mul(1_000_000),
|
||||
);
|
||||
timing.record_phase_nanos(
|
||||
"fresh_ccr_append_total",
|
||||
ccr_append_ms.saturating_mul(1_000_000),
|
||||
);
|
||||
}
|
||||
self.record_publication_point_step_ms(
|
||||
&ca.manifest_rsync_uri,
|
||||
"fresh_ccr_projection_build",
|
||||
ccr_projection_build_ms,
|
||||
);
|
||||
self.record_publication_point_step_ms(
|
||||
&ca.manifest_rsync_uri,
|
||||
"fresh_ccr_append",
|
||||
ccr_append_ms,
|
||||
);
|
||||
|
||||
let audit_build_started = std::time::Instant::now();
|
||||
let audit = build_publication_point_audit_from_snapshot(
|
||||
@@ -883,6 +1090,17 @@ impl<'a> Rpkiv1PublicationPointRunner<'a> {
|
||||
&child_audits,
|
||||
);
|
||||
let audit_build_ms = audit_build_started.elapsed().as_millis() as u64;
|
||||
if let Some(timing) = self.timing.as_ref() {
|
||||
timing.record_phase_nanos(
|
||||
"fresh_audit_build_total",
|
||||
audit_build_ms.saturating_mul(1_000_000),
|
||||
);
|
||||
}
|
||||
self.record_publication_point_step_ms(
|
||||
&ca.manifest_rsync_uri,
|
||||
"fresh_audit_build",
|
||||
audit_build_ms,
|
||||
);
|
||||
|
||||
Ok(FreshPublicationPointFinalizeOutput {
|
||||
result: PublicationPointRunResult {
|
||||
@@ -1296,6 +1514,11 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
|
||||
}
|
||||
};
|
||||
let objects_processing_ms = objects_processing_started.elapsed().as_millis() as u64;
|
||||
self.record_publication_point_step_ms(
|
||||
&ca.manifest_rsync_uri,
|
||||
"fresh_objects_processing",
|
||||
objects_processing_ms,
|
||||
);
|
||||
|
||||
objects.router_keys.extend(discovered_router_keys);
|
||||
objects
|
||||
@@ -2741,6 +2964,98 @@ fn build_publication_point_audit_from_vcir(
|
||||
}
|
||||
}
|
||||
|
||||
fn build_publication_point_audit_from_publication_point_cache_projection(
|
||||
ca: &CaInstanceHandle,
|
||||
source: PublicationPointSource,
|
||||
repo_sync_source: Option<&str>,
|
||||
repo_sync_phase: Option<&str>,
|
||||
repo_sync_duration_ms: Option<u64>,
|
||||
repo_sync_error: Option<&str>,
|
||||
projection: &PublicationPointCacheProjection,
|
||||
validation_time: time::OffsetDateTime,
|
||||
runner_warnings: &[Warning],
|
||||
objects: &crate::validation::objects::ObjectsOutput,
|
||||
child_audits: &[ObjectAuditEntry],
|
||||
) -> PublicationPointAudit {
|
||||
let mut warnings = Vec::new();
|
||||
warnings.extend(runner_warnings.iter().map(AuditWarning::from));
|
||||
warnings.extend(objects.warnings.iter().map(AuditWarning::from));
|
||||
|
||||
let mut audit_by_uri: HashMap<String, ObjectAuditEntry> = HashMap::new();
|
||||
for artifact in &projection.related_objects {
|
||||
let Some(uri) = artifact.uri.as_ref() else {
|
||||
continue;
|
||||
};
|
||||
audit_by_uri.insert(
|
||||
uri.clone(),
|
||||
ObjectAuditEntry {
|
||||
rsync_uri: uri.clone(),
|
||||
sha256_hex: artifact.sha256.clone(),
|
||||
kind: kind_from_vcir_artifact_kind(artifact.artifact_kind),
|
||||
result: audit_result_from_vcir_status(artifact.validation_status),
|
||||
detail: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
for entry in child_audits {
|
||||
audit_by_uri.insert(entry.rsync_uri.clone(), entry.clone());
|
||||
}
|
||||
for entry in &objects.audit {
|
||||
audit_by_uri.insert(entry.rsync_uri.clone(), entry.clone());
|
||||
}
|
||||
|
||||
let mut ordered_uris: Vec<String> = projection
|
||||
.related_objects
|
||||
.iter()
|
||||
.filter_map(|artifact| artifact.uri.clone())
|
||||
.collect();
|
||||
ordered_uris.sort();
|
||||
ordered_uris.dedup();
|
||||
|
||||
let mut objects_out: Vec<ObjectAuditEntry> = Vec::with_capacity(ordered_uris.len().max(1));
|
||||
if let Some(entry) = audit_by_uri.remove(&projection.manifest_rsync_uri) {
|
||||
objects_out.push(entry);
|
||||
} else {
|
||||
objects_out.push(ObjectAuditEntry {
|
||||
rsync_uri: projection.manifest_rsync_uri.clone(),
|
||||
sha256_hex: hex::encode(projection.manifest_sha256),
|
||||
kind: AuditObjectKind::Manifest,
|
||||
result: AuditObjectResult::Ok,
|
||||
detail: None,
|
||||
});
|
||||
}
|
||||
|
||||
for uri in ordered_uris {
|
||||
if uri == projection.manifest_rsync_uri {
|
||||
continue;
|
||||
}
|
||||
if let Some(entry) = audit_by_uri.remove(&uri) {
|
||||
objects_out.push(entry);
|
||||
}
|
||||
}
|
||||
|
||||
PublicationPointAudit {
|
||||
node_id: None,
|
||||
parent_node_id: None,
|
||||
discovered_from: None,
|
||||
rsync_base_uri: ca.rsync_base_uri.clone(),
|
||||
manifest_rsync_uri: ca.manifest_rsync_uri.clone(),
|
||||
publication_point_rsync_uri: ca.publication_point_rsync_uri.clone(),
|
||||
rrdp_notification_uri: ca.rrdp_notification_uri.clone(),
|
||||
source: source_label(source),
|
||||
repo_sync_source: repo_sync_source.map(ToString::to_string),
|
||||
repo_sync_phase: repo_sync_phase.map(ToString::to_string),
|
||||
repo_sync_duration_ms,
|
||||
repo_sync_error: repo_sync_error.map(ToString::to_string),
|
||||
repo_terminal_state: terminal_state_label(source).to_string(),
|
||||
this_update_rfc3339_utc: projection.manifest_this_update.rfc3339_utc.clone(),
|
||||
next_update_rfc3339_utc: projection.manifest_next_update.rfc3339_utc.clone(),
|
||||
verified_at_rfc3339_utc: PackTime::from_utc_offset_datetime(validation_time).rfc3339_utc,
|
||||
warnings,
|
||||
objects: objects_out,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_snapshot_time_value(pack_time: &PackTime) -> Result<time::OffsetDateTime, String> {
|
||||
time::OffsetDateTime::parse(
|
||||
&pack_time.rfc3339_utc,
|
||||
@@ -3160,6 +3475,202 @@ fn build_objects_output_from_vcir(
|
||||
output
|
||||
}
|
||||
|
||||
fn build_objects_output_from_publication_point_cache_projection(
|
||||
projection: &PublicationPointCacheProjection,
|
||||
validation_time: time::OffsetDateTime,
|
||||
warnings: &mut Vec<Warning>,
|
||||
) -> crate::validation::objects::ObjectsOutput {
|
||||
let mut output = empty_objects_output();
|
||||
let mut audit_by_uri: HashMap<String, ObjectAuditEntry> = HashMap::new();
|
||||
let mut roa_total: HashSet<String> = HashSet::new();
|
||||
let mut aspa_total: HashSet<String> = HashSet::new();
|
||||
let mut roa_ok: HashSet<String> = HashSet::new();
|
||||
let mut aspa_ok: HashSet<String> = HashSet::new();
|
||||
|
||||
for artifact in &projection.related_objects {
|
||||
if artifact.artifact_role != VcirArtifactRole::SignedObject {
|
||||
continue;
|
||||
}
|
||||
if let Some(uri) = artifact.uri.as_ref() {
|
||||
match artifact.artifact_kind {
|
||||
VcirArtifactKind::Roa => {
|
||||
roa_total.insert(uri.clone());
|
||||
}
|
||||
VcirArtifactKind::Aspa => {
|
||||
aspa_total.insert(uri.clone());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for projected in &projection.outputs {
|
||||
let effective_until = match parse_snapshot_time_value(&projected.item_effective_until) {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
warnings.push(
|
||||
Warning::new(format!(
|
||||
"publication-point cached local output has invalid item_effective_until: {err}"
|
||||
))
|
||||
.with_context(&projected.source_object_uri),
|
||||
);
|
||||
audit_by_uri.insert(
|
||||
projected.source_object_uri.clone(),
|
||||
ObjectAuditEntry {
|
||||
rsync_uri: projected.source_object_uri.clone(),
|
||||
sha256_hex: hex::encode(projected.source_object_hash),
|
||||
kind: audit_kind_for_vcir_output_type(projected.output_type),
|
||||
result: AuditObjectResult::Error,
|
||||
detail: Some(
|
||||
"publication-point cached local output has invalid item_effective_until"
|
||||
.to_string(),
|
||||
),
|
||||
},
|
||||
);
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if validation_time > effective_until {
|
||||
audit_by_uri
|
||||
.entry(projected.source_object_uri.clone())
|
||||
.or_insert_with(|| ObjectAuditEntry {
|
||||
rsync_uri: projected.source_object_uri.clone(),
|
||||
sha256_hex: hex::encode(projected.source_object_hash),
|
||||
kind: audit_kind_for_vcir_output_type(projected.output_type),
|
||||
result: AuditObjectResult::Skipped,
|
||||
detail: Some(
|
||||
"skipped: publication-point cached local output expired".to_string(),
|
||||
),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
match projected.output_type {
|
||||
VcirOutputType::Vrp => match parse_publication_point_cache_vrp_output(projected) {
|
||||
Ok(vrp) => {
|
||||
roa_ok.insert(projected.source_object_uri.clone());
|
||||
output.vrps.push(vrp);
|
||||
audit_by_uri.insert(
|
||||
projected.source_object_uri.clone(),
|
||||
ObjectAuditEntry {
|
||||
rsync_uri: projected.source_object_uri.clone(),
|
||||
sha256_hex: hex::encode(projected.source_object_hash),
|
||||
kind: AuditObjectKind::Roa,
|
||||
result: AuditObjectResult::Ok,
|
||||
detail: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
warnings.push(
|
||||
Warning::new(format!(
|
||||
"publication-point cached ROA local output parse failed: {err}"
|
||||
))
|
||||
.with_context(&projected.source_object_uri),
|
||||
);
|
||||
audit_by_uri.insert(
|
||||
projected.source_object_uri.clone(),
|
||||
ObjectAuditEntry {
|
||||
rsync_uri: projected.source_object_uri.clone(),
|
||||
sha256_hex: hex::encode(projected.source_object_hash),
|
||||
kind: AuditObjectKind::Roa,
|
||||
result: AuditObjectResult::Error,
|
||||
detail: Some(format!(
|
||||
"publication-point cached ROA local output parse failed: {err}"
|
||||
)),
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
VcirOutputType::Aspa => match parse_publication_point_cache_aspa_output(projected) {
|
||||
Ok(aspa) => {
|
||||
aspa_ok.insert(projected.source_object_uri.clone());
|
||||
output.aspas.push(aspa);
|
||||
audit_by_uri.insert(
|
||||
projected.source_object_uri.clone(),
|
||||
ObjectAuditEntry {
|
||||
rsync_uri: projected.source_object_uri.clone(),
|
||||
sha256_hex: hex::encode(projected.source_object_hash),
|
||||
kind: AuditObjectKind::Aspa,
|
||||
result: AuditObjectResult::Ok,
|
||||
detail: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
warnings.push(
|
||||
Warning::new(format!(
|
||||
"publication-point cached ASPA local output parse failed: {err}"
|
||||
))
|
||||
.with_context(&projected.source_object_uri),
|
||||
);
|
||||
audit_by_uri.insert(
|
||||
projected.source_object_uri.clone(),
|
||||
ObjectAuditEntry {
|
||||
rsync_uri: projected.source_object_uri.clone(),
|
||||
sha256_hex: hex::encode(projected.source_object_hash),
|
||||
kind: AuditObjectKind::Aspa,
|
||||
result: AuditObjectResult::Error,
|
||||
detail: Some(format!(
|
||||
"publication-point cached ASPA local output parse failed: {err}"
|
||||
)),
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
VcirOutputType::RouterKey => {
|
||||
match parse_publication_point_cache_router_key_output(projected) {
|
||||
Ok(router_key) => {
|
||||
output.router_keys.push(router_key);
|
||||
audit_by_uri.insert(
|
||||
projected.source_object_uri.clone(),
|
||||
ObjectAuditEntry {
|
||||
rsync_uri: projected.source_object_uri.clone(),
|
||||
sha256_hex: hex::encode(projected.source_object_hash),
|
||||
kind: AuditObjectKind::RouterCertificate,
|
||||
result: AuditObjectResult::Ok,
|
||||
detail: Some(
|
||||
"publication-point cached Router Key local output restored"
|
||||
.to_string(),
|
||||
),
|
||||
},
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
warnings.push(
|
||||
Warning::new(format!(
|
||||
"publication-point cached Router Key local output parse failed: {err}"
|
||||
))
|
||||
.with_context(&projected.source_object_uri),
|
||||
);
|
||||
audit_by_uri.insert(
|
||||
projected.source_object_uri.clone(),
|
||||
ObjectAuditEntry {
|
||||
rsync_uri: projected.source_object_uri.clone(),
|
||||
sha256_hex: hex::encode(projected.source_object_hash),
|
||||
kind: AuditObjectKind::RouterCertificate,
|
||||
result: AuditObjectResult::Error,
|
||||
detail: Some(format!(
|
||||
"publication-point cached Router Key local output parse failed: {err}"
|
||||
)),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output.stats.roa_total = roa_total.len();
|
||||
output.stats.roa_ok = roa_ok.len();
|
||||
output.stats.aspa_total = aspa_total.len();
|
||||
output.stats.aspa_ok = aspa_ok.len();
|
||||
let mut audit: Vec<_> = audit_by_uri.into_values().collect();
|
||||
audit.sort_by(|left, right| left.rsync_uri.cmp(&right.rsync_uri));
|
||||
output.audit = audit;
|
||||
output
|
||||
}
|
||||
|
||||
fn parse_vcir_vrp_output(local: &VcirLocalOutput) -> Result<Vrp, String> {
|
||||
match &local.payload {
|
||||
VcirLocalOutputPayload::Vrp {
|
||||
@@ -3213,6 +3724,65 @@ fn parse_vcir_router_key_output(local: &VcirLocalOutput) -> Result<RouterKeyPayl
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_publication_point_cache_vrp_output(
|
||||
projected: &PublicationPointCacheOutput,
|
||||
) -> Result<Vrp, String> {
|
||||
match &projected.payload {
|
||||
VcirLocalOutputPayload::Vrp {
|
||||
asn,
|
||||
afi,
|
||||
prefix_len,
|
||||
addr,
|
||||
max_length,
|
||||
} => Ok(Vrp {
|
||||
asn: *asn,
|
||||
prefix: crate::data_model::roa::IpPrefix {
|
||||
afi: *afi,
|
||||
prefix_len: *prefix_len,
|
||||
addr: *addr,
|
||||
},
|
||||
max_length: *max_length,
|
||||
}),
|
||||
_ => Err("publication-point cache output payload is not VRP".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_publication_point_cache_aspa_output(
|
||||
projected: &PublicationPointCacheOutput,
|
||||
) -> Result<AspaAttestation, String> {
|
||||
match &projected.payload {
|
||||
VcirLocalOutputPayload::Aspa {
|
||||
customer_as_id,
|
||||
provider_as_ids,
|
||||
} => Ok(AspaAttestation {
|
||||
customer_as_id: *customer_as_id,
|
||||
provider_as_ids: provider_as_ids.clone(),
|
||||
}),
|
||||
_ => Err("publication-point cache output payload is not ASPA".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_publication_point_cache_router_key_output(
|
||||
projected: &PublicationPointCacheOutput,
|
||||
) -> Result<RouterKeyPayload, String> {
|
||||
match &projected.payload {
|
||||
VcirLocalOutputPayload::RouterKey {
|
||||
as_id,
|
||||
ski,
|
||||
spki_der,
|
||||
} => Ok(RouterKeyPayload {
|
||||
as_id: *as_id,
|
||||
ski: ski.clone(),
|
||||
spki_der: spki_der.clone(),
|
||||
source_object_uri: projected.source_object_uri.clone(),
|
||||
source_object_hash: hex::encode(projected.source_object_hash),
|
||||
source_ee_cert_hash: hex::encode(projected.source_ee_cert_hash),
|
||||
item_effective_until: projected.item_effective_until.clone(),
|
||||
}),
|
||||
_ => Err("publication-point cache output payload is not Router Key".to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
fn restore_children_from_vcir(
|
||||
store: &RocksStore,
|
||||
ca: &CaInstanceHandle,
|
||||
|
||||
Reference in New Issue
Block a user