20260626 优化PP缓存索引与证书缓存长尾
This commit is contained in:
@@ -14,6 +14,37 @@ fn sha256_32(input: &[u8]) -> [u8; 32] {
|
||||
compute_sha256_32(input)
|
||||
}
|
||||
|
||||
fn sample_child_certificate_cache_projection(
|
||||
cache_key_sha256_hex: String,
|
||||
child_cert_uri: &str,
|
||||
child_cert_sha256_hex: &str,
|
||||
) -> ChildCertificateCacheProjection {
|
||||
ChildCertificateCacheProjection {
|
||||
schema_version: CHILD_CERTIFICATE_CACHE_SCHEMA_VERSION,
|
||||
algorithm_version: CHILD_CERTIFICATE_CACHE_ALGORITHM_VERSION,
|
||||
cache_key_sha256_hex,
|
||||
child_cert_uri: child_cert_uri.to_string(),
|
||||
child_cert_sha256_hex: child_cert_sha256_hex.to_string(),
|
||||
child_cert_serial: vec![1],
|
||||
issuer_ca_sha256_hex: sha256_hex(b"issuer-ca"),
|
||||
issuer_crl_uri: "rsync://example.test/repo/issuer.crl".to_string(),
|
||||
issuer_crl_sha256_hex: sha256_hex(b"issuer-crl"),
|
||||
parent_context_digest: sha256_32(b"parent-context"),
|
||||
validation_policy_fingerprint: sha256_32(b"policy"),
|
||||
effective_not_before: pack_time(0),
|
||||
effective_until: pack_time(24),
|
||||
payload: ChildCertificateCachePayload::ChildCa {
|
||||
child_manifest_rsync_uri: format!("{child_cert_uri}.mft"),
|
||||
child_ski: "11".repeat(20),
|
||||
child_rsync_base_uri: "rsync://example.test/repo/child/".to_string(),
|
||||
child_publication_point_rsync_uri: "rsync://example.test/repo/child/".to_string(),
|
||||
child_rrdp_notification_uri: Some("https://example.test/notify.xml".to_string()),
|
||||
child_effective_ip_resources: None,
|
||||
child_effective_as_resources: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_work_db_blob_mode_accepts_supported_values() {
|
||||
assert_eq!(default_work_db_blob_mode(), WorkDbBlobMode::Disabled);
|
||||
@@ -565,6 +596,128 @@ fn publication_point_cache_mmap_index_dirty_overlay_wins_and_refreshes_segment()
|
||||
assert_eq!(got.manifest_sha256, sha256_32(b"manifest-new"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn publication_point_cache_mmap_index_write_before_read_refreshes_segment() {
|
||||
let td = tempfile::tempdir().expect("tempdir");
|
||||
let db_path = td.path().join("work-db");
|
||||
let vcir = sample_vcir("rsync://example.test/repo/current.mft");
|
||||
let mut projection = PublicationPointCacheProjection::from_vcir_with_context(
|
||||
&vcir,
|
||||
"rsync://example.test/repo/".to_string(),
|
||||
Some("rsync://example.test/repo/ca.cer".to_string()),
|
||||
sha256_32(b"ca-cert"),
|
||||
sha256_32(b"manifest-old"),
|
||||
sha256_32(b"ta-context"),
|
||||
sha256_32(b"parent-context"),
|
||||
sha256_32(b"policy"),
|
||||
)
|
||||
.expect("build publication point projection");
|
||||
|
||||
{
|
||||
let store = RocksStore::open(&db_path).expect("open rocksdb");
|
||||
store
|
||||
.put_vcir_with_publication_point_cache_projection(&vcir, Some(&projection))
|
||||
.expect("put old projection");
|
||||
let stats = store
|
||||
.refresh_publication_point_cache_mmap_index()
|
||||
.expect("refresh base mmap index")
|
||||
.expect("refresh stats");
|
||||
assert_eq!(stats.state, "written");
|
||||
}
|
||||
|
||||
{
|
||||
let store = RocksStore::open(&db_path).expect("reopen rocksdb");
|
||||
projection.manifest_sha256 = sha256_32(b"manifest-new");
|
||||
store
|
||||
.put_vcir_with_publication_point_cache_projection(&vcir, Some(&projection))
|
||||
.expect("put new projection before any cached read");
|
||||
let stats = store
|
||||
.refresh_publication_point_cache_mmap_index()
|
||||
.expect("refresh dirty mmap segment")
|
||||
.expect("refresh stats");
|
||||
assert_eq!(stats.state, "segment_written");
|
||||
assert_eq!(stats.dirty_entries, 1);
|
||||
assert_eq!(stats.new_entries, 1);
|
||||
}
|
||||
|
||||
let store = RocksStore::open(&db_path).expect("reopen rocksdb after segment");
|
||||
let got = store
|
||||
.get_publication_point_cache_projection_cached(&vcir.manifest_rsync_uri)
|
||||
.expect("get refreshed projection")
|
||||
.expect("projection exists");
|
||||
assert_eq!(got.manifest_sha256, sha256_32(b"manifest-new"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn publication_point_cache_mmap_index_delete_before_read_refreshes_tombstone_segment() {
|
||||
let td = tempfile::tempdir().expect("tempdir");
|
||||
let db_path = td.path().join("work-db");
|
||||
let vcir = sample_vcir("rsync://example.test/repo/current.mft");
|
||||
let projection = PublicationPointCacheProjection::from_vcir_with_context(
|
||||
&vcir,
|
||||
"rsync://example.test/repo/".to_string(),
|
||||
Some("rsync://example.test/repo/ca.cer".to_string()),
|
||||
sha256_32(b"ca-cert"),
|
||||
sha256_32(b"manifest"),
|
||||
sha256_32(b"ta-context"),
|
||||
sha256_32(b"parent-context"),
|
||||
sha256_32(b"policy"),
|
||||
)
|
||||
.expect("build publication point projection");
|
||||
|
||||
{
|
||||
let store = RocksStore::open(&db_path).expect("open rocksdb");
|
||||
store
|
||||
.put_vcir_with_publication_point_cache_projection(&vcir, Some(&projection))
|
||||
.expect("put projection");
|
||||
let stats = store
|
||||
.refresh_publication_point_cache_mmap_index()
|
||||
.expect("refresh base mmap index")
|
||||
.expect("refresh stats");
|
||||
assert_eq!(stats.state, "written");
|
||||
}
|
||||
|
||||
{
|
||||
let store = RocksStore::open(&db_path).expect("reopen rocksdb");
|
||||
store
|
||||
.replace_vcir_manifest_replay_meta_and_projection_action(
|
||||
&vcir,
|
||||
None,
|
||||
PublicationPointCacheProjectionWriteAction::Delete {
|
||||
manifest_rsync_uri: &vcir.manifest_rsync_uri,
|
||||
},
|
||||
)
|
||||
.expect("delete projection before any cached read");
|
||||
assert!(
|
||||
store
|
||||
.get_publication_point_cache_projection_cached(&vcir.manifest_rsync_uri)
|
||||
.expect("dirty tombstone lookup")
|
||||
.is_none()
|
||||
);
|
||||
let stats = store
|
||||
.refresh_publication_point_cache_mmap_index()
|
||||
.expect("refresh tombstone mmap segment")
|
||||
.expect("refresh stats");
|
||||
assert_eq!(stats.state, "segment_written");
|
||||
assert_eq!(stats.dirty_entries, 1);
|
||||
assert_eq!(stats.new_entries, 1);
|
||||
}
|
||||
|
||||
let store = RocksStore::open(&db_path).expect("reopen rocksdb after tombstone segment");
|
||||
assert!(
|
||||
store
|
||||
.get_publication_point_cache_projection_cached(&vcir.manifest_rsync_uri)
|
||||
.expect("tombstone shadows old current index")
|
||||
.is_none()
|
||||
);
|
||||
assert!(
|
||||
store
|
||||
.get_publication_point_cache_projection(&vcir.manifest_rsync_uri)
|
||||
.expect("direct db lookup")
|
||||
.is_none()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn publication_point_cache_projection_rejects_version_mismatch() {
|
||||
let vcir = sample_vcir("rsync://example.test/repo/current.mft");
|
||||
@@ -1364,6 +1517,196 @@ fn replace_vcir_and_manifest_replay_meta_replaces_current_entry() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn get_child_certificate_cache_projections_batch_preserves_order_and_misses() {
|
||||
let td = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(td.path()).expect("open rocksdb");
|
||||
|
||||
let first_key = sha256_hex(b"child-cache-key-first");
|
||||
let missing_key = sha256_hex(b"child-cache-key-missing");
|
||||
let second_key = sha256_hex(b"child-cache-key-second");
|
||||
let first_hash = sha256_hex(b"first-child-cert");
|
||||
let second_hash = sha256_hex(b"second-child-cert");
|
||||
let first = sample_child_certificate_cache_projection(
|
||||
first_key.clone(),
|
||||
"rsync://example.test/repo/first.cer",
|
||||
&first_hash,
|
||||
);
|
||||
let second = sample_child_certificate_cache_projection(
|
||||
second_key.clone(),
|
||||
"rsync://example.test/repo/second.cer",
|
||||
&second_hash,
|
||||
);
|
||||
|
||||
store
|
||||
.put_child_certificate_cache_projection(&first)
|
||||
.expect("put first projection");
|
||||
store
|
||||
.put_child_certificate_cache_projection(&second)
|
||||
.expect("put second projection");
|
||||
|
||||
let got = store
|
||||
.get_child_certificate_cache_projections_batch(&[
|
||||
second_key.clone(),
|
||||
missing_key,
|
||||
first_key.clone(),
|
||||
])
|
||||
.expect("batch get child projections");
|
||||
|
||||
assert_eq!(got.len(), 3);
|
||||
assert_eq!(
|
||||
got[0]
|
||||
.as_ref()
|
||||
.expect("second projection")
|
||||
.child_cert_sha256_hex,
|
||||
second_hash
|
||||
);
|
||||
assert!(got[1].is_none());
|
||||
assert_eq!(
|
||||
got[2]
|
||||
.as_ref()
|
||||
.expect("first projection")
|
||||
.child_cert_sha256_hex,
|
||||
first_hash
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn child_certificate_cache_mmap_segment_preserves_order_and_misses() {
|
||||
let td = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(td.path()).expect("open rocksdb");
|
||||
|
||||
let manifest_uri = "rsync://example.test/repo/parent.mft";
|
||||
let first_key = sha256_hex(b"child-cache-mmap-key-first");
|
||||
let missing_key = sha256_hex(b"child-cache-mmap-key-missing");
|
||||
let second_key = sha256_hex(b"child-cache-mmap-key-second");
|
||||
let first_hash = sha256_hex(b"first-child-cert-mmap");
|
||||
let second_hash = sha256_hex(b"second-child-cert-mmap");
|
||||
let first = sample_child_certificate_cache_projection(
|
||||
first_key.clone(),
|
||||
"rsync://example.test/repo/first.cer",
|
||||
&first_hash,
|
||||
);
|
||||
let second = sample_child_certificate_cache_projection(
|
||||
second_key.clone(),
|
||||
"rsync://example.test/repo/second.cer",
|
||||
&second_hash,
|
||||
);
|
||||
|
||||
assert!(
|
||||
store
|
||||
.get_child_certificate_cache_projections_mmap_segment(
|
||||
manifest_uri,
|
||||
&[first_key.clone()]
|
||||
)
|
||||
.expect("missing segment lookup")
|
||||
.is_none()
|
||||
);
|
||||
|
||||
let write_stats = store
|
||||
.write_child_certificate_cache_mmap_segment(manifest_uri, &[first.clone(), second.clone()])
|
||||
.expect("write child projection segment");
|
||||
assert_eq!(write_stats.new_entries, 2);
|
||||
assert!(write_stats.file_bytes > 0);
|
||||
|
||||
let got = store
|
||||
.get_child_certificate_cache_projections_mmap_segment(
|
||||
manifest_uri,
|
||||
&[second_key.clone(), missing_key, first_key.clone()],
|
||||
)
|
||||
.expect("lookup child projection segment")
|
||||
.expect("segment exists");
|
||||
|
||||
assert_eq!(got.hits, 2);
|
||||
assert_eq!(got.misses, 1);
|
||||
assert!(got.file_bytes > 0);
|
||||
assert_eq!(got.projections.len(), 3);
|
||||
assert_eq!(
|
||||
got.projections[0]
|
||||
.as_ref()
|
||||
.expect("second projection")
|
||||
.child_cert_sha256_hex,
|
||||
second_hash
|
||||
);
|
||||
assert!(got.projections[1].is_none());
|
||||
assert_eq!(
|
||||
got.projections[2]
|
||||
.as_ref()
|
||||
.expect("first projection")
|
||||
.child_cert_sha256_hex,
|
||||
first_hash
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn child_certificate_cache_mmap_segment_overlay_preserves_existing_values() {
|
||||
let td = tempfile::tempdir().expect("tempdir");
|
||||
let store = RocksStore::open(td.path()).expect("open rocksdb");
|
||||
|
||||
let manifest_uri = "rsync://example.test/repo/parent-overlay.mft";
|
||||
let first_key = sha256_hex(b"child-cache-overlay-key-first");
|
||||
let second_key = sha256_hex(b"child-cache-overlay-key-second");
|
||||
let missing_key = sha256_hex(b"child-cache-overlay-key-missing");
|
||||
let first_hash = sha256_hex(b"first-child-cert-overlay");
|
||||
let old_second_hash = sha256_hex(b"old-second-child-cert-overlay");
|
||||
let new_second_hash = sha256_hex(b"new-second-child-cert-overlay");
|
||||
let first = sample_child_certificate_cache_projection(
|
||||
first_key.clone(),
|
||||
"rsync://example.test/repo/first-overlay.cer",
|
||||
&first_hash,
|
||||
);
|
||||
let old_second = sample_child_certificate_cache_projection(
|
||||
second_key.clone(),
|
||||
"rsync://example.test/repo/second-overlay.cer",
|
||||
&old_second_hash,
|
||||
);
|
||||
let new_second = sample_child_certificate_cache_projection(
|
||||
second_key.clone(),
|
||||
"rsync://example.test/repo/second-overlay.cer",
|
||||
&new_second_hash,
|
||||
);
|
||||
|
||||
store
|
||||
.write_child_certificate_cache_mmap_segment(
|
||||
manifest_uri,
|
||||
&[first.clone(), old_second.clone()],
|
||||
)
|
||||
.expect("write initial segment");
|
||||
let stats = store
|
||||
.write_child_certificate_cache_mmap_segment_overlay(
|
||||
manifest_uri,
|
||||
&[first_key.clone(), second_key.clone(), missing_key.clone()],
|
||||
std::slice::from_ref(&new_second),
|
||||
)
|
||||
.expect("write segment overlay");
|
||||
assert_eq!(stats.new_entries, 2);
|
||||
|
||||
let got = store
|
||||
.get_child_certificate_cache_projections_mmap_segment(
|
||||
manifest_uri,
|
||||
&[first_key.clone(), second_key.clone(), missing_key],
|
||||
)
|
||||
.expect("lookup segment")
|
||||
.expect("segment exists");
|
||||
assert_eq!(got.hits, 2);
|
||||
assert_eq!(got.misses, 1);
|
||||
assert_eq!(
|
||||
got.projections[0]
|
||||
.as_ref()
|
||||
.expect("first projection")
|
||||
.child_cert_sha256_hex,
|
||||
first_hash
|
||||
);
|
||||
assert_eq!(
|
||||
got.projections[1]
|
||||
.as_ref()
|
||||
.expect("updated second projection")
|
||||
.child_cert_sha256_hex,
|
||||
new_second_hash
|
||||
);
|
||||
assert!(got.projections[2].is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn storage_helpers_cover_optional_validation_paths() {
|
||||
let withdrawn = RepositoryViewEntry {
|
||||
|
||||
Reference in New Issue
Block a user