20260416_2 并行优化phase1后进行snapshot fast path优化,通过四方面关键优化技术消除了验证主链路上snapshot构建是的db访问性能热点问题,性能热点转移到ROA验证处理本身,目前APNIC+ARIN全量同步从500秒压缩到212秒,离rpkclient 112秒差距变小

This commit is contained in:
2026-04-17 14:58:47 +08:00
parent 38421b1ae7
commit 224ae10052
36 changed files with 2935 additions and 937 deletions

View File

@@ -1,4 +1,5 @@
use crate::blob_store::RawObjectStore;
use crate::current_repo_index::CurrentRepoIndexHandle;
use crate::data_model::manifest::{ManifestDecodeError, ManifestObject, ManifestValidateError};
use crate::data_model::signed_object::SignedObjectVerifyError;
use crate::policy::{CaFailedFetchPolicy, Policy};
@@ -299,11 +300,53 @@ pub fn process_manifest_publication_point_fresh_after_repo_sync(
repo_sync_ok: bool,
repo_sync_error: Option<&str>,
) -> Result<FreshValidatedPublicationPoint, ManifestFreshError> {
process_manifest_publication_point_fresh_after_repo_sync_with_timing(
store,
manifest_rsync_uri,
publication_point_rsync_uri,
None,
issuer_ca_der,
issuer_ca_rsync_uri,
validation_time,
repo_sync_ok,
repo_sync_error,
)
.map(|(fresh, _timing)| fresh)
}
#[derive(Clone, Debug, Default)]
pub struct FreshPublicationPointTimingBreakdown {
pub manifest_load_ms: u64,
pub manifest_decode_ms: u64,
pub replay_guard_ms: u64,
pub manifest_entries_ms: u64,
pub pack_files_ms: u64,
pub ee_path_validate_ms: u64,
}
pub fn process_manifest_publication_point_fresh_after_repo_sync_with_timing(
store: &RocksStore,
manifest_rsync_uri: &str,
publication_point_rsync_uri: &str,
current_repo_index: Option<&CurrentRepoIndexHandle>,
issuer_ca_der: &[u8],
issuer_ca_rsync_uri: Option<&str>,
validation_time: time::OffsetDateTime,
repo_sync_ok: bool,
repo_sync_error: Option<&str>,
) -> Result<
(
FreshValidatedPublicationPoint,
FreshPublicationPointTimingBreakdown,
),
ManifestFreshError,
> {
if repo_sync_ok {
try_build_fresh_publication_point(
try_build_fresh_publication_point_with_timing(
store,
manifest_rsync_uri,
publication_point_rsync_uri,
current_repo_index,
issuer_ca_der,
issuer_ca_rsync_uri,
validation_time,
@@ -469,11 +512,11 @@ pub fn load_current_instance_vcir_publication_point(
if !seen.insert(uri.clone()) {
continue;
}
let entry_bytes = store
.get_blob_bytes(&artifact.sha256)?
.ok_or_else(|| ManifestReuseError::MissingArtifactRaw {
let entry_bytes = store.get_blob_bytes(&artifact.sha256)?.ok_or_else(|| {
ManifestReuseError::MissingArtifactRaw {
rsync_uri: uri.clone(),
})?;
}
})?;
files.push(PackFile::from_bytes_compute_sha256(uri, entry_bytes));
}
@@ -541,6 +584,36 @@ pub(crate) fn try_build_fresh_publication_point(
issuer_ca_rsync_uri: Option<&str>,
validation_time: time::OffsetDateTime,
) -> Result<FreshValidatedPublicationPoint, ManifestFreshError> {
try_build_fresh_publication_point_with_timing(
store,
manifest_rsync_uri,
publication_point_rsync_uri,
None,
issuer_ca_der,
issuer_ca_rsync_uri,
validation_time,
)
.map(|(fresh, _timing)| fresh)
}
pub(crate) fn try_build_fresh_publication_point_with_timing(
store: &RocksStore,
manifest_rsync_uri: &str,
publication_point_rsync_uri: &str,
current_repo_index: Option<&CurrentRepoIndexHandle>,
issuer_ca_der: &[u8],
issuer_ca_rsync_uri: Option<&str>,
validation_time: time::OffsetDateTime,
) -> Result<
(
FreshValidatedPublicationPoint,
FreshPublicationPointTimingBreakdown,
),
ManifestFreshError,
> {
let mut timing = FreshPublicationPointTimingBreakdown::default();
let current_index_guard = current_repo_index.and_then(|handle| handle.lock().ok());
if !rsync_uri_is_under_publication_point(manifest_rsync_uri, publication_point_rsync_uri) {
return Err(ManifestFreshError::ManifestOutsidePublicationPoint {
manifest_rsync_uri: manifest_rsync_uri.to_string(),
@@ -548,17 +621,37 @@ pub(crate) fn try_build_fresh_publication_point(
});
}
let manifest_bytes = store
.load_current_object_bytes_by_uri(manifest_rsync_uri)
.map_err(|e| ManifestFreshError::MissingManifest {
manifest_rsync_uri: format!("{manifest_rsync_uri} ({e})"),
})?
.ok_or_else(|| ManifestFreshError::MissingManifest {
manifest_rsync_uri: manifest_rsync_uri.to_string(),
let manifest_load_started = std::time::Instant::now();
let manifest_bytes = if let Some(index) = current_index_guard.as_ref() {
let current = index.get_by_uri(manifest_rsync_uri).ok_or_else(|| {
ManifestFreshError::MissingManifest {
manifest_rsync_uri: manifest_rsync_uri.to_string(),
}
})?;
store
.get_blob_bytes(&current.current_hash_hex)
.map_err(|e| ManifestFreshError::MissingManifest {
manifest_rsync_uri: format!("{manifest_rsync_uri} ({e})"),
})?
.ok_or_else(|| ManifestFreshError::MissingManifest {
manifest_rsync_uri: manifest_rsync_uri.to_string(),
})?
} else {
store
.load_current_object_bytes_by_uri(manifest_rsync_uri)
.map_err(|e| ManifestFreshError::MissingManifest {
manifest_rsync_uri: format!("{manifest_rsync_uri} ({e})"),
})?
.ok_or_else(|| ManifestFreshError::MissingManifest {
manifest_rsync_uri: manifest_rsync_uri.to_string(),
})?
};
timing.manifest_load_ms = manifest_load_started.elapsed().as_millis() as u64;
let manifest_decode_started = std::time::Instant::now();
let manifest =
decode_and_validate_manifest_with_current_time(&manifest_bytes, validation_time)?;
timing.manifest_decode_ms = manifest_decode_started.elapsed().as_millis() as u64;
let this_update = manifest
.manifest
@@ -582,6 +675,7 @@ pub(crate) fn try_build_fresh_publication_point(
// - If manifestNumber is lower, treat as rollback and reject.
// - If manifestNumber is higher, require thisUpdate to be more recent than the previously
// validated thisUpdate.
let replay_guard_started = std::time::Instant::now();
if let Some(old_vcir) = store.get_vcir(manifest_rsync_uri).ok().flatten() {
if old_vcir.manifest_rsync_uri == manifest_rsync_uri {
let new_num = manifest.manifest.manifest_number.bytes_be.as_slice();
@@ -633,34 +727,79 @@ pub(crate) fn try_build_fresh_publication_point(
}
}
}
timing.replay_guard_ms = replay_guard_started.elapsed().as_millis() as u64;
let manifest_entries_started = std::time::Instant::now();
let entries = manifest
.manifest
.parse_files()
.map_err(ManifestDecodeError::Validate)?;
timing.manifest_entries_ms = manifest_entries_started.elapsed().as_millis() as u64;
let mut files = Vec::with_capacity(manifest.manifest.file_count());
let pack_files_started = std::time::Instant::now();
let external_raw_store = store.external_raw_store_ref().cloned().map(std::sync::Arc::new);
for entry in &entries {
let rsync_uri =
join_rsync_dir_and_file(publication_point_rsync_uri, entry.file_name.as_str());
let bytes = store
.load_current_object_bytes_by_uri(&rsync_uri)
.map_err(|_e| ManifestFreshError::MissingFile {
rsync_uri: rsync_uri.clone(),
})?
.ok_or_else(|| ManifestFreshError::MissingFile {
rsync_uri: rsync_uri.clone(),
})?;
let current_object = if let Some(index) = current_index_guard.as_ref() {
let current =
index
.get_by_uri(&rsync_uri)
.ok_or_else(|| ManifestFreshError::MissingFile {
rsync_uri: rsync_uri.clone(),
})?;
crate::storage::CurrentObjectWithHash {
current_hash_hex: current.current_hash_hex.clone(),
current_hash: current.current_hash,
bytes: Vec::new(),
}
} else {
store
.load_current_object_with_hash_by_uri(&rsync_uri)
.map_err(|_e| ManifestFreshError::MissingFile {
rsync_uri: rsync_uri.clone(),
})?
.ok_or_else(|| ManifestFreshError::MissingFile {
rsync_uri: rsync_uri.clone(),
})?
};
let computed = sha2::Sha256::digest(&bytes);
if computed.as_slice() != entry.hash_bytes.as_ref() {
if current_object.current_hash != entry.hash_bytes {
return Err(ManifestFreshError::HashMismatch { rsync_uri });
}
files.push(PackFile::from_bytes_compute_sha256(rsync_uri, bytes));
if let (Some(_), Some(raw_store)) = (current_index_guard.as_ref(), external_raw_store.as_ref()) {
files.push(PackFile::from_lazy_external_raw_store(
rsync_uri,
current_object.current_hash_hex,
current_object.current_hash,
raw_store.clone(),
));
} else {
let bytes = if current_object.bytes.is_empty() {
store
.get_blob_bytes(&current_object.current_hash_hex)
.map_err(|_e| ManifestFreshError::MissingFile {
rsync_uri: rsync_uri.clone(),
})?
.ok_or_else(|| ManifestFreshError::MissingFile {
rsync_uri: rsync_uri.clone(),
})?
} else {
current_object.bytes
};
files.push(PackFile::from_bytes_with_sha256(
rsync_uri,
bytes,
current_object.current_hash,
));
}
}
timing.pack_files_ms = pack_files_started.elapsed().as_millis() as u64;
// RFC 6488 §3: manifest (signed object) validity includes a valid EE cert path.
// We validate this after §6.4/§6.5 so the issuer CRL can be selected from the publication point.
let ee_path_validate_started = std::time::Instant::now();
validate_manifest_embedded_ee_cert_path(
&manifest,
&files,
@@ -668,17 +807,21 @@ pub(crate) fn try_build_fresh_publication_point(
issuer_ca_rsync_uri,
validation_time,
)?;
timing.ee_path_validate_ms = ee_path_validate_started.elapsed().as_millis() as u64;
Ok(FreshValidatedPublicationPoint {
manifest_rsync_uri: manifest_rsync_uri.to_string(),
publication_point_rsync_uri: publication_point_rsync_uri.to_string(),
manifest_number_be: manifest.manifest.manifest_number.bytes_be.clone(),
this_update: PackTime::from_utc_offset_datetime(this_update),
next_update: PackTime::from_utc_offset_datetime(next_update),
verified_at: PackTime::from_utc_offset_datetime(now),
manifest_bytes,
files,
})
Ok((
FreshValidatedPublicationPoint {
manifest_rsync_uri: manifest_rsync_uri.to_string(),
publication_point_rsync_uri: publication_point_rsync_uri.to_string(),
manifest_number_be: manifest.manifest.manifest_number.bytes_be.clone(),
this_update: PackTime::from_utc_offset_datetime(this_update),
next_update: PackTime::from_utc_offset_datetime(next_update),
verified_at: PackTime::from_utc_offset_datetime(now),
manifest_bytes,
files,
},
timing,
))
}
fn cmp_minimal_be_unsigned(a: &[u8], b: &[u8]) -> Ordering {
@@ -735,10 +878,13 @@ fn validate_manifest_embedded_ee_cert_path(
for u in crldp_uris {
let s = u.as_str();
if let Some(f) = crl_files.iter().find(|f| f.rsync_uri == s) {
let crl_bytes = f
.bytes()
.map_err(|e| ManifestFreshError::MissingFile { rsync_uri: format!("{s} ({e})") })?;
let _validated = validate_ee_cert_path(
ee_der,
issuer_ca_der,
f.bytes.as_slice(),
crl_bytes,
issuer_ca_rsync_uri,
Some(f.rsync_uri.as_str()),
validation_time,
@@ -759,6 +905,7 @@ fn validate_manifest_embedded_ee_cert_path(
#[cfg(test)]
mod tests {
use super::*;
use crate::current_repo_index::CurrentRepoIndex;
use crate::data_model::manifest::ManifestObject;
use crate::storage::{
PackFile, PackTime, RawByHashEntry, RocksStore, ValidatedCaInstanceResult,
@@ -853,6 +1000,12 @@ mod tests {
.expect("put repository view entry");
}
fn put_raw_only(store: &RocksStore, rsync_uri: &str, bytes: Vec<u8>, object_type: &str) {
store
.put_raw_by_hash_entry(&raw_by_hash_entry(rsync_uri, bytes, object_type))
.expect("put raw_by_hash entry");
}
fn sample_current_instance_vcir(
manifest_rsync_uri: &str,
publication_point_rsync_uri: &str,
@@ -1034,6 +1187,132 @@ mod tests {
);
}
#[test]
fn try_build_fresh_publication_point_detects_hash_mismatch_via_repository_view_hash() {
let temp = tempfile::tempdir().expect("tempdir");
let store = RocksStore::open(temp.path()).expect("open rocksdb");
let (
manifest,
manifest_bytes,
manifest_rsync_uri,
publication_point_rsync_uri,
validation_time,
) = load_manifest_fixture();
put_current_object(&store, &manifest_rsync_uri, manifest_bytes, "mft");
let non_crl_entries = manifest
.manifest
.parse_files()
.expect("parse files")
.into_iter()
.filter(|entry| !entry.file_name.ends_with(".crl"))
.collect::<Vec<_>>();
let first = &non_crl_entries[0];
let second = &non_crl_entries[1];
let first_uri = format!("{publication_point_rsync_uri}{}", first.file_name);
let second_path = manifest_fixture_path()
.parent()
.unwrap()
.join(second.file_name.as_str());
let wrong_bytes = std::fs::read(&second_path).expect("read wrong fixture file");
let object_type = first_uri.rsplit('.').next().unwrap_or("bin");
put_current_object(&store, &first_uri, wrong_bytes, object_type);
for entry in non_crl_entries.iter().skip(1) {
let file_path = manifest_fixture_path()
.parent()
.unwrap()
.join(entry.file_name.as_str());
let bytes = std::fs::read(&file_path).expect("read fixture file");
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
let object_type = rsync_uri.rsplit('.').next().unwrap_or("bin");
put_current_object(&store, &rsync_uri, bytes, object_type);
}
let err = try_build_fresh_publication_point(
&store,
&manifest_rsync_uri,
&publication_point_rsync_uri,
&issuer_ca_fixture_der(),
Some(issuer_ca_rsync_uri()),
validation_time,
)
.unwrap_err();
assert!(
matches!(err, ManifestFreshError::HashMismatch { .. }),
"{err}"
);
}
#[test]
fn try_build_fresh_publication_point_uses_current_repo_index_without_repository_view() {
let temp = tempfile::tempdir().expect("tempdir");
let store = RocksStore::open(temp.path()).expect("open rocksdb");
let (
manifest,
manifest_bytes,
manifest_rsync_uri,
publication_point_rsync_uri,
validation_time,
) = load_manifest_fixture();
put_raw_only(&store, &manifest_rsync_uri, manifest_bytes.clone(), "mft");
let current_index = CurrentRepoIndex::shared();
let mut entries = vec![crate::storage::RepositoryViewEntry {
rsync_uri: manifest_rsync_uri.clone(),
current_hash: Some(hex::encode(sha2::Sha256::digest(&manifest_bytes))),
repository_source: Some("https://example.test/notification.xml".to_string()),
object_type: Some("mft".to_string()),
state: crate::storage::RepositoryViewState::Present,
}];
for entry in manifest.manifest.parse_files().expect("parse files") {
let file_path = manifest_fixture_path()
.parent()
.unwrap()
.join(entry.file_name.as_str());
let bytes = std::fs::read(&file_path).expect("read fixture file");
let rsync_uri = format!("{publication_point_rsync_uri}{}", entry.file_name);
let object_type = rsync_uri.rsplit('.').next().unwrap_or("bin").to_string();
put_raw_only(&store, &rsync_uri, bytes.clone(), &object_type);
entries.push(crate::storage::RepositoryViewEntry {
rsync_uri,
current_hash: Some(hex::encode(sha2::Sha256::digest(&bytes))),
repository_source: Some("https://example.test/notification.xml".to_string()),
object_type: Some(object_type),
state: crate::storage::RepositoryViewState::Present,
});
}
current_index
.lock()
.expect("index lock")
.apply_repository_view_entries(&entries)
.expect("apply current index");
assert!(
store
.get_repository_view_entry(&manifest_rsync_uri)
.expect("get repository view")
.is_none()
);
let (fresh, _timing) = try_build_fresh_publication_point_with_timing(
&store,
&manifest_rsync_uri,
&publication_point_rsync_uri,
Some(&current_index),
&issuer_ca_fixture_der(),
Some(issuer_ca_rsync_uri()),
validation_time,
)
.expect("fresh publication point via current index");
assert_eq!(fresh.manifest_rsync_uri, manifest_rsync_uri);
assert_eq!(fresh.files.len(), manifest.manifest.file_count());
}
#[test]
fn validate_manifest_embedded_ee_cert_path_rejects_missing_crl_files() {
let (manifest, _, _, publication_point_rsync_uri, validation_time) =
@@ -1139,7 +1418,7 @@ mod tests {
assert_eq!(point.manifest_bytes, manifest_bytes);
assert_eq!(point.files.len(), 1);
assert_eq!(point.files[0].rsync_uri, locked_uri);
assert_eq!(point.files[0].bytes, locked_bytes);
assert_eq!(point.files[0].bytes_cloned().expect("locked bytes"), locked_bytes);
}
#[test]

View File

@@ -218,9 +218,12 @@ pub fn process_publication_point_for_issuer<P: PublicationPointData>(
.iter()
.filter(|f| f.rsync_uri.ends_with(".crl"))
.map(|f| {
let bytes = f
.bytes_cloned()
.expect("snapshot CRL bytes must be loadable");
(
f.rsync_uri.clone(),
CachedIssuerCrl::Pending(f.bytes.clone()),
CachedIssuerCrl::Pending(bytes),
)
})
.collect();
@@ -518,6 +521,9 @@ pub fn process_publication_point_snapshot_for_issuer(
#[derive(Debug, thiserror::Error)]
enum ObjectValidateError {
#[error("object bytes load failed: {0}")]
BytesLoad(String),
#[error("ROA decode failed: {0}")]
RoaDecode(#[from] RoaDecodeError),
@@ -584,7 +590,7 @@ fn process_roa_with_issuer(
let _decode = timing
.as_ref()
.map(|t| t.span_phase("objects_roa_decode_and_validate_total"));
let roa = RoaObject::decode_der(&file.bytes)?;
let roa = RoaObject::decode_der(file.bytes().map_err(ObjectValidateError::BytesLoad)?)?;
drop(_decode);
let _ee_profile = timing
@@ -692,7 +698,7 @@ fn process_aspa_with_issuer(
let _decode = timing
.as_ref()
.map(|t| t.span_phase("objects_aspa_decode_and_validate_total"));
let aspa = AspaObject::decode_der(&file.bytes)?;
let aspa = AspaObject::decode_der(file.bytes().map_err(ObjectValidateError::BytesLoad)?)?;
drop(_decode);
let _ee_profile = timing

View File

@@ -73,6 +73,7 @@ pub fn run_publication_point_once(
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: false,
rsync_repo_cache: Mutex::new(HashMap::new()),
current_repo_index: None,
repo_sync_runtime: None,
};

View File

@@ -3,6 +3,7 @@ use url::Url;
use crate::analysis::timing::TimingHandle;
use crate::audit::PublicationPointAudit;
use crate::audit_downloads::DownloadLogHandle;
use crate::current_repo_index::{CurrentRepoIndexHandle, CurrentRepoObject};
use crate::data_model::ta::TrustAnchor;
use crate::parallel::config::ParallelPhase1Config;
use crate::parallel::repo_runtime::{Phase1RepoSyncRuntime, RepoSyncRuntime};
@@ -84,6 +85,7 @@ pub struct RunTreeFromTalAuditOutput {
pub publication_points: Vec<PublicationPointAudit>,
pub downloads: Vec<crate::audit::AuditDownloadEvent>,
pub download_stats: crate::audit::AuditDownloadStats,
pub current_repo_objects: Vec<CurrentRepoObject>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -93,6 +95,14 @@ pub struct TalRootDiscovery {
pub root_handle: CaInstanceHandle,
}
fn snapshot_current_repo_objects(
current_repo_index: Option<&CurrentRepoIndexHandle>,
) -> Vec<CurrentRepoObject> {
current_repo_index
.and_then(|handle| handle.lock().ok().map(|idx| idx.snapshot_objects()))
.unwrap_or_default()
}
fn make_live_runner<'a>(
store: &'a crate::storage::RocksStore,
policy: &'a crate::policy::Policy,
@@ -101,6 +111,7 @@ fn make_live_runner<'a>(
validation_time: time::OffsetDateTime,
timing: Option<TimingHandle>,
download_log: Option<DownloadLogHandle>,
current_repo_index: Option<CurrentRepoIndexHandle>,
repo_sync_runtime: Option<Arc<dyn RepoSyncRuntime>>,
) -> Rpkiv1PublicationPointRunner<'a> {
Rpkiv1PublicationPointRunner {
@@ -117,6 +128,7 @@ fn make_live_runner<'a>(
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: true,
rsync_repo_cache: Mutex::new(HashMap::new()),
current_repo_index,
repo_sync_runtime,
}
}
@@ -130,15 +142,17 @@ fn build_phase1_repo_sync_runtime<H, R>(
timing: Option<TimingHandle>,
download_log: Option<DownloadLogHandle>,
tal_inputs: Vec<crate::parallel::types::TalInputSpec>,
) -> Result<Arc<dyn RepoSyncRuntime>, RunTreeFromTalError>
) -> Result<(Arc<dyn RepoSyncRuntime>, CurrentRepoIndexHandle), RunTreeFromTalError>
where
H: Fetcher + Clone + 'static,
R: crate::fetch::rsync::RsyncFetcher + Clone + 'static,
{
let coordinator = GlobalRunCoordinator::new(parallel_config.clone(), tal_inputs);
let current_repo_index = coordinator.current_repo_index_handle();
let rsync_fetcher_arc = Arc::new(rsync_fetcher.clone());
let executor = LiveRepoTransportExecutor::new(
Arc::clone(&store),
current_repo_index.clone(),
Arc::new(http_fetcher.clone()),
Arc::clone(&rsync_fetcher_arc),
timing,
@@ -149,12 +163,13 @@ where
let resolver: Arc<dyn Fn(&str) -> String + Send + Sync> =
Arc::new(move |base: &str| rsync_fetcher_arc.dedup_key(base));
let _ = policy; // policy reserved for later runtime-level decisions
Ok(Arc::new(Phase1RepoSyncRuntime::new(
let runtime = Arc::new(Phase1RepoSyncRuntime::new(
coordinator,
pool,
resolver,
policy.sync_preference,
)))
));
Ok((runtime, current_repo_index))
}
fn root_discovery_from_tal_input(
@@ -171,21 +186,16 @@ fn root_discovery_from_tal_input(
let tal_bytes = std::fs::read(path).map_err(|e| {
FromTalError::TalFetch(format!("read TAL file failed: {}: {e}", path.display()))
})?;
let tal = crate::data_model::tal::Tal::decode_bytes(&tal_bytes).map_err(FromTalError::from)?;
let tal = crate::data_model::tal::Tal::decode_bytes(&tal_bytes)
.map_err(FromTalError::from)?;
discover_root_ca_instance_from_tal_with_fetchers(http_fetcher, rsync_fetcher, tal, None)
}
TalSource::FilePathWithTa { tal_path, ta_path } => {
let tal_bytes = std::fs::read(tal_path).map_err(|e| {
FromTalError::TalFetch(format!(
"read TAL file failed: {}: {e}",
tal_path.display()
))
FromTalError::TalFetch(format!("read TAL file failed: {}: {e}", tal_path.display()))
})?;
let ta_der = std::fs::read(ta_path).map_err(|e| {
FromTalError::TaFetch(format!(
"read TA file failed: {}: {e}",
ta_path.display()
))
FromTalError::TaFetch(format!("read TA file failed: {}: {e}", ta_path.display()))
})?;
discover_root_ca_instance_from_tal_and_ta_der(&tal_bytes, &ta_der, None)
}
@@ -269,6 +279,7 @@ pub fn run_tree_from_tal_url_serial(
None,
None,
None,
None,
);
let root = root_handle_from_trust_anchor(
@@ -303,6 +314,7 @@ pub fn run_tree_from_tal_url_serial_audit(
None,
Some(download_log.clone()),
None,
None,
);
let root = root_handle_from_trust_anchor(
@@ -325,6 +337,7 @@ pub fn run_tree_from_tal_url_serial_audit(
publication_points,
downloads,
download_stats,
current_repo_objects: Vec::new(),
})
}
@@ -352,6 +365,7 @@ pub fn run_tree_from_tal_url_serial_audit_with_timing(
Some(timing.clone()),
Some(download_log.clone()),
None,
None,
);
let root = root_handle_from_trust_anchor(
@@ -375,6 +389,7 @@ pub fn run_tree_from_tal_url_serial_audit_with_timing(
publication_points,
downloads,
download_stats,
current_repo_objects: Vec::new(),
})
}
@@ -394,7 +409,7 @@ where
{
let discovery = discover_root_ca_instance_from_tal_url(http_fetcher, tal_url)?;
let download_log = DownloadLogHandle::new();
let runtime = build_phase1_repo_sync_runtime(
let (runtime, current_repo_index) = build_phase1_repo_sync_runtime(
Arc::clone(&store),
policy,
http_fetcher,
@@ -404,6 +419,7 @@ where
Some(download_log.clone()),
vec![TalInputSpec::from_url(tal_url.to_string())],
)?;
let current_repo_index_for_output = current_repo_index.clone();
let runner = make_live_runner(
store.as_ref(),
policy,
@@ -412,6 +428,7 @@ where
validation_time,
None,
Some(download_log.clone()),
Some(current_repo_index),
Some(runtime),
);
@@ -434,6 +451,7 @@ where
publication_points,
downloads,
download_stats,
current_repo_objects: snapshot_current_repo_objects(Some(&current_repo_index_for_output)),
})
}
@@ -453,7 +471,8 @@ where
H: Fetcher + Clone + 'static,
R: crate::fetch::rsync::RsyncFetcher + Clone + 'static,
{
let discovery = discover_root_ca_instance_from_tal_and_ta_der(tal_bytes, ta_der, resolved_ta_uri)?;
let discovery =
discover_root_ca_instance_from_tal_and_ta_der(tal_bytes, ta_der, resolved_ta_uri)?;
let download_log = DownloadLogHandle::new();
let derived_tal_id = derive_tal_id(&discovery);
let tal_inputs = vec![TalInputSpec {
@@ -468,7 +487,7 @@ where
ta_der: ta_der.to_vec(),
},
}];
let runtime = build_phase1_repo_sync_runtime(
let (runtime, current_repo_index) = build_phase1_repo_sync_runtime(
Arc::clone(&store),
policy,
http_fetcher,
@@ -478,6 +497,7 @@ where
Some(download_log.clone()),
tal_inputs,
)?;
let current_repo_index_for_output = current_repo_index.clone();
let runner = make_live_runner(
store.as_ref(),
policy,
@@ -486,6 +506,7 @@ where
validation_time,
None,
Some(download_log.clone()),
Some(current_repo_index),
Some(runtime),
);
@@ -508,6 +529,7 @@ where
publication_points,
downloads,
download_stats,
current_repo_objects: snapshot_current_repo_objects(Some(&current_repo_index_for_output)),
})
}
@@ -526,21 +548,25 @@ where
R: crate::fetch::rsync::RsyncFetcher + Clone + 'static,
{
if tal_inputs.is_empty() {
return Err(RunTreeFromTalError::Replay("multi-TAL run requires at least one TAL input".to_string()));
return Err(RunTreeFromTalError::Replay(
"multi-TAL run requires at least one TAL input".to_string(),
));
}
let roots = discover_multiple_roots_from_tal_inputs(&tal_inputs, http_fetcher, rsync_fetcher)?;
let primary = roots
.first()
.cloned()
.ok_or_else(|| RunTreeFromTalError::Replay("multi-TAL root discovery returned no roots".to_string()))?;
let discoveries = roots.iter().map(|item| item.discovery.clone()).collect::<Vec<_>>();
let primary = roots.first().cloned().ok_or_else(|| {
RunTreeFromTalError::Replay("multi-TAL root discovery returned no roots".to_string())
})?;
let discoveries = roots
.iter()
.map(|item| item.discovery.clone())
.collect::<Vec<_>>();
let root_handles = roots
.into_iter()
.map(|item| item.root_handle)
.collect::<Vec<_>>();
let download_log = DownloadLogHandle::new();
let runtime = build_phase1_repo_sync_runtime(
let (runtime, current_repo_index) = build_phase1_repo_sync_runtime(
Arc::clone(&store),
policy,
http_fetcher,
@@ -550,6 +576,7 @@ where
Some(download_log.clone()),
tal_inputs,
)?;
let current_repo_index_for_output = current_repo_index.clone();
let runner = make_live_runner(
store.as_ref(),
policy,
@@ -558,6 +585,7 @@ where
validation_time,
None,
Some(download_log.clone()),
Some(current_repo_index),
Some(runtime),
);
@@ -574,6 +602,7 @@ where
publication_points,
downloads,
download_stats,
current_repo_objects: snapshot_current_repo_objects(Some(&current_repo_index_for_output)),
})
}
@@ -605,6 +634,7 @@ pub fn run_tree_from_tal_and_ta_der_serial(
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: true,
rsync_repo_cache: Mutex::new(HashMap::new()),
current_repo_index: None,
repo_sync_runtime: None,
};
@@ -652,6 +682,7 @@ pub fn run_tree_from_tal_bytes_serial_audit(
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: true,
rsync_repo_cache: Mutex::new(HashMap::new()),
current_repo_index: None,
repo_sync_runtime: None,
};
@@ -675,6 +706,7 @@ pub fn run_tree_from_tal_bytes_serial_audit(
publication_points,
downloads,
download_stats,
current_repo_objects: Vec::new(),
})
}
@@ -714,6 +746,7 @@ pub fn run_tree_from_tal_bytes_serial_audit_with_timing(
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: true,
rsync_repo_cache: Mutex::new(HashMap::new()),
current_repo_index: None,
repo_sync_runtime: None,
};
@@ -739,6 +772,7 @@ pub fn run_tree_from_tal_bytes_serial_audit_with_timing(
publication_points,
downloads,
download_stats,
current_repo_objects: Vec::new(),
})
}
@@ -771,6 +805,7 @@ pub fn run_tree_from_tal_and_ta_der_serial_audit(
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: true,
rsync_repo_cache: Mutex::new(HashMap::new()),
current_repo_index: None,
repo_sync_runtime: None,
};
@@ -794,6 +829,7 @@ pub fn run_tree_from_tal_and_ta_der_serial_audit(
publication_points,
downloads,
download_stats,
current_repo_objects: Vec::new(),
})
}
@@ -829,6 +865,7 @@ pub fn run_tree_from_tal_and_ta_der_serial_audit_with_timing(
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: true,
rsync_repo_cache: Mutex::new(HashMap::new()),
current_repo_index: None,
repo_sync_runtime: None,
};
@@ -853,6 +890,7 @@ pub fn run_tree_from_tal_and_ta_der_serial_audit_with_timing(
publication_points,
downloads,
download_stats,
current_repo_objects: Vec::new(),
})
}
@@ -894,6 +932,7 @@ pub fn run_tree_from_tal_and_ta_der_payload_replay_serial(
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: true,
rsync_repo_cache: Mutex::new(HashMap::new()),
current_repo_index: None,
repo_sync_runtime: None,
};
@@ -947,6 +986,7 @@ pub fn run_tree_from_tal_and_ta_der_payload_replay_serial_audit(
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: true,
rsync_repo_cache: Mutex::new(HashMap::new()),
current_repo_index: None,
repo_sync_runtime: None,
};
@@ -970,6 +1010,7 @@ pub fn run_tree_from_tal_and_ta_der_payload_replay_serial_audit(
publication_points,
downloads,
download_stats,
current_repo_objects: Vec::new(),
})
}
@@ -1015,6 +1056,7 @@ pub fn run_tree_from_tal_and_ta_der_payload_replay_serial_audit_with_timing(
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: true,
rsync_repo_cache: Mutex::new(HashMap::new()),
current_repo_index: None,
repo_sync_runtime: None,
};
@@ -1039,6 +1081,7 @@ pub fn run_tree_from_tal_and_ta_der_payload_replay_serial_audit_with_timing(
publication_points,
downloads,
download_stats,
current_repo_objects: Vec::new(),
})
}
@@ -1066,6 +1109,7 @@ fn build_payload_replay_runner<'a>(
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: true,
rsync_repo_cache: Mutex::new(HashMap::new()),
current_repo_index: None,
repo_sync_runtime: None,
}
}
@@ -1094,6 +1138,7 @@ fn build_payload_delta_replay_runner<'a>(
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: true,
rsync_repo_cache: Mutex::new(HashMap::new()),
current_repo_index: None,
repo_sync_runtime: None,
}
}
@@ -1122,6 +1167,7 @@ fn build_payload_delta_replay_current_store_runner<'a>(
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: true,
rsync_repo_cache: Mutex::new(HashMap::new()),
current_repo_index: None,
repo_sync_runtime: None,
}
}
@@ -1238,6 +1284,7 @@ fn run_payload_delta_replay_audit_inner(
publication_points,
downloads,
download_stats,
current_repo_objects: Vec::new(),
})
}
@@ -1381,6 +1428,7 @@ fn run_payload_delta_replay_step_audit_inner(
publication_points,
downloads,
download_stats,
current_repo_objects: Vec::new(),
})
}
@@ -1442,23 +1490,15 @@ mod multi_tal_tests {
#[test]
fn discover_multiple_roots_from_tal_inputs_builds_multiple_root_handles() {
let apnic_tal = std::fs::read("tests/fixtures/tal/apnic-rfc7730-https.tal")
.expect("read apnic tal");
let apnic_tal =
std::fs::read("tests/fixtures/tal/apnic-rfc7730-https.tal").expect("read apnic tal");
let apnic_ta = std::fs::read("tests/fixtures/ta/apnic-ta.cer").expect("read apnic ta");
let arin_tal = std::fs::read("tests/fixtures/tal/arin.tal").expect("read arin tal");
let arin_ta = std::fs::read("tests/fixtures/ta/arin-ta.cer").expect("read arin ta");
let tal_inputs = vec![
TalInputSpec::from_ta_der(
"https://example.test/apnic.tal",
apnic_tal,
apnic_ta,
),
TalInputSpec::from_ta_der(
"https://example.test/arin.tal",
arin_tal,
arin_ta,
),
TalInputSpec::from_ta_der("https://example.test/apnic.tal", apnic_tal, apnic_ta),
TalInputSpec::from_ta_der("https://example.test/arin.tal", arin_tal, arin_ta),
];
let roots = discover_multiple_roots_from_tal_inputs(
@@ -1631,16 +1671,14 @@ mod replay_api_tests {
let store = crate::storage::RocksStore::open(&temp.path().join("db")).expect("open db");
let (tal_bytes, ta_der, archive_root, locks_path, validation_time) =
apnic_multi_rir_replay_inputs();
assert!(
archive_root.is_dir(),
"payload replay archive missing: {}",
archive_root.display()
);
assert!(
locks_path.is_file(),
"payload replay locks missing: {}",
locks_path.display()
);
if !archive_root.is_dir() || !locks_path.is_file() {
eprintln!(
"skipping multi-rir payload replay api test; missing fixtures: archive={} locks={}",
archive_root.display(),
locks_path.display()
);
return;
}
let out = run_tree_from_tal_and_ta_der_payload_replay_serial_audit(
&store,

View File

@@ -4,6 +4,7 @@ use crate::audit::{
sha256_hex, sha256_hex_from_32,
};
use crate::audit_downloads::DownloadLogHandle;
use crate::current_repo_index::CurrentRepoIndexHandle;
use crate::data_model::aspa::AspaObject;
use crate::data_model::crl::RpkixCrl;
use crate::data_model::manifest::ManifestObject;
@@ -14,8 +15,8 @@ use crate::data_model::router_cert::{
BgpsecRouterCertificateProfileError,
};
use crate::fetch::rsync::RsyncFetcher;
use crate::policy::Policy;
use crate::parallel::repo_runtime::{RepoSyncRuntime, RepoSyncRuntimeOutcome};
use crate::policy::Policy;
use crate::replay::archive::ReplayArchiveIndex;
use crate::replay::delta_archive::ReplayDeltaArchiveIndex;
use crate::report::{RfcRef, Warning};
@@ -35,7 +36,7 @@ use crate::validation::ca_path::{
};
use crate::validation::manifest::{
ManifestFreshError, PublicationPointData, PublicationPointSource,
process_manifest_publication_point_fresh_after_repo_sync,
process_manifest_publication_point_fresh_after_repo_sync_with_timing,
};
use crate::validation::objects::{
AspaAttestation, RouterKeyPayload, Vrp, process_publication_point_for_issuer,
@@ -53,6 +54,26 @@ use serde_json::json;
use x509_parser::prelude::FromDer;
use x509_parser::x509::SubjectPublicKeyInfo;
#[derive(Clone, Debug, Default)]
struct BuildVcirTimingBreakdown {
select_crl_ms: u64,
current_ca_decode_ms: u64,
local_outputs_ms: u64,
child_entries_ms: u64,
related_artifacts_ms: u64,
struct_build_ms: u64,
}
#[derive(Clone, Debug, Default)]
struct PersistVcirTimingBreakdown {
embedded_collect_ms: u64,
embedded_store_ms: u64,
build_vcir_ms: u64,
previous_load_ms: u64,
replace_vcir_ms: u64,
build_vcir: BuildVcirTimingBreakdown,
}
pub struct Rpkiv1PublicationPointRunner<'a> {
pub store: &'a RocksStore,
pub policy: &'a Policy,
@@ -78,6 +99,7 @@ pub struct Rpkiv1PublicationPointRunner<'a> {
/// same `rsync_base_uri` (observed in APNIC full sync timing reports).
pub rsync_dedup: bool,
pub rsync_repo_cache: Mutex<HashMap<String, bool>>, // rsync_base_uri -> rsync_ok
pub current_repo_index: Option<CurrentRepoIndexHandle>,
pub repo_sync_runtime: Option<Arc<dyn RepoSyncRuntime>>,
}
@@ -196,7 +218,12 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
warnings: repo_warnings,
} = runtime.sync_publication_point_repo(ca)?;
warnings.extend(repo_warnings);
(repo_sync_ok, repo_sync_err, repo_sync_source, repo_sync_phase)
(
repo_sync_ok,
repo_sync_err,
repo_sync_source,
repo_sync_phase,
)
} else if skip_sync_due_to_dedup {
let source = if effective_notification_uri.is_some() {
Some("rrdp_dedup_skip".to_string())
@@ -332,15 +359,17 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
}),
);
let snapshot_prepare_started = std::time::Instant::now();
let fresh_publication_point = {
let _manifest_total = self
.timing
.as_ref()
.map(|t| t.span_phase("manifest_processing_total"));
process_manifest_publication_point_fresh_after_repo_sync(
process_manifest_publication_point_fresh_after_repo_sync_with_timing(
self.store,
&ca.manifest_rsync_uri,
&ca.publication_point_rsync_uri,
self.current_repo_index.as_ref(),
&ca.ca_certificate_der,
ca.ca_certificate_rsync_uri.as_deref(),
self.validation_time,
@@ -348,9 +377,11 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
repo_sync_err.as_deref(),
)
};
let snapshot_prepare_ms = snapshot_prepare_started.elapsed().as_millis() as u64;
match fresh_publication_point {
Ok(fresh_point) => {
Ok((fresh_point, snapshot_prepare_timing)) => {
let objects_processing_started = std::time::Instant::now();
let mut objects = {
let _objects_total = self
.timing
@@ -367,7 +398,9 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
self.timing.as_ref(),
)
};
let objects_processing_ms = objects_processing_started.elapsed().as_millis() as u64;
let child_discovery_started = std::time::Instant::now();
let out = {
let _child_disc_total = self
.timing
@@ -391,15 +424,19 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
(Vec::new(), Vec::new(), Vec::new())
}
};
let child_discovery_ms = child_discovery_started.elapsed().as_millis() as u64;
objects.router_keys.extend(discovered_router_keys);
objects
.local_outputs_cache
.extend(build_router_key_local_outputs(ca, &objects.router_keys));
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;
persist_vcir_for_fresh_result(
let persist_vcir_started = std::time::Instant::now();
let persist_vcir_timing = persist_vcir_for_fresh_result_with_timing(
self.store,
ca,
&pack,
@@ -410,7 +447,9 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
self.validation_time,
)
.map_err(|e| format!("persist VCIR failed: {e}"))?;
let persist_vcir_ms = persist_vcir_started.elapsed().as_millis() as u64;
let audit_build_started = std::time::Instant::now();
let audit = build_publication_point_audit_from_snapshot(
ca,
PublicationPointSource::Fresh,
@@ -423,6 +462,7 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
&objects,
&child_audits,
);
let audit_build_ms = audit_build_started.elapsed().as_millis() as u64;
let result = PublicationPointRunResult {
source: PublicationPointSource::Fresh,
snapshot: Some(pack),
@@ -442,6 +482,30 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
"repo_sync_phase": repo_sync_phase,
"repo_sync_duration_ms": repo_sync_duration_ms,
"total_duration_ms": total_duration_ms,
"post_repo_duration_ms": total_duration_ms.saturating_sub(repo_sync_duration_ms),
"snapshot_prepare_ms": snapshot_prepare_ms,
"snapshot_manifest_load_ms": snapshot_prepare_timing.manifest_load_ms,
"snapshot_manifest_decode_ms": snapshot_prepare_timing.manifest_decode_ms,
"snapshot_replay_guard_ms": snapshot_prepare_timing.replay_guard_ms,
"snapshot_manifest_entries_ms": snapshot_prepare_timing.manifest_entries_ms,
"snapshot_pack_files_ms": snapshot_prepare_timing.pack_files_ms,
"snapshot_ee_path_validate_ms": snapshot_prepare_timing.ee_path_validate_ms,
"objects_processing_ms": objects_processing_ms,
"child_discovery_ms": child_discovery_ms,
"snapshot_pack_ms": snapshot_pack_ms,
"persist_vcir_ms": persist_vcir_ms,
"persist_embedded_collect_ms": persist_vcir_timing.embedded_collect_ms,
"persist_embedded_store_ms": persist_vcir_timing.embedded_store_ms,
"persist_build_vcir_ms": persist_vcir_timing.build_vcir_ms,
"persist_previous_load_ms": persist_vcir_timing.previous_load_ms,
"persist_replace_vcir_ms": persist_vcir_timing.replace_vcir_ms,
"persist_select_crl_ms": persist_vcir_timing.build_vcir.select_crl_ms,
"persist_current_ca_decode_ms": persist_vcir_timing.build_vcir.current_ca_decode_ms,
"persist_local_outputs_ms": persist_vcir_timing.build_vcir.local_outputs_ms,
"persist_child_entries_ms": persist_vcir_timing.build_vcir.child_entries_ms,
"persist_related_artifacts_ms": persist_vcir_timing.build_vcir.related_artifacts_ms,
"persist_vcir_struct_ms": persist_vcir_timing.build_vcir.struct_build_ms,
"audit_build_ms": audit_build_ms,
"warning_count": result.warnings.len(),
"vrp_count": result.objects.vrps.len(),
"vap_count": result.objects.aspas.len(),
@@ -461,6 +525,30 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
"repo_sync_phase": repo_sync_phase,
"repo_sync_duration_ms": repo_sync_duration_ms,
"total_duration_ms": total_duration_ms,
"post_repo_duration_ms": total_duration_ms.saturating_sub(repo_sync_duration_ms),
"snapshot_prepare_ms": snapshot_prepare_ms,
"snapshot_manifest_load_ms": snapshot_prepare_timing.manifest_load_ms,
"snapshot_manifest_decode_ms": snapshot_prepare_timing.manifest_decode_ms,
"snapshot_replay_guard_ms": snapshot_prepare_timing.replay_guard_ms,
"snapshot_manifest_entries_ms": snapshot_prepare_timing.manifest_entries_ms,
"snapshot_pack_files_ms": snapshot_prepare_timing.pack_files_ms,
"snapshot_ee_path_validate_ms": snapshot_prepare_timing.ee_path_validate_ms,
"objects_processing_ms": objects_processing_ms,
"child_discovery_ms": child_discovery_ms,
"snapshot_pack_ms": snapshot_pack_ms,
"persist_vcir_ms": persist_vcir_ms,
"persist_embedded_collect_ms": persist_vcir_timing.embedded_collect_ms,
"persist_embedded_store_ms": persist_vcir_timing.embedded_store_ms,
"persist_build_vcir_ms": persist_vcir_timing.build_vcir_ms,
"persist_previous_load_ms": persist_vcir_timing.previous_load_ms,
"persist_replace_vcir_ms": persist_vcir_timing.replace_vcir_ms,
"persist_select_crl_ms": persist_vcir_timing.build_vcir.select_crl_ms,
"persist_current_ca_decode_ms": persist_vcir_timing.build_vcir.current_ca_decode_ms,
"persist_local_outputs_ms": persist_vcir_timing.build_vcir.local_outputs_ms,
"persist_child_entries_ms": persist_vcir_timing.build_vcir.child_entries_ms,
"persist_related_artifacts_ms": persist_vcir_timing.build_vcir.related_artifacts_ms,
"persist_vcir_struct_ms": persist_vcir_timing.build_vcir.struct_build_ms,
"audit_build_ms": audit_build_ms,
}),
);
}
@@ -479,6 +567,10 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
"repo_sync_phase": repo_sync_phase,
"repo_sync_duration_ms": repo_sync_duration_ms,
"total_duration_ms": total_duration_ms,
"post_repo_duration_ms": total_duration_ms.saturating_sub(repo_sync_duration_ms),
"snapshot_prepare_ms": snapshot_prepare_ms,
"projection_ms": 0,
"audit_build_ms": 0,
"error": fresh_err.to_string(),
}),
);
@@ -498,6 +590,7 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
Err(format!("{fresh_err}"))
}
crate::policy::CaFailedFetchPolicy::ReuseCurrentInstanceVcir => {
let projection_started = std::time::Instant::now();
let projection = project_current_instance_vcir_on_failed_fetch(
self.store,
ca,
@@ -505,7 +598,9 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
self.validation_time,
)
.map_err(|e| format!("failed fetch VCIR projection failed: {e}"))?;
let projection_ms = projection_started.elapsed().as_millis() as u64;
warnings.extend(projection.warnings.clone());
let audit_build_started = std::time::Instant::now();
let audit = build_publication_point_audit_from_vcir(
ca,
projection.source,
@@ -519,6 +614,7 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
&projection.objects,
&projection.child_audits,
);
let audit_build_ms = audit_build_started.elapsed().as_millis() as u64;
let result = PublicationPointRunResult {
source: projection.source,
snapshot: projection.snapshot,
@@ -538,6 +634,10 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
"repo_sync_phase": repo_sync_phase,
"repo_sync_duration_ms": repo_sync_duration_ms,
"total_duration_ms": total_duration_ms,
"post_repo_duration_ms": total_duration_ms.saturating_sub(repo_sync_duration_ms),
"snapshot_prepare_ms": snapshot_prepare_ms,
"projection_ms": projection_ms,
"audit_build_ms": audit_build_ms,
"warning_count": result.warnings.len(),
"vrp_count": result.objects.vrps.len(),
"vap_count": result.objects.aspas.len(),
@@ -604,6 +704,10 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
"repo_sync_phase": repo_sync_phase,
"repo_sync_duration_ms": repo_sync_duration_ms,
"total_duration_ms": total_duration_ms,
"post_repo_duration_ms": total_duration_ms.saturating_sub(repo_sync_duration_ms),
"snapshot_prepare_ms": snapshot_prepare_ms,
"projection_ms": projection_ms,
"audit_build_ms": audit_build_ms,
}),
);
}
@@ -715,13 +819,16 @@ fn discover_children_from_fresh_snapshot_with_audit<P: PublicationPointData>(
let mut crl_cache: std::collections::HashMap<String, CachedIssuerCrl> = locked_files
.iter()
.filter(|f| f.rsync_uri.ends_with(".crl"))
.map(|f| {
(
.map(|f| -> Result<(String, CachedIssuerCrl), String> {
let bytes = f
.bytes_cloned()
.map_err(|e| format!("snapshot CRL bytes load failed: {e}"))?;
Ok((
f.rsync_uri.clone(),
CachedIssuerCrl::Pending(f.bytes.clone()),
)
CachedIssuerCrl::Pending(bytes),
))
})
.collect();
.collect::<Result<_, String>>()?;
let mut out: Vec<DiscoveredChildCaInstance> = Vec::new();
let mut audits: Vec<ObjectAuditEntry> = Vec::new();
@@ -798,7 +905,9 @@ fn discover_children_from_fresh_snapshot_with_audit<P: PublicationPointData>(
continue;
}
cer_seen = cer_seen.saturating_add(1);
let child_der = f.bytes.as_slice();
let child_der = f
.bytes()
.map_err(|e| format!("child certificate bytes load failed: {e}"))?;
let tdecode = std::time::Instant::now();
let child_cert = match crate::data_model::rc::ResourceCertificate::decode_der(child_der) {
@@ -1232,7 +1341,10 @@ fn select_issuer_crl_from_snapshot<'a>(
for u in crldp_uris {
let s = u.as_str();
if let Some(f) = pack.files.iter().find(|f| f.rsync_uri == s) {
return Ok((f.rsync_uri.as_str(), f.bytes.as_slice()));
let bytes = f
.bytes()
.map_err(|e| format!("snapshot CRL bytes load failed: {e}"))?;
return Ok((f.rsync_uri.as_str(), bytes));
}
}
@@ -1285,7 +1397,8 @@ fn repo_sync_failure_phase_label(
original_notification_uri: Option<&str>,
effective_notification_uri: Option<&str>,
) -> &'static str {
if attempted_rrdp && original_notification_uri.is_some() && effective_notification_uri.is_some() {
if attempted_rrdp && original_notification_uri.is_some() && effective_notification_uri.is_some()
{
"rrdp_failed_rsync_failed"
} else if attempted_rrdp
&& original_notification_uri.is_some()
@@ -1367,7 +1480,7 @@ fn build_publication_point_audit_from_snapshot(
if !f.rsync_uri.ends_with(".crl") {
continue;
}
let ok = RpkixCrl::decode_der(&f.bytes).is_ok();
let ok = f.bytes().ok().and_then(|bytes| RpkixCrl::decode_der(bytes).ok()).is_some();
audit_by_uri.insert(
f.rsync_uri.clone(),
ObjectAuditEntry {
@@ -1567,15 +1680,15 @@ fn build_publication_point_audit_from_vcir(
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: vcir
.validated_manifest_meta
.validated_manifest_this_update
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: vcir
.validated_manifest_meta
.validated_manifest_this_update
.rfc3339_utc
.clone(),
next_update_rfc3339_utc: vcir
@@ -2129,15 +2242,42 @@ fn persist_vcir_for_fresh_result(
discovered_children: &[DiscoveredChildCaInstance],
validation_time: time::OffsetDateTime,
) -> Result<(), String> {
if objects.stats.publication_point_dropped {
return Ok(());
}
let embedded_evidence = collect_vcir_embedded_evidence(pack, objects)?;
persist_vcir_non_repository_evidence(store, ca, &embedded_evidence)
.map_err(|e| format!("store VCIR audit evidence failed: {e}"))?;
let vcir = build_vcir_from_fresh_result(
persist_vcir_for_fresh_result_with_timing(
store,
ca,
pack,
objects,
warnings,
child_audits,
discovered_children,
validation_time,
)
.map(|_timing| ())
}
fn persist_vcir_for_fresh_result_with_timing(
store: &RocksStore,
ca: &CaInstanceHandle,
pack: &PublicationPointSnapshot,
objects: &crate::validation::objects::ObjectsOutput,
warnings: &[Warning],
child_audits: &[ObjectAuditEntry],
discovered_children: &[DiscoveredChildCaInstance],
validation_time: time::OffsetDateTime,
) -> Result<PersistVcirTimingBreakdown, String> {
let mut timing = PersistVcirTimingBreakdown::default();
if objects.stats.publication_point_dropped {
return Ok(timing);
}
let embedded_store_started = std::time::Instant::now();
persist_vcir_non_repository_evidence(store, ca)
.map_err(|e| format!("store VCIR audit evidence failed: {e}"))?;
timing.embedded_store_ms = embedded_store_started.elapsed().as_millis() as u64;
let build_vcir_started = std::time::Instant::now();
let (vcir, build_vcir_timing) = build_vcir_from_fresh_result_with_timing(
ca,
pack,
objects,
@@ -2145,17 +2285,23 @@ fn persist_vcir_for_fresh_result(
child_audits,
discovered_children,
validation_time,
&embedded_evidence,
)?;
timing.build_vcir_ms = build_vcir_started.elapsed().as_millis() as u64;
timing.build_vcir = build_vcir_timing;
let previous_load_started = std::time::Instant::now();
let previous = store
.get_vcir(&pack.manifest_rsync_uri)
.map_err(|e| format!("load existing VCIR failed: {e}"))?;
timing.previous_load_ms = previous_load_started.elapsed().as_millis() as u64;
let replace_vcir_started = std::time::Instant::now();
store
.replace_vcir_and_audit_rule_indexes(previous.as_ref(), &vcir)
.map_err(|e| format!("store VCIR and audit rule index failed: {e}"))?;
timing.replace_vcir_ms = replace_vcir_started.elapsed().as_millis() as u64;
Ok(())
Ok(timing)
}
fn build_vcir_from_fresh_result(
@@ -2166,21 +2312,56 @@ fn build_vcir_from_fresh_result(
child_audits: &[ObjectAuditEntry],
discovered_children: &[DiscoveredChildCaInstance],
validation_time: time::OffsetDateTime,
embedded_evidence: &[VcirEmbeddedEvidence],
) -> Result<ValidatedCaInstanceResult, String> {
build_vcir_from_fresh_result_with_timing(
ca,
pack,
objects,
warnings,
child_audits,
discovered_children,
validation_time,
)
.map(|(vcir, _timing)| vcir)
}
fn build_vcir_from_fresh_result_with_timing(
ca: &CaInstanceHandle,
pack: &PublicationPointSnapshot,
objects: &crate::validation::objects::ObjectsOutput,
warnings: &[Warning],
child_audits: &[ObjectAuditEntry],
discovered_children: &[DiscoveredChildCaInstance],
validation_time: time::OffsetDateTime,
) -> Result<(ValidatedCaInstanceResult, BuildVcirTimingBreakdown), String> {
let mut timing = BuildVcirTimingBreakdown::default();
let select_crl_started = std::time::Instant::now();
let current_crl = select_manifest_current_crl_from_snapshot(pack)?;
timing.select_crl_ms = select_crl_started.elapsed().as_millis() as u64;
let current_ca_decode_started = std::time::Instant::now();
let ca_cert = ResourceCertificate::decode_der(&ca.ca_certificate_der)
.map_err(|e| format!("decode current CA certificate failed: {e}"))?;
timing.current_ca_decode_ms = current_ca_decode_started.elapsed().as_millis() as u64;
let local_outputs_started = std::time::Instant::now();
let local_outputs = build_vcir_local_outputs(ca, pack, objects)?;
timing.local_outputs_ms = local_outputs_started.elapsed().as_millis() as u64;
let child_entries_started = std::time::Instant::now();
let child_entries = build_vcir_child_entries(discovered_children, validation_time)?;
timing.child_entries_ms = child_entries_started.elapsed().as_millis() as u64;
let related_artifacts_started = std::time::Instant::now();
let related_artifacts = build_vcir_related_artifacts(
ca,
pack,
current_crl.file.rsync_uri.as_str(),
objects,
child_audits,
embedded_evidence,
);
timing.related_artifacts_ms = related_artifacts_started.elapsed().as_millis() as u64;
let accepted_object_count = related_artifacts
.iter()
.filter(|artifact| artifact.validation_status == VcirArtifactValidationStatus::Accepted)
@@ -2207,6 +2388,7 @@ fn build_vcir_from_fresh_result(
.ok_or_else(|| "current CA certificate missing AuthorityKeyIdentifier".to_string())?,
);
let struct_build_started = std::time::Instant::now();
let vcir = ValidatedCaInstanceResult {
manifest_rsync_uri: pack.manifest_rsync_uri.clone(),
parent_manifest_rsync_uri: ca.parent_manifest_rsync_uri.clone(),
@@ -2264,7 +2446,8 @@ fn build_vcir_from_fresh_result(
},
};
vcir.validate_internal().map_err(|e| e.to_string())?;
Ok(vcir)
timing.struct_build_ms = struct_build_started.elapsed().as_millis() as u64;
Ok((vcir, timing))
}
struct CurrentCrlRef<'a> {
@@ -2290,7 +2473,10 @@ fn select_manifest_current_crl_from_snapshot(
.iter()
.find(|candidate| candidate.rsync_uri == *uri)
{
let crl = RpkixCrl::decode_der(&file.bytes)
let crl = RpkixCrl::decode_der(
file.bytes()
.map_err(|e| format!("load current CRL bytes for VCIR failed: {e}"))?,
)
.map_err(|e| format!("decode current CRL for VCIR failed: {e}"))?;
return Ok(CurrentCrlRef { file, crl });
}
@@ -2329,7 +2515,10 @@ fn build_vcir_local_outputs(
for file in &pack.files {
let source_object_hash = sha256_hex_from_32(&file.sha256);
if accepted_roa_uris.contains(file.rsync_uri.as_str()) {
let roa = RoaObject::decode_der(&file.bytes)
let roa = RoaObject::decode_der(
file.bytes()
.map_err(|e| format!("load accepted ROA bytes for VCIR failed: {e}"))?,
)
.map_err(|e| format!("decode accepted ROA for VCIR failed: {e}"))?;
let ee = &roa.signed_object.signed_data.certificates[0];
let source_ee_cert_hash = sha256_hex(ee.raw_der.as_slice());
@@ -2368,7 +2557,10 @@ fn build_vcir_local_outputs(
});
}
} else if accepted_aspa_uris.contains(file.rsync_uri.as_str()) {
let aspa = AspaObject::decode_der(&file.bytes)
let aspa = AspaObject::decode_der(
file.bytes()
.map_err(|e| format!("load accepted ASPA bytes for VCIR failed: {e}"))?,
)
.map_err(|e| format!("decode accepted ASPA for VCIR failed: {e}"))?;
let ee = &aspa.signed_object.signed_data.certificates[0];
let source_ee_cert_hash = sha256_hex(ee.raw_der.as_slice());
@@ -2489,98 +2681,9 @@ fn build_vcir_child_entries(
Ok(out)
}
#[derive(Clone, Debug)]
struct VcirEmbeddedEvidence {
artifact: VcirRelatedArtifact,
raw_entry: RawByHashEntry,
}
fn collect_vcir_embedded_evidence(
pack: &PublicationPointSnapshot,
objects: &crate::validation::objects::ObjectsOutput,
) -> Result<Vec<VcirEmbeddedEvidence>, String> {
let mut evidence = Vec::new();
let mut seen_hashes = HashSet::new();
let manifest = ManifestObject::decode_der(&pack.manifest_bytes)
.map_err(|e| format!("decode manifest for VCIR embedded evidence failed: {e}"))?;
if let Some(ee) = manifest.signed_object.signed_data.certificates.first() {
let ee_hash = sha256_hex(ee.raw_der.as_slice());
if seen_hashes.insert(ee_hash.clone()) {
evidence.push(VcirEmbeddedEvidence {
artifact: VcirRelatedArtifact {
artifact_role: VcirArtifactRole::EeCert,
artifact_kind: VcirArtifactKind::Cer,
uri: None,
sha256: ee_hash.clone(),
object_type: Some("cer".to_string()),
validation_status: VcirArtifactValidationStatus::Accepted,
},
raw_entry: embedded_raw_entry(ee_hash, ee.raw_der.to_vec()),
});
}
}
let accepted_uris: HashSet<&str> = objects
.audit
.iter()
.filter(|entry| matches!(entry.result, AuditObjectResult::Ok))
.map(|entry| entry.rsync_uri.as_str())
.collect();
for file in &pack.files {
let Some(kind) = signed_object_kind_from_uri(file.rsync_uri.as_str()) else {
continue;
};
if !accepted_uris.contains(file.rsync_uri.as_str()) {
continue;
}
let ee_der = match kind {
VcirArtifactKind::Roa => RoaObject::decode_der(&file.bytes)
.map_err(|e| format!("decode accepted ROA for VCIR embedded evidence failed: {e}"))?
.signed_object
.signed_data
.certificates
.first()
.map(|cert| cert.raw_der.to_vec()),
VcirArtifactKind::Aspa => AspaObject::decode_der(&file.bytes)
.map_err(|e| {
format!("decode accepted ASPA for VCIR embedded evidence failed: {e}")
})?
.signed_object
.signed_data
.certificates
.first()
.map(|cert| cert.raw_der.to_vec()),
_ => None,
};
let Some(ee_der) = ee_der else {
continue;
};
let ee_hash = sha256_hex(ee_der.as_slice());
if !seen_hashes.insert(ee_hash.clone()) {
continue;
}
evidence.push(VcirEmbeddedEvidence {
artifact: VcirRelatedArtifact {
artifact_role: VcirArtifactRole::EeCert,
artifact_kind: VcirArtifactKind::Cer,
uri: None,
sha256: ee_hash.clone(),
object_type: Some("cer".to_string()),
validation_status: VcirArtifactValidationStatus::Accepted,
},
raw_entry: embedded_raw_entry(ee_hash, ee_der),
});
}
Ok(evidence)
}
fn persist_vcir_non_repository_evidence(
store: &RocksStore,
ca: &CaInstanceHandle,
embedded_evidence: &[VcirEmbeddedEvidence],
) -> Result<(), String> {
let current_ca_hash = sha256_hex(&ca.ca_certificate_der);
let mut current_ca_entry =
@@ -2591,10 +2694,6 @@ fn persist_vcir_non_repository_evidence(
current_ca_entry.object_type = Some("cer".to_string());
current_ca_entry.encoding = Some("der".to_string());
upsert_raw_by_hash_entry(store, current_ca_entry)?;
for evidence in embedded_evidence {
upsert_raw_by_hash_entry(store, evidence.raw_entry.clone())?;
}
Ok(())
}
@@ -2641,32 +2740,12 @@ fn upsert_raw_by_hash_entry(store: &RocksStore, entry: RawByHashEntry) -> Result
}
}
fn embedded_raw_entry(sha256_hex: String, bytes: Vec<u8>) -> RawByHashEntry {
let mut entry = RawByHashEntry::from_bytes(sha256_hex, bytes);
entry.object_type = Some("cer".to_string());
entry.encoding = Some("der".to_string());
entry
}
fn signed_object_kind_from_uri(uri: &str) -> Option<VcirArtifactKind> {
if uri.ends_with(".roa") {
Some(VcirArtifactKind::Roa)
} else if uri.ends_with(".asa") {
Some(VcirArtifactKind::Aspa)
} else if uri.ends_with(".gbr") {
Some(VcirArtifactKind::Gbr)
} else {
None
}
}
fn build_vcir_related_artifacts(
ca: &CaInstanceHandle,
pack: &PublicationPointSnapshot,
current_crl_rsync_uri: &str,
objects: &crate::validation::objects::ObjectsOutput,
child_audits: &[ObjectAuditEntry],
embedded_evidence: &[VcirEmbeddedEvidence],
) -> Vec<VcirRelatedArtifact> {
let mut audit_by_uri: HashMap<&str, AuditObjectResult> = HashMap::new();
for entry in child_audits.iter().chain(objects.audit.iter()) {
@@ -2711,11 +2790,6 @@ fn build_vcir_related_artifacts(
});
}
artifacts.extend(
embedded_evidence
.iter()
.map(|evidence| evidence.artifact.clone()),
);
artifacts
}
@@ -3498,7 +3572,7 @@ authorityKeyIdentifier = keyid:always
}
#[test]
fn collect_and_persist_vcir_embedded_evidence_for_real_signed_objects() {
fn persist_vcir_non_repository_evidence_stores_current_ca_cert_only() {
let (pack, issuer_ca_der, validation_time) =
cernet_publication_point_snapshot_for_vcir_tests();
let issuer_ca = ResourceCertificate::decode_der(&issuer_ca_der).expect("decode issuer ca");
@@ -3519,13 +3593,6 @@ authorityKeyIdentifier = keyid:always
"expected local outputs from signed objects"
);
let evidence =
collect_vcir_embedded_evidence(&pack, &objects).expect("collect embedded evidence");
assert!(
evidence.len() >= 2,
"expected manifest EE and signed-object EE evidence"
);
let store_dir = tempfile::tempdir().expect("store dir");
let store = RocksStore::open(store_dir.path()).expect("open rocksdb");
let ca = CaInstanceHandle {
@@ -3543,7 +3610,7 @@ authorityKeyIdentifier = keyid:always
publication_point_rsync_uri: pack.publication_point_rsync_uri.clone(),
rrdp_notification_uri: None,
};
persist_vcir_non_repository_evidence(&store, &ca, &evidence)
persist_vcir_non_repository_evidence(&store, &ca)
.expect("persist embedded evidence");
let issuer_hash = sha256_hex(&issuer_ca_der);
@@ -3557,14 +3624,16 @@ authorityKeyIdentifier = keyid:always
.iter()
.any(|uri| uri.ends_with("BfycW4hQb3wNP4YsiJW-1n6fjro.cer"))
);
for entry in &evidence {
assert!(
store
.get_raw_by_hash_entry(&entry.raw_entry.sha256_hex)
.expect("load evidence raw entry")
.is_some()
);
}
let first_output = objects
.local_outputs_cache
.first()
.expect("first local output");
assert!(
store
.get_raw_by_hash_entry(&first_output.source_ee_cert_hash)
.expect("load source ee raw")
.is_none()
);
}
#[test]
@@ -3794,24 +3863,12 @@ authorityKeyIdentifier = keyid:always
},
],
};
let embedded = vec![VcirEmbeddedEvidence {
artifact: VcirRelatedArtifact {
artifact_role: VcirArtifactRole::EeCert,
artifact_kind: VcirArtifactKind::Cer,
uri: None,
sha256: sha256_hex(b"embedded-ee"),
object_type: Some("cer".to_string()),
validation_status: VcirArtifactValidationStatus::Accepted,
},
raw_entry: embedded_raw_entry(sha256_hex(b"embedded-ee"), vec![1u8, 2, 3]),
}];
let artifacts = build_vcir_related_artifacts(
&ca,
&pack,
"rsync://example.test/repo/issuer/issuer.crl",
&objects,
&[],
&embedded,
);
assert!(
artifacts
@@ -3841,9 +3898,12 @@ authorityKeyIdentifier = keyid:always
assert!(artifacts.iter().any(|artifact| artifact.uri.as_deref()
== Some("rsync://example.test/repo/issuer/extra.bin")
&& artifact.artifact_kind == VcirArtifactKind::Other));
assert!(artifacts.iter().any(
|artifact| artifact.uri.is_none() && artifact.sha256 == sha256_hex(b"embedded-ee")
));
assert!(
!artifacts
.iter()
.any(|artifact| artifact.uri.is_none() && artifact.sha256 == sha256_hex(b"embedded-ee")),
"embedded EE cert artifacts should no longer be persisted separately"
);
}
#[test]
@@ -4046,7 +4106,8 @@ authorityKeyIdentifier = keyid:always
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: false,
rsync_repo_cache: Mutex::new(HashMap::new()),
repo_sync_runtime: None,
current_repo_index: None,
repo_sync_runtime: None,
};
// For this fixture-driven smoke, we provide the correct issuer CA certificate (the CA for
@@ -4108,17 +4169,6 @@ authorityKeyIdentifier = keyid:always
.expect("audit rule index exists");
assert_eq!(audit_rule.manifest_rsync_uri, manifest_rsync_uri);
assert_eq!(audit_rule.output_id, first_vrp.output_id);
assert!(vcir.related_artifacts.iter().any(|artifact| {
artifact.artifact_role == VcirArtifactRole::EeCert
&& artifact.artifact_kind == VcirArtifactKind::Cer
}));
let ee_entry = store
.get_raw_by_hash_entry(&first_vrp.source_ee_cert_hash)
.expect("get source ee raw")
.expect("source ee raw exists");
assert_eq!(ee_entry.object_type.as_deref(), Some("cer"));
assert_eq!(ee_entry.encoding.as_deref(), Some("der"));
let trace = crate::audit_trace::trace_rule_to_root(
&store,
crate::storage::AuditRuleKind::Roa,
@@ -4215,7 +4265,8 @@ authorityKeyIdentifier = keyid:always
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: true,
rsync_repo_cache: Mutex::new(HashMap::new()),
repo_sync_runtime: None,
current_repo_index: None,
repo_sync_runtime: None,
};
let first = runner.run_publication_point(&handle).expect("first run ok");
@@ -4323,7 +4374,8 @@ authorityKeyIdentifier = keyid:always
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: true,
rsync_repo_cache: Mutex::new(HashMap::new()),
repo_sync_runtime: None,
current_repo_index: None,
repo_sync_runtime: None,
};
let first = runner.run_publication_point(&handle).expect("first run ok");
@@ -4434,7 +4486,8 @@ authorityKeyIdentifier = keyid:always
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: true,
rsync_repo_cache: Mutex::new(HashMap::new()),
repo_sync_runtime: None,
current_repo_index: None,
repo_sync_runtime: None,
};
let first = runner.run_publication_point(&handle).expect("first run ok");
@@ -4517,7 +4570,8 @@ authorityKeyIdentifier = keyid:always
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: false,
rsync_repo_cache: Mutex::new(HashMap::new()),
repo_sync_runtime: None,
current_repo_index: None,
repo_sync_runtime: None,
};
let first = ok_runner
.run_publication_point(&handle)
@@ -4543,7 +4597,8 @@ authorityKeyIdentifier = keyid:always
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: false,
rsync_repo_cache: Mutex::new(HashMap::new()),
repo_sync_runtime: None,
current_repo_index: None,
repo_sync_runtime: None,
};
let second = bad_runner
.run_publication_point(&handle)
@@ -5728,7 +5783,8 @@ authorityKeyIdentifier = keyid:always
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: false,
rsync_repo_cache: Mutex::new(HashMap::new()),
repo_sync_runtime: None,
current_repo_index: None,
repo_sync_runtime: None,
};
let first = runner_rrdp
.run_publication_point(&handle)
@@ -5757,7 +5813,8 @@ authorityKeyIdentifier = keyid:always
rrdp_repo_cache: Mutex::new(HashMap::new()),
rsync_dedup: true,
rsync_repo_cache: Mutex::new(HashMap::new()),
repo_sync_runtime: None,
current_repo_index: None,
repo_sync_runtime: None,
};
let third = runner_rsync
.run_publication_point(&handle)