v0.3.11-pre.007
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/runtime.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
struct ActiveTaskGuard {
|
||||
active: std::sync::Arc<std::sync::atomic::AtomicUsize>,
|
||||
@@ -251,3 +251,284 @@ async fn pre_005_supervisor_reaps_completed_source_task_and_still_joins_live_chi
|
||||
assert_eq!(active.load(std::sync::atomic::Ordering::Acquire), 0);
|
||||
return;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum RuntimePortResponse {
|
||||
Conflict,
|
||||
StoreFailure,
|
||||
SuccessBlocked,
|
||||
}
|
||||
|
||||
struct RuntimePersistencePort {
|
||||
active: std::sync::atomic::AtomicUsize,
|
||||
completed: std::sync::atomic::AtomicUsize,
|
||||
max_active: std::sync::atomic::AtomicUsize,
|
||||
network: ksp_store_lib::RawNetworkId,
|
||||
normal_mode_seen: std::sync::atomic::AtomicBool,
|
||||
notify: tokio::sync::Notify,
|
||||
released: std::sync::atomic::AtomicBool,
|
||||
response: RuntimePortResponse,
|
||||
}
|
||||
|
||||
impl RuntimePersistencePort {
|
||||
fn new(network: ksp_store_lib::RawNetworkId, response: RuntimePortResponse, released: bool) -> Self {
|
||||
return Self {
|
||||
active: std::sync::atomic::AtomicUsize::new(0),
|
||||
completed: std::sync::atomic::AtomicUsize::new(0),
|
||||
max_active: std::sync::atomic::AtomicUsize::new(0),
|
||||
network,
|
||||
normal_mode_seen: std::sync::atomic::AtomicBool::new(false),
|
||||
notify: tokio::sync::Notify::new(),
|
||||
released: std::sync::atomic::AtomicBool::new(released),
|
||||
response,
|
||||
};
|
||||
}
|
||||
|
||||
fn release(&self) {
|
||||
self.released.store(true, std::sync::atomic::Ordering::Release);
|
||||
self.notify.notify_waiters();
|
||||
return;
|
||||
}
|
||||
|
||||
fn update_max_active(&self, current: usize) {
|
||||
let mut observed = self.max_active.load(std::sync::atomic::Ordering::Acquire);
|
||||
while current > observed {
|
||||
match self.max_active.compare_exchange_weak(observed, current, std::sync::atomic::Ordering::AcqRel, std::sync::atomic::Ordering::Acquire) {
|
||||
std::result::Result::Ok(_) => return,
|
||||
std::result::Result::Err(value) => observed = value,
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::RawTransactionIngestPersistencePort for RuntimePersistencePort {
|
||||
fn network_matches(&self, network: &ksp_store_lib::RawNetworkId) -> bool {
|
||||
return &self.network == network;
|
||||
}
|
||||
|
||||
fn persist_acquisition<'a>(
|
||||
&'a self,
|
||||
_transaction: ksp_store_lib::RawTransaction,
|
||||
_observation: ksp_store_lib::RawTransactionObservation,
|
||||
mode: ksp_store_lib::RawTransactionAcquisitionMode,
|
||||
) -> ksp_store_lib::StoreApiFuture<'a, ksp_store_lib::Result<ksp_store_lib::RawAcquisitionWriteOutcome>> {
|
||||
self.normal_mode_seen.store(mode == ksp_store_lib::RawTransactionAcquisitionMode::Normal, std::sync::atomic::Ordering::Release);
|
||||
let response = self.response;
|
||||
return std::boxed::Box::pin(async move {
|
||||
if matches!(response, RuntimePortResponse::SuccessBlocked) {
|
||||
let current = self.active.fetch_add(1, std::sync::atomic::Ordering::AcqRel) + 1;
|
||||
self.update_max_active(current);
|
||||
while !self.released.load(std::sync::atomic::Ordering::Acquire) {
|
||||
self.notify.notified().await;
|
||||
}
|
||||
self.active.fetch_sub(1, std::sync::atomic::Ordering::AcqRel);
|
||||
self.completed.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
|
||||
return std::result::Result::Ok(ksp_store_lib::RawAcquisitionWriteOutcome::new(
|
||||
ksp_store_lib::RawEntityWriteOutcome::Inserted,
|
||||
ksp_store_lib::RawObservationWriteOutcome::Inserted,
|
||||
));
|
||||
}
|
||||
if matches!(response, RuntimePortResponse::Conflict) {
|
||||
self.completed.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(ksp_store_lib::ERROR_CODE_RAW_CONFLICT, "synthetic conflict"));
|
||||
}
|
||||
self.completed.fetch_add(1, std::sync::atomic::Ordering::AcqRel);
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
ksp_core_lib::ErrorCode::new("synthetic_store", "write_failed"),
|
||||
"synthetic store failure",
|
||||
));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn runtime_ingress(network: &ksp_store_lib::RawNetworkId, signature_byte: u8) -> std::option::Option<crate::RawTransactionIngress> {
|
||||
let signature = ksp_store_lib::RawTransactionSignature::new([signature_byte; 64]);
|
||||
let material = ksp_raw_transaction_lib::RawTransactionMaterial::binary_base64(
|
||||
network.clone(),
|
||||
signature,
|
||||
42,
|
||||
std::option::Option::Some(1_700_000_000),
|
||||
"AQID",
|
||||
ksp_raw_transaction_lib::RawTransactionWireField::Omitted,
|
||||
ksp_raw_transaction_lib::RawTransactionWireField::Omitted,
|
||||
ksp_raw_transaction_lib::RawTransactionWireField::Omitted,
|
||||
);
|
||||
let provider = match ksp_store_lib::RawProvenanceCode::new("deterministic-harness") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::option::Option::None,
|
||||
};
|
||||
let protocol = match ksp_store_lib::RawProvenanceCode::new("internal") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::option::Option::None,
|
||||
};
|
||||
let method = match ksp_store_lib::RawProvenanceCode::new("runtime") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::option::Option::None,
|
||||
};
|
||||
let received_at = match ksp_store_lib::RawTimestamp::from_unix_millis(1_700_000_000_000) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::option::Option::None,
|
||||
};
|
||||
let provenance = ksp_store_lib::RawAcquisitionProvenance::new(provider, protocol, method, ksp_store_lib::RawAcquisitionOrigin::Live, received_at);
|
||||
return std::option::Option::Some(crate::RawTransactionIngress { material, network: network.clone(), provenance, source_key: [signature_byte; 32] });
|
||||
}
|
||||
|
||||
fn settings_with_persistence_concurrency(concurrency: usize) -> std::option::Option<crate::RawTransactionIngestSettings> {
|
||||
let network = match ksp_store_lib::RawNetworkId::new("mainnet") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::option::Option::None,
|
||||
};
|
||||
let worker_id = match ksp_worker_api::WorkerId::new("raw-ingest-runtime-persistence-001") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::option::Option::None,
|
||||
};
|
||||
return match crate::RawTransactionIngestSettings::new(network, worker_id, 8, concurrency, std::time::Duration::from_secs(5)) {
|
||||
std::result::Result::Ok(value) => std::option::Option::Some(value),
|
||||
std::result::Result::Err(_) => std::option::Option::None,
|
||||
};
|
||||
}
|
||||
|
||||
async fn wait_for_completed(port: &std::sync::Arc<RuntimePersistencePort>, expected: usize) -> bool {
|
||||
for _ in 0..128 {
|
||||
if port.completed.load(std::sync::atomic::Ordering::Acquire) == expected {
|
||||
return true;
|
||||
}
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
return port.completed.load(std::sync::atomic::Ordering::Acquire) == expected;
|
||||
}
|
||||
|
||||
async fn wait_for_max_active(port: &std::sync::Arc<RuntimePersistencePort>, expected: usize) -> bool {
|
||||
for _ in 0..128 {
|
||||
if port.max_active.load(std::sync::atomic::Ordering::Acquire) == expected {
|
||||
return true;
|
||||
}
|
||||
tokio::task::yield_now().await;
|
||||
}
|
||||
return port.max_active.load(std::sync::atomic::Ordering::Acquire) == expected;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn pre_007_runtime_bounds_in_flight_store_persistence_to_configured_concurrency() {
|
||||
let settings = match settings_with_persistence_concurrency(2) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let network = settings.network().clone();
|
||||
let port = std::sync::Arc::new(RuntimePersistencePort::new(network.clone(), RuntimePortResponse::SuccessBlocked, false));
|
||||
let runtime_port: super::PersistencePort = port.clone();
|
||||
let handle = match super::start_foundation_with_port_and_source_spawner(
|
||||
settings,
|
||||
tokio::runtime::Handle::current(),
|
||||
std::option::Option::Some(runtime_port),
|
||||
move |children, _stop_receiver, admission_sender| {
|
||||
let _abort_handle = children.spawn(async move {
|
||||
for signature_byte in 1..=4 {
|
||||
let ingress = match runtime_ingress(&network, signature_byte) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
if admission_sender.send(ingress).await.is_err() {
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
});
|
||||
},
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
assert!(wait_for_max_active(&port, 2).await);
|
||||
assert_eq!(port.active.load(std::sync::atomic::Ordering::Acquire), 2);
|
||||
assert_eq!(port.max_active.load(std::sync::atomic::Ordering::Acquire), 2);
|
||||
port.release();
|
||||
assert!(wait_for_completed(&port, 4).await);
|
||||
assert!(port.normal_mode_seen.load(std::sync::atomic::Ordering::Acquire));
|
||||
assert_eq!(port.max_active.load(std::sync::atomic::Ordering::Acquire), 2);
|
||||
assert!(handle.request_stop());
|
||||
let terminal = handle.wait_terminal().await;
|
||||
let terminal = match terminal {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
assert_eq!(terminal, ksp_worker_api::WorkerState::Stopped);
|
||||
assert_eq!(port.active.load(std::sync::atomic::Ordering::Acquire), 0);
|
||||
return;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn pre_007_content_conflict_becomes_terminal_after_private_drain() {
|
||||
let settings = match settings_with_persistence_concurrency(1) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let network = settings.network().clone();
|
||||
let port = std::sync::Arc::new(RuntimePersistencePort::new(network.clone(), RuntimePortResponse::Conflict, true));
|
||||
let runtime_port: super::PersistencePort = port.clone();
|
||||
let handle = match super::start_foundation_with_port_and_source_spawner(
|
||||
settings,
|
||||
tokio::runtime::Handle::current(),
|
||||
std::option::Option::Some(runtime_port),
|
||||
move |children, _stop_receiver, admission_sender| {
|
||||
let _abort_handle = children.spawn(async move {
|
||||
let ingress = match runtime_ingress(&network, 11) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let _sent = admission_sender.send(ingress).await;
|
||||
return;
|
||||
});
|
||||
},
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let terminal = handle.wait_terminal().await;
|
||||
let terminal = match terminal {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
assert_eq!(terminal, ksp_worker_api::WorkerState::Faulted(crate::ERROR_CODE_RAW_TRANSACTION_INGEST_CONTENT_CONFLICT));
|
||||
assert_eq!(port.completed.load(std::sync::atomic::Ordering::Acquire), 1);
|
||||
return;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "current_thread")]
|
||||
async fn pre_007_store_failure_becomes_terminal_without_exposing_remote_error_text() {
|
||||
let settings = match settings_with_persistence_concurrency(1) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let network = settings.network().clone();
|
||||
let port = std::sync::Arc::new(RuntimePersistencePort::new(network.clone(), RuntimePortResponse::StoreFailure, true));
|
||||
let runtime_port: super::PersistencePort = port.clone();
|
||||
let handle = match super::start_foundation_with_port_and_source_spawner(
|
||||
settings,
|
||||
tokio::runtime::Handle::current(),
|
||||
std::option::Option::Some(runtime_port),
|
||||
move |children, _stop_receiver, admission_sender| {
|
||||
let _abort_handle = children.spawn(async move {
|
||||
let ingress = match runtime_ingress(&network, 12) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => return,
|
||||
};
|
||||
let _sent = admission_sender.send(ingress).await;
|
||||
return;
|
||||
});
|
||||
},
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let terminal = handle.wait_terminal().await;
|
||||
let terminal = match terminal {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
assert_eq!(terminal, ksp_worker_api::WorkerState::Faulted(crate::ERROR_CODE_RAW_TRANSACTION_INGEST_STORE_FAILED));
|
||||
assert_eq!(port.completed.load(std::sync::atomic::Ordering::Acquire), 1);
|
||||
assert!(!std::format!("{handle:?}").contains("synthetic store failure"));
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user