v0.3.14-pre.008
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/runtime_resources.rs
|
||||
// version: 28
|
||||
// version: 29
|
||||
|
||||
fn grpc_endpoint(cluster: &str) -> std::option::Option<ksp_onchain_transport_lib::YellowstoneGrpcEndpointSettings> {
|
||||
return grpc_endpoint_with_identity(cluster, "yellowstone-fixture", "fixture-provider");
|
||||
@@ -131,6 +131,62 @@ fn ws_endpoint(
|
||||
));
|
||||
}
|
||||
|
||||
fn supervisor_contracts(
|
||||
source_specs: &[(u8, &'static str, u8)],
|
||||
) -> std::option::Option<std::sync::Arc<std::sync::Mutex<crate::RawTransactionIngestContinuityContracts>>> {
|
||||
let network = match ksp_store_lib::RawNetworkId::new("devnet") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::option::Option::None,
|
||||
};
|
||||
let mut capabilities = std::vec::Vec::new();
|
||||
for (source_key_byte, family, fingerprint_byte) in source_specs {
|
||||
let (scope, reference_bearing, block_material) = if *family == "full_ledger" {
|
||||
(crate::RawTransactionIngestCoverageScope::full_ledger_transactions(), false, true)
|
||||
} else {
|
||||
(crate::RawTransactionIngestCoverageScope::exact_source_scope(*family, [*fingerprint_byte; 32]), true, false)
|
||||
};
|
||||
let capability = crate::RawTransactionIngestContinuityCapabilityDescriptor::new(
|
||||
[*source_key_byte; 32],
|
||||
network.clone(),
|
||||
ksp_onchain_transport_lib::SolanaCommitment::Confirmed,
|
||||
scope,
|
||||
reference_bearing,
|
||||
block_material,
|
||||
false,
|
||||
reference_bearing,
|
||||
false,
|
||||
false,
|
||||
);
|
||||
let capability = match capability {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::option::Option::None,
|
||||
};
|
||||
capabilities.push(capability);
|
||||
}
|
||||
return match crate::RawTransactionIngestContinuityContracts::new(capabilities) {
|
||||
std::result::Result::Ok(value) => std::option::Option::Some(std::sync::Arc::new(std::sync::Mutex::new(value))),
|
||||
std::result::Result::Err(_) => std::option::Option::None,
|
||||
};
|
||||
}
|
||||
|
||||
fn supervisor_inventory(
|
||||
source_keys: std::vec::Vec<[u8; 32]>,
|
||||
states: &[std::option::Option<crate::RawTransactionIngestSourceState>],
|
||||
) -> std::option::Option<std::sync::Arc<std::sync::Mutex<super::RawTransactionIngestSourceInventory>>> {
|
||||
if source_keys.len() != states.len() {
|
||||
return std::option::Option::None;
|
||||
}
|
||||
let mut inventory = super::RawTransactionIngestSourceInventory::new(source_keys.clone());
|
||||
for (index, (source_key, state)) in source_keys.iter().zip(states.iter()).enumerate() {
|
||||
let projection = crate::RawTransactionIngestProcessingFrontierProjection::new(0, std::option::Option::Some(100), std::option::Option::None)
|
||||
.with_source_continuity(*state, 0, 0, 0);
|
||||
if inventory.update(index, *source_key, projection).is_err() {
|
||||
return std::option::Option::None;
|
||||
}
|
||||
}
|
||||
return std::option::Option::Some(std::sync::Arc::new(std::sync::Mutex::new(inventory)));
|
||||
}
|
||||
|
||||
fn standard_block_source(
|
||||
cluster: &str,
|
||||
endpoint_name: &str,
|
||||
@@ -1049,8 +1105,24 @@ async fn v0_3_13_pre_007_source_supervisor_joins_all_children_on_stop() {
|
||||
let active = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
|
||||
let (stop_sender, stop_receiver) = tokio::sync::watch::channel(false);
|
||||
let (source_stop_sender, source_stop_receiver) = tokio::sync::watch::channel(false);
|
||||
let source_keys = std::vec![[1_u8; 32], [2_u8; 32], [3_u8; 32]];
|
||||
let contracts = match supervisor_contracts(&[(1, "fixture-a", 1), (2, "fixture-b", 2), (3, "fixture-c", 3)]) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let inventory = match supervisor_inventory(
|
||||
source_keys.clone(),
|
||||
&[
|
||||
std::option::Option::Some(crate::RawTransactionIngestSourceState::Active),
|
||||
std::option::Option::Some(crate::RawTransactionIngestSourceState::Active),
|
||||
std::option::Option::Some(crate::RawTransactionIngestSourceState::Active),
|
||||
],
|
||||
) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let mut children = tokio::task::JoinSet::new();
|
||||
for _ in 0..3 {
|
||||
for source_key in source_keys {
|
||||
let active = std::sync::Arc::clone(&active);
|
||||
let mut source_stop_receiver = source_stop_receiver.clone();
|
||||
let _abort_handle = children.spawn(async move {
|
||||
@@ -1059,12 +1131,14 @@ async fn v0_3_13_pre_007_source_supervisor_joins_all_children_on_stop() {
|
||||
let changed = source_stop_receiver.changed().await;
|
||||
if changed.is_err() || *source_stop_receiver.borrow() {
|
||||
active.fetch_sub(1, std::sync::atomic::Ordering::AcqRel);
|
||||
return std::result::Result::Ok(());
|
||||
return (source_key, std::result::Result::Ok(()));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
let supervisor = tokio::spawn(super::supervise_live_source_tasks(stop_receiver, source_stop_sender, children));
|
||||
let supervisor = tokio::spawn(async move {
|
||||
return super::supervise_live_source_tasks(stop_receiver, source_stop_sender, children, contracts, inventory).await;
|
||||
});
|
||||
for _ in 0..64 {
|
||||
if active.load(std::sync::atomic::Ordering::Acquire) == 3 {
|
||||
break;
|
||||
@@ -1089,11 +1163,25 @@ async fn v0_3_13_pre_007_source_failure_stops_and_joins_sibling_sources() {
|
||||
let sibling_active = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
|
||||
let (_stop_sender, stop_receiver) = tokio::sync::watch::channel(false);
|
||||
let (source_stop_sender, source_stop_receiver) = tokio::sync::watch::channel(false);
|
||||
let 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 = match supervisor_inventory(
|
||||
std::vec![[1_u8; 32], [2_u8; 32]],
|
||||
&[
|
||||
std::option::Option::Some(crate::RawTransactionIngestSourceState::Failed),
|
||||
std::option::Option::Some(crate::RawTransactionIngestSourceState::Active),
|
||||
],
|
||||
) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let mut children = tokio::task::JoinSet::new();
|
||||
let failing_barrier = std::sync::Arc::clone(&barrier);
|
||||
let _failing_abort_handle = children.spawn(async move {
|
||||
let _barrier_wait = failing_barrier.wait().await;
|
||||
return std::result::Result::Err(crate::runtime_error("test.pre_007_source_failed"));
|
||||
return ([1_u8; 32], std::result::Result::Err(crate::runtime_error("test.pre_007_source_failed")));
|
||||
});
|
||||
let sibling_barrier = std::sync::Arc::clone(&barrier);
|
||||
let sibling_active_for_task = std::sync::Arc::clone(&sibling_active);
|
||||
@@ -1105,11 +1193,11 @@ async fn v0_3_13_pre_007_source_failure_stops_and_joins_sibling_sources() {
|
||||
let changed = sibling_stop_receiver.changed().await;
|
||||
if changed.is_err() || *sibling_stop_receiver.borrow() {
|
||||
sibling_active_for_task.store(false, std::sync::atomic::Ordering::Release);
|
||||
return std::result::Result::Ok(());
|
||||
return ([2_u8; 32], std::result::Result::Ok(()));
|
||||
}
|
||||
}
|
||||
});
|
||||
let result = super::supervise_live_source_tasks(stop_receiver, source_stop_sender, children).await;
|
||||
let result = super::supervise_live_source_tasks(stop_receiver, source_stop_sender, children, contracts, inventory).await;
|
||||
assert!(result.is_err());
|
||||
assert!(!sibling_active.load(std::sync::atomic::Ordering::Acquire));
|
||||
return;
|
||||
@@ -3701,13 +3789,23 @@ async fn v0_3_13_pre_011_aborting_outer_source_supervisor_aborts_nested_source_t
|
||||
let active = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
|
||||
let (_stop_sender, stop_receiver) = tokio::sync::watch::channel(false);
|
||||
let (source_stop_sender, _source_stop_receiver) = tokio::sync::watch::channel(false);
|
||||
let contracts = match supervisor_contracts(&[(1, "fixture-a", 1)]) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let inventory = match supervisor_inventory(std::vec![[1_u8; 32]], &[std::option::Option::Some(crate::RawTransactionIngestSourceState::Active)]) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let mut children = tokio::task::JoinSet::new();
|
||||
let task_active = std::sync::Arc::clone(&active);
|
||||
let _abort_handle = children.spawn(async move {
|
||||
let _guard = Pre011SourceActiveGuard::new(task_active);
|
||||
return std::future::pending::<ksp_core_lib::Result<()>>().await;
|
||||
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;
|
||||
});
|
||||
let supervisor = tokio::spawn(super::supervise_live_source_tasks(stop_receiver, source_stop_sender, children));
|
||||
for _ in 0..64 {
|
||||
if active.load(std::sync::atomic::Ordering::Acquire) == 1 {
|
||||
break;
|
||||
@@ -3733,11 +3831,25 @@ async fn v0_3_13_pre_011_stop_racing_ready_source_fault_preserves_fault_and_join
|
||||
let fault_gate = std::sync::Arc::new(tokio::sync::Notify::new());
|
||||
let (stop_sender, stop_receiver) = tokio::sync::watch::channel(false);
|
||||
let (source_stop_sender, source_stop_receiver) = tokio::sync::watch::channel(false);
|
||||
let 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 = match supervisor_inventory(
|
||||
std::vec![[1_u8; 32], [2_u8; 32]],
|
||||
&[
|
||||
std::option::Option::Some(crate::RawTransactionIngestSourceState::Failed),
|
||||
std::option::Option::Some(crate::RawTransactionIngestSourceState::Active),
|
||||
],
|
||||
) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let mut children = tokio::task::JoinSet::new();
|
||||
let task_fault_gate = std::sync::Arc::clone(&fault_gate);
|
||||
let _fault_abort_handle = children.spawn(async move {
|
||||
task_fault_gate.notified().await;
|
||||
return std::result::Result::Err(crate::runtime_error("test.pre_011_ready_source_fault"));
|
||||
return ([1_u8; 32], std::result::Result::Err(crate::runtime_error("test.pre_011_ready_source_fault")));
|
||||
});
|
||||
let sibling_active_for_task = std::sync::Arc::clone(&sibling_active);
|
||||
let mut sibling_stop_receiver = source_stop_receiver.clone();
|
||||
@@ -3746,11 +3858,13 @@ async fn v0_3_13_pre_011_stop_racing_ready_source_fault_preserves_fault_and_join
|
||||
loop {
|
||||
let changed = sibling_stop_receiver.changed().await;
|
||||
if changed.is_err() || *sibling_stop_receiver.borrow() {
|
||||
return std::result::Result::Ok(());
|
||||
return ([2_u8; 32], std::result::Result::Ok(()));
|
||||
}
|
||||
}
|
||||
});
|
||||
let supervisor = tokio::spawn(super::supervise_live_source_tasks(stop_receiver, source_stop_sender, children));
|
||||
let supervisor = tokio::spawn(async move {
|
||||
return super::supervise_live_source_tasks(stop_receiver, source_stop_sender, children, contracts, inventory).await;
|
||||
});
|
||||
for _ in 0..64 {
|
||||
if sibling_active.load(std::sync::atomic::Ordering::Acquire) == 1 {
|
||||
break;
|
||||
@@ -3773,6 +3887,86 @@ async fn v0_3_13_pre_011_stop_racing_ready_source_fault_preserves_fault_and_join
|
||||
return;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn v0_3_14_pre_008_proven_full_ledger_epoch_survives_filtered_peer_loss_without_worker_respawn() {
|
||||
let sibling_active = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
|
||||
let (stop_sender, stop_receiver) = tokio::sync::watch::channel(false);
|
||||
let (source_stop_sender, source_stop_receiver) = tokio::sync::watch::channel(false);
|
||||
let contracts = match supervisor_contracts(&[(1, "standard_logs", 7), (2, "full_ledger", 0)]) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
{
|
||||
let mut contracts_guard = match contracts.lock() {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(poisoned) => poisoned.into_inner(),
|
||||
};
|
||||
assert!(contracts_guard.record_coverage_epoch([2_u8; 32], 90, 110).is_ok());
|
||||
}
|
||||
let inventory = match supervisor_inventory(
|
||||
std::vec![[1_u8; 32], [2_u8; 32]],
|
||||
&[
|
||||
std::option::Option::Some(crate::RawTransactionIngestSourceState::Failed),
|
||||
std::option::Option::Some(crate::RawTransactionIngestSourceState::Active),
|
||||
],
|
||||
) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let mut children = tokio::task::JoinSet::new();
|
||||
let _lost_abort_handle = children.spawn(async move {
|
||||
return ([1_u8; 32], std::result::Result::Err(super::continuity_range_error("source.continuity_gap_proven", 95, 100)));
|
||||
});
|
||||
let sibling_active_for_task = std::sync::Arc::clone(&sibling_active);
|
||||
let mut sibling_stop_receiver = source_stop_receiver.clone();
|
||||
let _sibling_abort_handle = children.spawn(async move {
|
||||
sibling_active_for_task.store(true, std::sync::atomic::Ordering::Release);
|
||||
loop {
|
||||
let changed = sibling_stop_receiver.changed().await;
|
||||
if changed.is_err() || *sibling_stop_receiver.borrow() {
|
||||
sibling_active_for_task.store(false, std::sync::atomic::Ordering::Release);
|
||||
return ([2_u8; 32], std::result::Result::Ok(()));
|
||||
}
|
||||
}
|
||||
});
|
||||
let supervisor = tokio::spawn(async move {
|
||||
return super::supervise_live_source_tasks(stop_receiver, source_stop_sender, children, contracts, inventory).await;
|
||||
});
|
||||
for _ in 0..64 {
|
||||
if sibling_active.load(std::sync::atomic::Ordering::Acquire) {
|
||||
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));
|
||||
stop_sender.send_replace(true);
|
||||
let result = match supervisor.await {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
assert!(result.is_ok());
|
||||
assert!(!sibling_active.load(std::sync::atomic::Ordering::Acquire));
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v0_3_14_pre_008_only_classified_source_loss_enters_coverage_decision() {
|
||||
let transport = super::source_transport_error(ksp_onchain_transport_lib::ERROR_CODE_HTTP_CONNECTION_FAILED);
|
||||
assert!(super::source_loss_is_reconcilable(&transport));
|
||||
assert!(super::source_loss_is_reconcilable(&crate::runtime_error("source.continuity_gap_proven")));
|
||||
assert!(super::source_loss_is_reconcilable(&crate::runtime_error("source.replay_coverage_unproven")));
|
||||
assert!(!super::source_loss_is_reconcilable(&crate::runtime_error("source.hydration_task_join_failed")));
|
||||
let bounded = super::continuity_range_error("source.continuity_gap_proven", 10, 20);
|
||||
assert!(matches!(super::source_loss_continuity_range(&bounded), std::result::Result::Ok(std::option::Option::Some((10, 20)))));
|
||||
assert!(matches!(
|
||||
super::source_loss_continuity_range(&crate::runtime_error("source.continuity_gap_proven")),
|
||||
std::result::Result::Ok(std::option::Option::None)
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn v0_3_14_pre_003_websocket_reconnect_is_anchored_before_terminal_gap_projection() {
|
||||
let (sender, receiver) = tokio::sync::watch::channel(crate::RawTransactionIngestProcessingFrontierProjection::empty());
|
||||
@@ -3790,7 +3984,7 @@ fn v0_3_14_pre_003_websocket_reconnect_is_anchored_before_terminal_gap_projectio
|
||||
assert!(!anchor.saw_overflow());
|
||||
assert!(reporter.observe_websocket_continuity(crate::RawTransactionIngestSourceState::Active, 1, 0).is_ok());
|
||||
let bounded = reporter.observe_websocket_post_incident_slot(105);
|
||||
assert!(matches!(bounded, std::result::Result::Ok(true)));
|
||||
assert!(matches!(bounded, std::result::Result::Ok(std::option::Option::Some((100, 105)))));
|
||||
let anchor = match reporter.websocket_incident_anchor {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => panic!("bounded reconnect incident anchor missing"),
|
||||
@@ -3822,7 +4016,7 @@ fn v0_3_14_pre_003_websocket_overflow_and_reconnect_storm_share_earliest_anchor(
|
||||
assert_eq!(projection.source_reconnect_total(), 1);
|
||||
assert_eq!(projection.source_continuity_gap_total(), 3);
|
||||
let bounded = reporter.observe_websocket_post_incident_slot(201);
|
||||
assert!(matches!(bounded, std::result::Result::Ok(true)));
|
||||
assert!(matches!(bounded, std::result::Result::Ok(std::option::Option::Some((200, 201)))));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user