v0.3.14-pre.009

This commit is contained in:
2026-09-12 11:22:53 +02:00
parent bf394e0e4b
commit 4bcd942928
10 changed files with 853 additions and 58 deletions

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/runtime_resources.rs
// version: 30
// version: 31
fn grpc_endpoint(cluster: &str) -> std::option::Option<ksp_onchain_transport_lib::YellowstoneGrpcEndpointSettings> {
return grpc_endpoint_with_identity(cluster, "yellowstone-fixture", "fixture-provider");
@@ -1051,15 +1051,21 @@ async fn v0_3_13_pre_002_runtime_resources_reject_duplicate_and_cross_network_so
#[test]
fn v0_3_13_pre_007_source_inventory_aggregates_frontier_and_lifecycle_conservatively() {
let source_keys = std::vec![[1_u8; 32], [2_u8; 32]];
let continuity_contracts = match supervisor_contracts(&[(1, "fixture-a", 1), (2, "fixture-b", 2)]) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let inventory = std::sync::Arc::new(std::sync::Mutex::new(super::RawTransactionIngestSourceInventory::new(source_keys)));
let (aggregate_sender, aggregate_receiver) = tokio::sync::watch::channel(crate::RawTransactionIngestProcessingFrontierProjection::empty());
let first = super::RawTransactionIngestSourceInventoryPublisher {
aggregate_sender: aggregate_sender.clone(),
continuity_contracts: std::sync::Arc::clone(&continuity_contracts),
entry_index: 0,
inventory: std::sync::Arc::clone(&inventory),
source_key: [1_u8; 32],
};
let second = super::RawTransactionIngestSourceInventoryPublisher { aggregate_sender, entry_index: 1, inventory, source_key: [2_u8; 32] };
let second =
super::RawTransactionIngestSourceInventoryPublisher { aggregate_sender, continuity_contracts, entry_index: 1, inventory, source_key: [2_u8; 32] };
assert!(
first
.publish(
@@ -1077,6 +1083,8 @@ fn v0_3_13_pre_007_source_inventory_aggregates_frontier_and_lifecycle_conservati
assert_eq!(first_aggregate.source_active(), 1);
assert_eq!(first_aggregate.source_reconnecting(), 0);
assert_eq!(first_aggregate.source_failed(), 0);
assert!(first_aggregate.continuity_policy_observed());
assert!(!first_aggregate.future_target_coverage());
assert!(
second
.publish(
@@ -1097,6 +1105,9 @@ fn v0_3_13_pre_007_source_inventory_aggregates_frontier_and_lifecycle_conservati
assert_eq!(aggregate.source_reconnect_total(), 4);
assert_eq!(aggregate.source_replay_attempt_total(), 6);
assert_eq!(aggregate.source_continuity_gap_total(), 1);
assert!(aggregate.continuity_policy_observed());
assert!(!aggregate.continuity_has_open_gaps());
assert!(!aggregate.future_target_coverage());
return;
}
@@ -1137,7 +1148,15 @@ async fn v0_3_13_pre_007_source_supervisor_joins_all_children_on_stop() {
});
}
let supervisor = tokio::spawn(async move {
return super::supervise_live_source_tasks(stop_receiver, source_stop_sender, children, contracts, inventory).await;
return super::supervise_live_source_tasks(
stop_receiver,
source_stop_sender,
children,
contracts,
inventory,
tokio::sync::watch::channel(crate::RawTransactionIngestProcessingFrontierProjection::empty()).0,
)
.await;
});
for _ in 0..64 {
if active.load(std::sync::atomic::Ordering::Acquire) == 3 {
@@ -1197,7 +1216,15 @@ async fn v0_3_13_pre_007_source_failure_stops_and_joins_sibling_sources() {
}
}
});
let result = super::supervise_live_source_tasks(stop_receiver, source_stop_sender, children, contracts, inventory).await;
let result = super::supervise_live_source_tasks(
stop_receiver,
source_stop_sender,
children,
contracts,
inventory,
tokio::sync::watch::channel(crate::RawTransactionIngestProcessingFrontierProjection::empty()).0,
)
.await;
assert!(result.is_err());
assert!(!sibling_active.load(std::sync::atomic::Ordering::Acquire));
return;
@@ -3804,7 +3831,15 @@ async fn v0_3_13_pre_011_aborting_outer_source_supervisor_aborts_nested_source_t
return std::future::pending::<([u8; 32], ksp_core_lib::Result<()>)>().await;
});
let supervisor = tokio::spawn(async move {
return super::supervise_live_source_tasks(stop_receiver, source_stop_sender, children, contracts, inventory).await;
return super::supervise_live_source_tasks(
stop_receiver,
source_stop_sender,
children,
contracts,
inventory,
tokio::sync::watch::channel(crate::RawTransactionIngestProcessingFrontierProjection::empty()).0,
)
.await;
});
for _ in 0..64 {
if active.load(std::sync::atomic::Ordering::Acquire) == 1 {
@@ -3863,7 +3898,15 @@ async fn v0_3_13_pre_011_stop_racing_ready_source_fault_preserves_fault_and_join
}
});
let supervisor = tokio::spawn(async move {
return super::supervise_live_source_tasks(stop_receiver, source_stop_sender, children, contracts, inventory).await;
return super::supervise_live_source_tasks(
stop_receiver,
source_stop_sender,
children,
contracts,
inventory,
tokio::sync::watch::channel(crate::RawTransactionIngestProcessingFrontierProjection::empty()).0,
)
.await;
});
for _ in 0..64 {
if sibling_active.load(std::sync::atomic::Ordering::Acquire) == 1 {
@@ -3929,18 +3972,22 @@ async fn v0_3_14_pre_008_proven_full_ledger_epoch_survives_filtered_peer_loss_wi
}
}
});
let (health_sender, health_receiver) = tokio::sync::watch::channel(crate::RawTransactionIngestProcessingFrontierProjection::empty());
let supervisor = tokio::spawn(async move {
return super::supervise_live_source_tasks(stop_receiver, source_stop_sender, children, contracts, inventory).await;
return super::supervise_live_source_tasks(stop_receiver, source_stop_sender, children, contracts, inventory, health_sender).await;
});
for _ in 0..64 {
if sibling_active.load(std::sync::atomic::Ordering::Acquire) {
if sibling_active.load(std::sync::atomic::Ordering::Acquire) && health_receiver.borrow().failed_source_losses_reconciled() {
break;
}
tokio::task::yield_now().await;
}
assert!(sibling_active.load(std::sync::atomic::Ordering::Acquire));
tokio::task::yield_now().await;
assert!(sibling_active.load(std::sync::atomic::Ordering::Acquire));
let health = *health_receiver.borrow();
assert!(health.continuity_policy_observed());
assert!(!health.continuity_has_open_gaps());
assert!(health.future_target_coverage());
assert!(health.failed_source_losses_reconciled());
stop_sender.send_replace(true);
let result = match supervisor.await {
std::result::Result::Ok(value) => value,
@@ -4036,3 +4083,62 @@ fn v0_3_14_pre_003_websocket_incident_without_observed_slot_is_unbounded_and_cou
assert!(reporter.observe_websocket_continuity(crate::RawTransactionIngestSourceState::Active, 0, 0).is_err());
return;
}
#[test]
fn v0_3_14_pre_009_inventory_health_projection_tracks_reconciled_coverage() {
let source_keys = std::vec![[1_u8; 32], [2_u8; 32]];
let continuity_contracts = match supervisor_contracts(&[(1, "standard_logs", 7), (2, "standard_logs", 7)]) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let inventory = std::sync::Arc::new(std::sync::Mutex::new(super::RawTransactionIngestSourceInventory::new(source_keys)));
let (aggregate_sender, aggregate_receiver) = tokio::sync::watch::channel(crate::RawTransactionIngestProcessingFrontierProjection::empty());
let first = super::RawTransactionIngestSourceInventoryPublisher {
aggregate_sender: aggregate_sender.clone(),
continuity_contracts: std::sync::Arc::clone(&continuity_contracts),
entry_index: 0,
inventory: std::sync::Arc::clone(&inventory),
source_key: [1_u8; 32],
};
let second = super::RawTransactionIngestSourceInventoryPublisher {
aggregate_sender,
continuity_contracts: std::sync::Arc::clone(&continuity_contracts),
entry_index: 1,
inventory,
source_key: [2_u8; 32],
};
let active = crate::RawTransactionIngestProcessingFrontierProjection::new(0, std::option::Option::Some(60), std::option::Option::None)
.with_source_continuity(std::option::Option::Some(crate::RawTransactionIngestSourceState::Active), 0, 0, 0);
assert!(first.publish(active).is_ok());
assert!(second.publish(active).is_ok());
let healthy = *aggregate_receiver.borrow();
assert!(healthy.continuity_policy_observed());
assert_eq!(healthy.continuity_frontier_slot(), std::option::Option::Some(60));
assert!(!healthy.continuity_has_open_gaps());
assert!(healthy.future_target_coverage());
{
let mut contracts = match continuity_contracts.lock() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(poisoned) => poisoned.into_inner(),
};
assert!(contracts.record_source_loss_gap([2_u8; 32], 50, 55).is_ok());
}
assert!(first.publish(active).is_ok());
let pending = *aggregate_receiver.borrow();
assert_eq!(pending.continuity_frontier_slot(), std::option::Option::Some(49));
assert!(pending.continuity_has_open_gaps());
assert!(pending.future_target_coverage());
{
let mut contracts = match continuity_contracts.lock() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(poisoned) => poisoned.into_inner(),
};
assert!(contracts.record_coverage_epoch([1_u8; 32], 40, 60).is_ok());
}
assert!(first.publish(active).is_ok());
let reconciled = *aggregate_receiver.borrow();
assert_eq!(reconciled.continuity_frontier_slot(), std::option::Option::Some(60));
assert!(!reconciled.continuity_has_open_gaps());
assert!(reconciled.future_target_coverage());
return;
}