20260609 CIR区分fresh和cached验证输入

This commit is contained in:
2026-06-11 19:13:48 +08:00
parent 74012c686d
commit 4047214ddf
26 changed files with 1054 additions and 552 deletions

View File

@@ -352,11 +352,24 @@ impl Histogram {
struct CirMetrics {
version: u32,
objects: u64,
fresh_objects: u64,
cached_objects: u64,
trust_anchors: u64,
rejected_objects: u64,
fresh_rejected_objects: u64,
cached_rejected_objects: u64,
object_list_sha256: String,
fresh_object_list_sha256: String,
cached_object_list_sha256: String,
reject_list_sha256: String,
fresh_reject_list_sha256: String,
cached_reject_list_sha256: String,
objects_by_type: BTreeMap<String, u64>,
fresh_objects_by_type: BTreeMap<String, u64>,
cached_objects_by_type: BTreeMap<String, u64>,
rejected_objects_by_type: BTreeMap<String, u64>,
fresh_rejected_objects_by_type: BTreeMap<String, u64>,
cached_rejected_objects_by_type: BTreeMap<String, u64>,
}
#[derive(Clone, Debug, Default, Serialize)]
@@ -973,25 +986,62 @@ fn parse_cir(path: &Path, snapshot: &mut MetricsSnapshot) {
{
Ok(cir) => {
let mut objects_by_type = BTreeMap::new();
for object in &cir.objects {
for object in cir.validated_objects() {
*objects_by_type
.entry(object_type_from_uri(&object.rsync_uri))
.or_default() += 1;
}
let mut fresh_objects_by_type = BTreeMap::new();
for object in &cir.fresh_validated_objects {
*fresh_objects_by_type
.entry(object_type_from_uri(&object.rsync_uri))
.or_default() += 1;
}
let mut cached_objects_by_type = BTreeMap::new();
for object in &cir.cached_validated_objects {
*cached_objects_by_type
.entry(object_type_from_uri(&object.rsync_uri))
.or_default() += 1;
}
let mut rejected_objects_by_type = BTreeMap::new();
for object in &cir.rejected_objects {
for object in cir.rejected_objects() {
*rejected_objects_by_type
.entry(object_type_from_uri(&object.object_uri))
.or_default() += 1;
}
let mut fresh_rejected_objects_by_type = BTreeMap::new();
for object in &cir.fresh_rejected_objects {
*fresh_rejected_objects_by_type
.entry(object_type_from_uri(&object.object_uri))
.or_default() += 1;
}
let mut cached_rejected_objects_by_type = BTreeMap::new();
for object in &cir.cached_rejected_objects {
*cached_rejected_objects_by_type
.entry(object_type_from_uri(&object.object_uri))
.or_default() += 1;
}
snapshot.cir = Some(CirMetrics {
version: cir.version,
objects: cir.objects.len() as u64,
objects: cir.validated_object_count() as u64,
fresh_objects: cir.fresh_validated_objects.len() as u64,
cached_objects: cir.cached_validated_objects.len() as u64,
trust_anchors: cir.trust_anchors.len() as u64,
rejected_objects: cir.rejected_objects.len() as u64,
rejected_objects: cir.rejected_object_count() as u64,
fresh_rejected_objects: cir.fresh_rejected_objects.len() as u64,
cached_rejected_objects: cir.cached_rejected_objects.len() as u64,
object_list_sha256: hex::encode(&cir.object_list_sha256),
fresh_object_list_sha256: hex::encode(&cir.fresh_object_list_sha256),
cached_object_list_sha256: hex::encode(&cir.cached_object_list_sha256),
reject_list_sha256: hex::encode(&cir.reject_list_sha256),
fresh_reject_list_sha256: hex::encode(&cir.fresh_reject_list_sha256),
cached_reject_list_sha256: hex::encode(&cir.cached_reject_list_sha256),
objects_by_type,
fresh_objects_by_type,
cached_objects_by_type,
rejected_objects_by_type,
fresh_rejected_objects_by_type,
cached_rejected_objects_by_type,
});
}
Err(err) => snapshot
@@ -1580,6 +1630,14 @@ fn render_cir_metrics(writer: &mut PromWriter<'_>, instance: &str, cir: &CirMetr
&[label("instance", instance)],
cir.objects as f64,
);
for (source, count) in [("fresh", cir.fresh_objects), ("cached", cir.cached_objects)] {
writer.gauge(
"ours_rp_cir_objects_by_source",
"CIR object count by validation input source",
&[label("instance", instance), label("source", source)],
count as f64,
);
}
writer.gauge(
"ours_rp_cir_trust_anchors",
"CIR trust anchor count",
@@ -1592,6 +1650,29 @@ fn render_cir_metrics(writer: &mut PromWriter<'_>, instance: &str, cir: &CirMetr
&[label("instance", instance)],
cir.rejected_objects as f64,
);
for (source, count) in [
("fresh", cir.fresh_rejected_objects),
("cached", cir.cached_rejected_objects),
] {
writer.gauge(
"ours_rp_cir_rejected_objects_by_source",
"CIR rejected object count by validation input source",
&[label("instance", instance), label("source", source)],
count as f64,
);
}
for (source, digest) in [
("merged", cir.object_list_sha256.as_str()),
("fresh", cir.fresh_object_list_sha256.as_str()),
("cached", cir.cached_object_list_sha256.as_str()),
] {
writer.gauge(
"ours_rp_cir_object_list_digest_present",
"CIR object list digest is present by validation input source",
&[label("instance", instance), label("source", source)],
if digest.len() == 64 { 1.0 } else { 0.0 },
);
}
writer.gauge(
"ours_rp_cir_reject_list_digest_present",
"CIR reject list digest is present",
@@ -1602,6 +1683,18 @@ fn render_cir_metrics(writer: &mut PromWriter<'_>, instance: &str, cir: &CirMetr
0.0
},
);
for (source, digest) in [
("merged", cir.reject_list_sha256.as_str()),
("fresh", cir.fresh_reject_list_sha256.as_str()),
("cached", cir.cached_reject_list_sha256.as_str()),
] {
writer.gauge(
"ours_rp_cir_reject_list_digest_present_by_source",
"CIR reject list digest is present by validation input source",
&[label("instance", instance), label("source", source)],
if digest.len() == 64 { 1.0 } else { 0.0 },
);
}
for (object_type, count) in &cir.objects_by_type {
writer.gauge(
"ours_rp_cir_objects_by_type",
@@ -1613,6 +1706,23 @@ fn render_cir_metrics(writer: &mut PromWriter<'_>, instance: &str, cir: &CirMetr
*count as f64,
);
}
for (source, counts) in [
("fresh", &cir.fresh_objects_by_type),
("cached", &cir.cached_objects_by_type),
] {
for (object_type, count) in counts {
writer.gauge(
"ours_rp_cir_objects_by_source_type",
"CIR object count by validation input source and file type",
&[
label("instance", instance),
label("source", source),
label("object_type", object_type),
],
*count as f64,
);
}
}
for (object_type, count) in &cir.rejected_objects_by_type {
writer.gauge(
"ours_rp_cir_rejected_objects_by_type",
@@ -1624,6 +1734,23 @@ fn render_cir_metrics(writer: &mut PromWriter<'_>, instance: &str, cir: &CirMetr
*count as f64,
);
}
for (source, counts) in [
("fresh", &cir.fresh_rejected_objects_by_type),
("cached", &cir.cached_rejected_objects_by_type),
] {
for (object_type, count) in counts {
writer.gauge(
"ours_rp_cir_rejected_objects_by_source_type",
"CIR rejected object count by validation input source and file type",
&[
label("instance", instance),
label("source", source),
label("object_type", object_type),
],
*count as f64,
);
}
}
}
fn render_ccr_metrics(writer: &mut PromWriter<'_>, instance: &str, ccr: &CcrMetrics) {
@@ -2092,8 +2219,8 @@ mod tests {
encode_content_info,
};
use crate::cir::{
CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirRejectedObject,
CirTrustAnchor, compute_reject_list_sha256, encode_cir, sha256,
CanonicalInputRepresentation, CirObject, CirRejectedObject, CirTrustAnchor, encode_cir,
sha256,
};
use tempfile::TempDir;
@@ -2154,7 +2281,7 @@ mod tests {
assert!(snapshot.repo_stats[0].sync_success);
assert_eq!(snapshot.repo_stats[0].download_bytes, 333);
assert_eq!(snapshot.top_pp_by_object_count[0].object_count, 2);
assert_eq!(snapshot.cir.as_ref().unwrap().objects, 1);
assert_eq!(snapshot.cir.as_ref().unwrap().objects, 2);
assert_eq!(snapshot.ccr.as_ref().unwrap().state_items["tas"], 1);
let metrics = render_metrics(&snapshot);
assert!(metrics.contains("ours_rp_repository_info"));
@@ -2162,12 +2289,29 @@ mod tests {
assert!(metrics.contains("ours_rp_repository_download_bytes"));
assert!(metrics.contains("ours_rp_large_publication_points"));
assert!(metrics.contains("ours_rp_cir_objects"));
assert!(metrics.contains("ours_rp_cir_objects_by_source"));
assert!(metrics.contains("ours_rp_cir_rejected_objects_by_source"));
assert!(metrics.contains("ours_rp_cir_objects_by_source_type"));
assert!(metrics.contains("ours_rp_cir_rejected_objects_by_source_type"));
assert!(metrics.contains("ours_rp_cir_object_list_digest_present"));
assert!(metrics.contains("ours_rp_cir_reject_list_digest_present_by_source"));
assert!(metrics.contains("ours_rp_ccr_state_items"));
assert!(
metrics.contains(r#"ours_rp_cir_objects_by_source{instance="test",source="fresh"} 2"#)
);
assert!(
metrics.contains(r#"ours_rp_cir_objects_by_source{instance="test",source="cached"} 0"#)
);
assert!(metrics.contains(
r#"ours_rp_cir_rejected_objects_by_source{instance="test",source="fresh"} 1"#
));
assert!(metrics.contains(r#"ours_rp_vrps{instance="test",kind="total"} 3"#));
assert!(metrics.contains(r#"ours_rp_vrps{instance="test",kind="unique"} 2"#));
let status = render_status_json(&snapshot).expect("status");
assert!(status.contains("topPublicationPointsByObjectCount"));
assert!(status.contains(r#""vrpsUnique": 2"#));
assert!(status.contains(r#""freshObjects": 2"#));
assert!(status.contains(r#""cachedObjects": 0"#));
}
#[test]
@@ -2186,30 +2330,33 @@ mod tests {
object_uri: "rsync://repo.example/a/bad.roa".to_string(),
reason: Some("bad".to_string()),
}];
let cir = CanonicalInputRepresentation {
version: crate::cir::CIR_VERSION_V3,
hash_alg: CirHashAlgorithm::Sha256,
validation_time: time::OffsetDateTime::parse(
let cir = CanonicalInputRepresentation::new_v4(
time::OffsetDateTime::parse(
"2026-05-25T00:00:00Z",
&time::format_description::well_known::Rfc3339,
)
.unwrap(),
objects: vec![CirObject {
rsync_uri: "rsync://repo.example/a/a.roa".to_string(),
sha256: vec![1; 32],
}],
trust_anchors: vec![CirTrustAnchor {
vec![
CirObject {
rsync_uri: "rsync://repo.example/a/a.roa".to_string(),
sha256: vec![1; 32],
},
CirObject {
rsync_uri: "rsync://repo.example/a/bad.roa".to_string(),
sha256: vec![2; 32],
},
],
Vec::new(),
vec![CirTrustAnchor {
ta_rsync_uri: "rsync://repo.example/ta.cer".to_string(),
tal_uri: "https://tal.example/tal.tal".to_string(),
tal_bytes: b"rsync://repo.example/ta.cer\n\nAQID\n".to_vec(),
ta_certificate_der: b"ta".to_vec(),
ta_certificate_sha256: sha256(b"ta"),
}],
reject_list_sha256: compute_reject_list_sha256(
rejected.iter().map(|item| item.object_uri.as_str()),
),
rejected_objects: rejected,
};
rejected,
Vec::new(),
);
encode_cir(&cir).expect("encode cir")
}

View File

@@ -48,8 +48,8 @@ mod tests {
build_aspa_payload_state, build_roa_payload_state, encode_content_info,
};
use crate::cir::{
CIR_VERSION_V3, CanonicalInputRepresentation, CirHashAlgorithm, CirObject,
CirRejectedObject, CirTrustAnchor, compute_reject_list_sha256, encode_cir, sha256,
CanonicalInputRepresentation, CirObject, CirRejectedObject, CirTrustAnchor, encode_cir,
sha256,
};
use crate::data_model::roa::{IpPrefix, RoaAfi};
use crate::validation::objects::{AspaAttestation, Vrp};
@@ -100,6 +100,7 @@ mod tests {
&[
object("rsync://example.net/a.roa", 0x11),
object("rsync://example.net/persistent.roa", 0x44),
object("rsync://example.net/reject-old.roa", 0xee),
],
&[],
64496,
@@ -485,7 +486,18 @@ mod tests {
9,
64496,
);
write_sample_with_ccr_seq(root, "right2", 2, &[peer_mismatch], &[], 10, 64497);
write_sample_with_ccr_seq(
root,
"right2",
2,
&[
peer_mismatch,
object("rsync://example.net/pp/rejected.roa", 0xee),
],
&[],
10,
64497,
);
std::fs::write(
root.join("left.jsonl"),
jsonl(&[
@@ -845,7 +857,6 @@ mod tests {
rejected: &[&str],
) -> CanonicalInputRepresentation {
let mut objects = objects.to_vec();
objects.sort_by(|a, b| a.rsync_uri.cmp(&b.rsync_uri));
let rejected_objects = rejected
.iter()
.map(|uri| CirRejectedObject {
@@ -853,15 +864,26 @@ mod tests {
reason: Some("test".to_string()),
})
.collect::<Vec<_>>();
CanonicalInputRepresentation {
version: CIR_VERSION_V3,
hash_alg: CirHashAlgorithm::Sha256,
validation_time: sample_time(seq),
objects,
trust_anchors: vec![sample_trust_anchor()],
reject_list_sha256: compute_reject_list_sha256(rejected.iter().copied()),
rejected_objects,
for rejected_uri in rejected {
if !objects
.iter()
.any(|object| object.rsync_uri == *rejected_uri)
{
objects.push(CirObject {
rsync_uri: (*rejected_uri).to_string(),
sha256: vec![0xee; 32],
});
}
}
objects.sort_by(|a, b| a.rsync_uri.cmp(&b.rsync_uri));
CanonicalInputRepresentation::new_v4(
sample_time(seq),
objects,
Vec::new(),
vec![sample_trust_anchor()],
rejected_objects,
Vec::new(),
)
}
fn sample_trust_anchor() -> CirTrustAnchor {

View File

@@ -42,10 +42,12 @@ pub(super) fn load_sequence_meta(path: &Path, side: Side) -> Result<Vec<Sequence
.transpose()?;
let validation_time = cir.validation_time;
samples.push(SequenceMeta {
cir_object_count: raw.cir_object_count.or(Some(cir.objects.len() as u64)),
cir_object_count: raw
.cir_object_count
.or(Some(cir.validated_object_count() as u64)),
cir_reject_count: raw
.cir_reject_count
.or(Some(cir.rejected_objects.len() as u64)),
.or(Some(cir.rejected_object_count() as u64)),
cir_trust_anchor_count: raw
.cir_trust_anchor_count
.or(Some(cir.trust_anchors.len() as u64)),
@@ -89,8 +91,7 @@ fn load_sample_parts(
)
})?;
objects = cir
.objects
.iter()
.validated_objects()
.map(|item| (item.rsync_uri.clone(), hex::encode(&item.sha256)))
.collect::<BTreeMap<_, _>>();
object_hashes = objects
@@ -98,8 +99,7 @@ fn load_sample_parts(
.map(|(uri, hash)| object_hash_key(uri, hash))
.collect::<BTreeSet<_>>();
rejects = cir
.rejected_objects
.iter()
.rejected_objects()
.map(|item| item.object_uri.clone())
.collect::<BTreeSet<_>>();
}