20260526 增加持续soak监控与本地回放工具

This commit is contained in:
2026-05-26 18:02:40 +08:00
parent cda7fdb135
commit 7e1c24fcc3
30 changed files with 4541 additions and 12 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,7 @@ pub mod decode;
pub mod encode;
#[cfg(feature = "full")]
pub mod export;
#[cfg(feature = "full")]
pub mod materialize;
pub mod model;
pub mod sequence;
@@ -15,6 +16,7 @@ pub use export::{
CirExportError, CirExportSummary, CirTrustAnchorBinding, build_cir_from_run,
build_cir_from_run_multi, export_cir_from_run, export_cir_from_run_multi, write_cir_file,
};
#[cfg(feature = "full")]
pub use materialize::{
CirMaterializeError, CirMaterializeSummary, materialize_cir, materialize_cir_from_raw_store,
materialize_cir_from_repo_bytes, mirror_relative_path_for_rsync_uri, resolve_static_pool_file,

View File

@@ -12,6 +12,7 @@ pub mod audit_downloads;
pub mod audit_trace;
#[cfg(feature = "full")]
pub mod blob_store;
#[cfg(feature = "full")]
pub mod cli;
#[cfg(feature = "full")]
pub mod current_repo_index;

View File

@@ -589,6 +589,48 @@ mod tests {
}
}
struct FailRrdpThenFailRsyncExecutor {
rrdp_count: Arc<AtomicUsize>,
rsync_count: Arc<AtomicUsize>,
}
impl RepoTransportExecutor for FailRrdpThenFailRsyncExecutor {
fn execute_transport(&self, task: RepoTransportTask) -> RepoTransportResultEnvelope {
match task.mode {
RepoTransportMode::Rrdp => {
self.rrdp_count.fetch_add(1, Ordering::SeqCst);
RepoTransportResultEnvelope {
dedup_key: task.dedup_key,
repo_identity: task.repo_identity,
mode: RepoTransportMode::Rrdp,
tal_id: task.tal_id,
rir_id: task.rir_id,
timing_ms: 10,
result: RepoTransportResultKind::Failed {
detail: "rrdp failed".to_string(),
warnings: vec![Warning::new("rrdp failed")],
},
}
}
RepoTransportMode::Rsync => {
self.rsync_count.fetch_add(1, Ordering::SeqCst);
RepoTransportResultEnvelope {
dedup_key: task.dedup_key,
repo_identity: task.repo_identity,
mode: RepoTransportMode::Rsync,
tal_id: task.tal_id,
rir_id: task.rir_id,
timing_ms: 12,
result: RepoTransportResultKind::Failed {
detail: "rsync failed".to_string(),
warnings: vec![Warning::new("rsync failed")],
},
}
}
}
}
}
#[test]
fn phase1_runtime_waits_for_rrdp_transport_and_returns_rrdp_outcome() {
let coordinator = GlobalRunCoordinator::new(
@@ -852,6 +894,42 @@ mod tests {
assert_eq!(rsync_count.load(Ordering::SeqCst), 1);
}
#[test]
fn phase1_runtime_terminal_failure_keeps_rsync_failure_duration() {
let rrdp_count = Arc::new(AtomicUsize::new(0));
let rsync_count = Arc::new(AtomicUsize::new(0));
let coordinator = GlobalRunCoordinator::new(
ParallelPhase1Config::default(),
vec![TalInputSpec::from_url("https://example.test/arin.tal")],
);
let pool = RepoTransportWorkerPool::new(
RepoWorkerPoolConfig { max_workers: 1 },
FailRrdpThenFailRsyncExecutor {
rrdp_count: Arc::clone(&rrdp_count),
rsync_count: Arc::clone(&rsync_count),
},
)
.expect("pool");
let runtime = Phase1RepoSyncRuntime::new(
coordinator,
pool,
Arc::new(|_base: &str| "rsync://example.test/module/".to_string()),
SyncPreference::RrdpThenRsync,
);
let outcome = runtime
.sync_publication_point_repo(&sample_ca("rsync://example.test/repo/root.mft"))
.expect("sync repo");
assert!(!outcome.repo_sync_ok);
assert_eq!(
outcome.repo_sync_phase.as_deref(),
Some("rrdp_failed_rsync_failed")
);
assert_eq!(outcome.repo_sync_duration_ms, 12);
assert_eq!(rrdp_count.load(Ordering::SeqCst), 1);
assert_eq!(rsync_count.load(Ordering::SeqCst), 1);
}
#[test]
fn phase1_runtime_prefetch_submits_transport_task_before_consumption() {
let rrdp_count = Arc::new(AtomicUsize::new(0));

View File

@@ -433,6 +433,7 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
}
let repo_sync_started = std::time::Instant::now();
let mut runtime_repo_sync_duration_ms = None;
let (repo_sync_ok, repo_sync_err, repo_sync_source, repo_sync_phase): (
bool,
Option<String>,
@@ -444,9 +445,10 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
repo_sync_err,
repo_sync_source,
repo_sync_phase,
repo_sync_duration_ms: _,
repo_sync_duration_ms,
warnings: repo_warnings,
} = runtime.sync_publication_point_repo(ca)?;
runtime_repo_sync_duration_ms = Some(repo_sync_duration_ms);
warnings.extend(repo_warnings);
(
repo_sync_ok,
@@ -575,7 +577,11 @@ impl<'a> PublicationPointRunner for Rpkiv1PublicationPointRunner<'a> {
}
}
};
let repo_sync_duration_ms = repo_sync_started.elapsed().as_millis() as u64;
let repo_sync_duration_ms = effective_repo_sync_duration_ms(
repo_sync_started.elapsed().as_millis() as u64,
runtime_repo_sync_duration_ms,
repo_sync_ok,
);
crate::progress_log::emit(
"publication_point_repo_sync_done",
serde_json::json!({
@@ -1640,6 +1646,19 @@ fn repo_sync_source_label(source: crate::sync::repo::RepoSyncSource) -> &'static
}
}
fn effective_repo_sync_duration_ms(
elapsed_ms: u64,
runtime_reported_duration_ms: Option<u64>,
repo_sync_ok: bool,
) -> u64 {
if repo_sync_ok {
return elapsed_ms;
}
runtime_reported_duration_ms
.map(|runtime_ms| elapsed_ms.max(runtime_ms))
.unwrap_or(elapsed_ms)
}
fn kind_from_vcir_artifact_kind(kind: VcirArtifactKind) -> AuditObjectKind {
match kind {
VcirArtifactKind::Mft => AuditObjectKind::Manifest,
@@ -6463,6 +6482,15 @@ authorityKeyIdentifier = keyid:always
assert!(audit.objects.is_empty());
}
#[test]
fn effective_repo_sync_duration_uses_runtime_duration_for_failures() {
assert_eq!(effective_repo_sync_duration_ms(0, Some(12), false), 12);
assert_eq!(effective_repo_sync_duration_ms(5, Some(12), false), 12);
assert_eq!(effective_repo_sync_duration_ms(20, Some(12), false), 20);
assert_eq!(effective_repo_sync_duration_ms(5, None, false), 5);
assert_eq!(effective_repo_sync_duration_ms(5, Some(12), true), 5);
}
#[test]
fn reconstruct_snapshot_from_vcir_reports_missing_manifest_and_related_raw_bytes() {
let now = time::OffsetDateTime::now_utc();