v0.3.15-pre.009
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/src/app_state.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
//! Shared backend state owned by the Raw Transaction Ingest Desk Tauri application.
|
||||
|
||||
@@ -115,7 +115,7 @@ impl crate::AppState {
|
||||
return crate::validate_route_start(&self.config_management, generation, request);
|
||||
}
|
||||
|
||||
/// Starts one real mono-route Store + Worker runtime after repeating the complete Start-time revalidation.
|
||||
/// Starts one independent route Worker after repeating complete Start-time revalidation, sharing Store only on an exact network match.
|
||||
pub(crate) async fn start_route(&self, request: &crate::RawIngestRouteStartRequestDto) -> ksp_core_lib::Result<crate::RouteRuntimeLaunch> {
|
||||
let generation = {
|
||||
let generation = self.inventory_generation.lock();
|
||||
@@ -137,9 +137,9 @@ impl crate::AppState {
|
||||
return crate::start_route_runtime(std::sync::Arc::clone(&self.route_runtime), prepared).await;
|
||||
}
|
||||
|
||||
/// Requests cooperative Stop and waits until the Worker is terminal and Store cleanup has released the mono-route slot.
|
||||
pub(crate) async fn stop_route(&self) -> ksp_core_lib::Result<crate::RawIngestRouteRuntimeDto> {
|
||||
return self.route_runtime.stop_and_wait().await;
|
||||
/// Requests cooperative Stop for one exact logical route and waits until its Worker cleanup is complete.
|
||||
pub(crate) async fn stop_route(&self, request: &crate::RawIngestRouteStopRequestDto) -> ksp_core_lib::Result<crate::RawIngestRouteRuntimeDto> {
|
||||
return self.route_runtime.stop_and_wait(request).await;
|
||||
}
|
||||
|
||||
/// Builds the safe scaffold status exposed by the shell.
|
||||
@@ -175,7 +175,7 @@ impl crate::AppState {
|
||||
application_version: env!("CARGO_PKG_VERSION").to_owned(),
|
||||
config_document_count: document_count,
|
||||
fallback_logging_active: runtime.fallback_active,
|
||||
shell_phase: "pre.008-mono-route-runtime".to_owned(),
|
||||
shell_phase: "pre.009-multi-route-mainnet".to_owned(),
|
||||
startup_diagnostic: runtime.startup_diagnostic.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/src/constants.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
//! Application-owned tracing targets and Config component identifiers.
|
||||
|
||||
@@ -21,7 +21,7 @@ pub(crate) const TRACING_DOMAIN_BOOTSTRAP: &str = "raw_transaction_ingest.bootst
|
||||
pub(crate) const TRACING_DOMAIN_FRONTEND: &str = "frontend";
|
||||
/// Structured domain used while rebuilding Config-only route inventory.
|
||||
pub(crate) const TRACING_DOMAIN_INVENTORY: &str = "raw_transaction_ingest.inventory";
|
||||
/// Structured domain used by mono-route Store and Worker lifecycle operations.
|
||||
/// Structured domain used by multi-route shared-Store and Worker lifecycle operations.
|
||||
pub(crate) const TRACING_DOMAIN_ROUTE_RUNTIME: &str = "raw_transaction_ingest.route_runtime";
|
||||
/// Structured domain used while revalidating future Start requests and reconstructing route resources.
|
||||
pub(crate) const TRACING_DOMAIN_ROUTE_START: &str = "raw_transaction_ingest.route_start";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/src/dto_route.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
//! Safe route-identity, Config-composability and lifecycle DTOs for Raw Transaction Ingest Desk.
|
||||
|
||||
@@ -10,7 +10,7 @@ use ts_rs::TS; // rust-rules: trait-import
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_raw_transaction_ingest_desk/dto_route/RawIngestRouteId.ts")]
|
||||
pub(crate) enum RawIngestRouteId {
|
||||
/// Yellowstone Subscribe plus same-network HTTP hydration.
|
||||
/// Yellowstone block subscription plus same-network HTTP block reconciliation.
|
||||
YellowstoneHydrated,
|
||||
/// Standard Solana logs subscription plus same-network HTTP hydration.
|
||||
StandardLogsHydrated,
|
||||
@@ -45,7 +45,7 @@ impl crate::RawIngestRouteId {
|
||||
#[must_use]
|
||||
pub(crate) const fn label(self) -> &'static str {
|
||||
return match self {
|
||||
Self::YellowstoneHydrated => "Yellowstone + HTTP hydration",
|
||||
Self::YellowstoneHydrated => "Yellowstone Block + HTTP block reconciliation",
|
||||
Self::StandardLogsHydrated => "Standard Logs + HTTP hydration",
|
||||
Self::StandardBlockDirect => "Standard Block direct",
|
||||
Self::HeliusTransactionHydrated => "Helius Transaction + HTTP hydration",
|
||||
@@ -112,6 +112,17 @@ pub(crate) struct RawIngestRouteStartRequestDto {
|
||||
pub(crate) route_id: crate::RawIngestRouteId,
|
||||
}
|
||||
|
||||
/// Safe targeted Stop request containing only the logical Worker route identity.
|
||||
#[derive(Clone, Debug, serde::Deserialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_raw_transaction_ingest_desk/dto_route/RawIngestRouteStopRequestDto.ts")]
|
||||
pub(crate) struct RawIngestRouteStopRequestDto {
|
||||
/// Safe logical network-profile identifier that owns the Worker route.
|
||||
pub(crate) profile_id: String,
|
||||
/// Stable logical route identifier to stop.
|
||||
pub(crate) route_id: crate::RawIngestRouteId,
|
||||
}
|
||||
|
||||
/// Safe acknowledgement that exact Start-time Transport resources were reconstructed and validated without Worker launch.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -129,7 +140,7 @@ pub(crate) struct RawIngestRouteStartValidationDto {
|
||||
pub(crate) route_id: crate::RawIngestRouteId,
|
||||
}
|
||||
|
||||
/// Safe mono-route runtime acknowledgement containing only logical identity and lifecycle state.
|
||||
/// Safe per-route runtime acknowledgement containing only logical identity and lifecycle state.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_raw_transaction_ingest_desk/dto_route/RawIngestRouteRuntimeDto.ts")]
|
||||
@@ -142,7 +153,7 @@ pub(crate) struct RawIngestRouteRuntimeDto {
|
||||
pub(crate) network: String,
|
||||
/// Safe logical network-profile identifier.
|
||||
pub(crate) profile_id: String,
|
||||
/// Exact logical route owned by the mono-route Worker.
|
||||
/// Exact logical route owned by the independent Worker.
|
||||
pub(crate) route_id: crate::RawIngestRouteId,
|
||||
/// Safe application-owned projection of current Worker lifecycle.
|
||||
pub(crate) state: crate::RawIngestRouteState,
|
||||
@@ -174,6 +185,8 @@ pub(crate) enum RawIngestRouteUnavailableReason {
|
||||
ProfileUnresolved,
|
||||
/// Transport and Store do not prove one identical logical network.
|
||||
NetworkMismatch,
|
||||
/// No HTTP role can satisfy `getBlock` for same-network block reconciliation.
|
||||
MissingHttpGetBlock,
|
||||
/// No HTTP role can satisfy `getTransaction` for same-network hydration.
|
||||
MissingHttpGetTransaction,
|
||||
/// No HTTP role can satisfy the complete block-polling method set.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/src/errors.rs
|
||||
// version: 4
|
||||
// version: 5
|
||||
|
||||
//! Stable Raw Transaction Ingest Desk application error codes.
|
||||
|
||||
@@ -26,11 +26,14 @@ pub(crate) const ERROR_CODE_ROUTE_INVENTORY_INVALID: ksp_core_lib::ErrorCode =
|
||||
/// Exact Start-time Transport resources cannot be reconstructed safely.
|
||||
pub(crate) const ERROR_CODE_ROUTE_START_PREPARATION_FAILED: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "route_start_preparation_failed");
|
||||
/// A mono-route Worker runtime already owns the application slot.
|
||||
/// The selected logical route is already starting/active or shared Store lifecycle is temporarily exclusive.
|
||||
pub(crate) const ERROR_CODE_ROUTE_RUNTIME_ACTIVE: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "route_runtime_active");
|
||||
/// No mono-route Worker runtime is currently active.
|
||||
/// No selected route Worker runtime is currently active.
|
||||
pub(crate) const ERROR_CODE_ROUTE_RUNTIME_NOT_ACTIVE: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "route_runtime_not_active");
|
||||
/// A selected route cannot share the currently open Store because its network differs.
|
||||
pub(crate) const ERROR_CODE_ROUTE_RUNTIME_NETWORK_MISMATCH: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "route_runtime_network_mismatch");
|
||||
/// Store or Worker startup failed after Start-time route revalidation.
|
||||
pub(crate) const ERROR_CODE_ROUTE_RUNTIME_START_FAILED: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "route_runtime_start_failed");
|
||||
@@ -40,7 +43,7 @@ pub(crate) const ERROR_CODE_ROUTE_RUNTIME_STORE_NOT_READY: ksp_core_lib::ErrorCo
|
||||
/// The Store could not be reclaimed or explicitly closed after Worker termination.
|
||||
pub(crate) const ERROR_CODE_ROUTE_RUNTIME_STORE_SHUTDOWN_FAILED: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "route_runtime_store_shutdown_failed");
|
||||
/// The mono-route runtime reservation/handle state changed unexpectedly.
|
||||
/// The multi-route runtime reservation/handle state changed unexpectedly.
|
||||
pub(crate) const ERROR_CODE_ROUTE_RUNTIME_STATE_INVALID: ksp_core_lib::ErrorCode =
|
||||
ksp_core_lib::ErrorCode::new("raw_transaction_ingest_desk", "route_runtime_state_invalid");
|
||||
/// Start request references an inventory generation that is no longer current.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/src/lib.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
//! Tauri desktop application scaffold for composing and supervising KSP live RAW transaction ingest routes.
|
||||
|
||||
@@ -58,7 +58,7 @@ pub(crate) use self::constants::TRACING_DOMAIN_BOOTSTRAP;
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_FRONTEND;
|
||||
/// Structured domain used while rebuilding Config-only route inventory.
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_INVENTORY;
|
||||
/// Structured domain used by mono-route Store and Worker lifecycle operations.
|
||||
/// Structured domain used by multi-route shared-Store and Worker lifecycle operations.
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_ROUTE_RUNTIME;
|
||||
/// Structured domain used while revalidating future Start requests.
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_ROUTE_START;
|
||||
@@ -92,7 +92,7 @@ pub(crate) use self::dto_route::RawIngestRouteFoundationDto;
|
||||
pub(crate) use self::dto_route::RawIngestRouteId;
|
||||
/// Complete deterministic Config-only route inventory.
|
||||
pub(crate) use self::dto_route::RawIngestRouteInventoryDto;
|
||||
/// Safe mono-route runtime acknowledgement containing only logical identity and lifecycle.
|
||||
/// Safe per-route runtime acknowledgement containing only logical identity and lifecycle.
|
||||
pub(crate) use self::dto_route::RawIngestRouteRuntimeDto;
|
||||
/// Safe future Start request containing only logical route selection and inventory generation.
|
||||
pub(crate) use self::dto_route::RawIngestRouteStartRequestDto;
|
||||
@@ -100,6 +100,8 @@ pub(crate) use self::dto_route::RawIngestRouteStartRequestDto;
|
||||
pub(crate) use self::dto_route::RawIngestRouteStartValidationDto;
|
||||
/// Stable route lifecycle states reserved for runtime projections.
|
||||
pub(crate) use self::dto_route::RawIngestRouteState;
|
||||
/// Safe targeted Stop request containing only logical route identity.
|
||||
pub(crate) use self::dto_route::RawIngestRouteStopRequestDto;
|
||||
/// Safe reason explaining why one logical route is not composable from Config.
|
||||
pub(crate) use self::dto_route::RawIngestRouteUnavailableReason;
|
||||
/// Shared Raw Transaction Ingest Desk application state is internally inconsistent.
|
||||
@@ -116,13 +118,15 @@ pub(crate) use self::errors::ERROR_CODE_FRONTEND_LOG_TARGET_INVALID;
|
||||
pub(crate) use self::errors::ERROR_CODE_LOGGING_BOOTSTRAP_FAILED;
|
||||
/// Config-only route inventory cannot be projected safely.
|
||||
pub(crate) use self::errors::ERROR_CODE_ROUTE_INVENTORY_INVALID;
|
||||
/// A mono-route Worker runtime already owns the application slot.
|
||||
/// A Worker runtime already owns the same logical route in the shared application runtime.
|
||||
pub(crate) use self::errors::ERROR_CODE_ROUTE_RUNTIME_ACTIVE;
|
||||
/// No mono-route Worker runtime is currently active.
|
||||
/// Selected route cannot share the currently open Store because its network differs.
|
||||
pub(crate) use self::errors::ERROR_CODE_ROUTE_RUNTIME_NETWORK_MISMATCH;
|
||||
/// No selected route Worker runtime is currently active.
|
||||
pub(crate) use self::errors::ERROR_CODE_ROUTE_RUNTIME_NOT_ACTIVE;
|
||||
/// Store or Worker startup failed after Start-time route revalidation.
|
||||
pub(crate) use self::errors::ERROR_CODE_ROUTE_RUNTIME_START_FAILED;
|
||||
/// The mono-route runtime reservation/handle state changed unexpectedly.
|
||||
/// The multi-route runtime reservation or handle state changed unexpectedly.
|
||||
pub(crate) use self::errors::ERROR_CODE_ROUTE_RUNTIME_STATE_INVALID;
|
||||
/// The opened Store did not prove Ready before Worker launch.
|
||||
pub(crate) use self::errors::ERROR_CODE_ROUTE_RUNTIME_STORE_NOT_READY;
|
||||
@@ -154,9 +158,9 @@ pub(crate) use self::logging_runtime::launch_identity;
|
||||
pub(crate) use self::route_inventory::build_route_inventory;
|
||||
/// Rebuilds the route inventory from one already captured Config environment snapshot.
|
||||
pub(crate) use self::route_inventory::build_route_inventory_with_environment;
|
||||
/// Launch ownership for one mono-route Worker and its caller-owned Store.
|
||||
/// Launch ownership for one independent route Worker sharing the application Store when eligible.
|
||||
pub(crate) use self::route_runtime::RouteRuntimeLaunch;
|
||||
/// Shared mono-route runtime state admitting at most one starting or active Worker.
|
||||
/// Shared multi-route runtime state with one independent Worker per route and one same-network Store.
|
||||
pub(crate) use self::route_runtime::RouteRuntimeState;
|
||||
/// Opens Store and starts one exact route Worker after Start-time revalidation.
|
||||
pub(crate) use self::route_runtime::start_route_runtime;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/src/route_inventory.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
//! Config-only logical route inventory for Raw Transaction Ingest Desk.
|
||||
|
||||
@@ -18,6 +18,7 @@ struct RouteCapabilities {
|
||||
has_helius_transaction: bool,
|
||||
helius_source_declared: bool,
|
||||
helius_source_missing_secret: bool,
|
||||
yellowstone_has_get_block: bool,
|
||||
yellowstone_source_missing_secret: bool,
|
||||
}
|
||||
|
||||
@@ -236,6 +237,9 @@ fn build_profile_inventory(
|
||||
crate::RawIngestRouteUnavailableReason::NetworkMismatch,
|
||||
);
|
||||
}
|
||||
if component_id == crate::COMPOSITE_COMPONENT_ID_TRANSPORT_YELLOWSTONE {
|
||||
capabilities.yellowstone_has_get_block = http_role_supports_methods(resolved.http_settings(), &["getBlock"]);
|
||||
}
|
||||
capabilities.absorb_transport(&resolved, network.as_str());
|
||||
}
|
||||
let routes = build_configured_profile_routes(&capabilities, network.as_str());
|
||||
@@ -268,7 +272,7 @@ fn build_configured_profile_routes(capabilities: &RouteCapabilities, network: &s
|
||||
network,
|
||||
&[
|
||||
(capabilities.has_yellowstone, yellowstone_reason),
|
||||
(capabilities.has_get_transaction, crate::RawIngestRouteUnavailableReason::MissingHttpGetTransaction),
|
||||
(capabilities.yellowstone_has_get_block, crate::RawIngestRouteUnavailableReason::MissingHttpGetBlock),
|
||||
],
|
||||
),
|
||||
route_with_requirements(
|
||||
|
||||
@@ -1,29 +1,41 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/src/route_runtime.rs
|
||||
// version: 4
|
||||
// version: 5
|
||||
|
||||
//! Mono-route Store and Worker lifecycle owned by Raw Transaction Ingest Desk.
|
||||
//! Multi-route Store and independent Worker lifecycle owned by Raw Transaction Ingest Desk.
|
||||
|
||||
const ROUTE_STOP_CLEANUP_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(2);
|
||||
const STORE_RECLAIM_POLL_INTERVAL: std::time::Duration = std::time::Duration::from_millis(5);
|
||||
const STORE_RECLAIM_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(1);
|
||||
|
||||
struct RouteRuntimeInner {
|
||||
last_terminal: std::option::Option<crate::RawIngestRouteRuntimeDto>,
|
||||
last_terminals: std::vec::Vec<crate::RawIngestRouteRuntimeDto>,
|
||||
network: std::option::Option<String>,
|
||||
next_sequence: u64,
|
||||
slot: RouteRuntimeSlot,
|
||||
routes: std::vec::Vec<RouteRuntimeSlot>,
|
||||
store: std::option::Option<std::sync::Arc<ksp_store_lib::Store>>,
|
||||
store_closing_token: std::option::Option<u64>,
|
||||
store_opening_token: std::option::Option<u64>,
|
||||
}
|
||||
|
||||
/// Shared mono-route runtime state. `pre.008` admits at most one active or starting Worker.
|
||||
/// Shared multi-route runtime state. Each route owns an independent Worker while same-network routes share one Store.
|
||||
pub(crate) struct RouteRuntimeState {
|
||||
inner: std::sync::Mutex<RouteRuntimeInner>,
|
||||
}
|
||||
|
||||
impl crate::RouteRuntimeState {
|
||||
/// Creates an idle mono-route runtime state.
|
||||
/// Creates an idle multi-route runtime state without Store or Worker ownership.
|
||||
#[must_use]
|
||||
pub(crate) fn new() -> Self {
|
||||
return Self {
|
||||
inner: std::sync::Mutex::new(RouteRuntimeInner { last_terminal: std::option::Option::None, next_sequence: 0, slot: RouteRuntimeSlot::Idle }),
|
||||
inner: std::sync::Mutex::new(RouteRuntimeInner {
|
||||
last_terminals: std::vec::Vec::new(),
|
||||
network: std::option::Option::None,
|
||||
next_sequence: 0,
|
||||
routes: std::vec::Vec::new(),
|
||||
store: std::option::Option::None,
|
||||
store_closing_token: std::option::Option::None,
|
||||
store_opening_token: std::option::Option::None,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -33,14 +45,34 @@ impl crate::RouteRuntimeState {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(runtime_lock_error()),
|
||||
};
|
||||
if !matches!(inner.slot, RouteRuntimeSlot::Idle) {
|
||||
if inner.store_closing_token.is_some() {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_ACTIVE,
|
||||
"Raw Transaction Ingest Desk already owns one mono-route Worker runtime",
|
||||
"Raw Transaction Ingest Desk Store is still closing after the previous route set",
|
||||
));
|
||||
}
|
||||
let sequence = inner.next_sequence.checked_add(1);
|
||||
let sequence = match sequence {
|
||||
if inner.routes.iter().any(|slot| return slot.matches_identity(prepared.profile_id.as_str(), prepared.route_id)) {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_ACTIVE,
|
||||
"Raw Transaction Ingest Desk already owns one Worker for the selected logical route",
|
||||
));
|
||||
}
|
||||
if let std::option::Option::Some(network) = inner.network.as_deref()
|
||||
&& network != prepared.network.as_str()
|
||||
{
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_NETWORK_MISMATCH,
|
||||
"Raw Transaction Ingest Desk cannot share one Store across different logical networks",
|
||||
));
|
||||
}
|
||||
let needs_store_open = inner.store.is_none();
|
||||
if needs_store_open && inner.store_opening_token.is_some() {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_ACTIVE,
|
||||
"Raw Transaction Ingest Desk same-network Store is still opening",
|
||||
));
|
||||
}
|
||||
let sequence = match inner.next_sequence.checked_add(1) {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
@@ -66,10 +98,55 @@ impl crate::RouteRuntimeState {
|
||||
profile_id: prepared.profile_id.clone(),
|
||||
route_id: prepared.route_id,
|
||||
};
|
||||
inner.last_terminal = std::option::Option::None;
|
||||
if inner.network.is_none() {
|
||||
inner.last_terminals.clear();
|
||||
inner.network = std::option::Option::Some(prepared.network.clone());
|
||||
}
|
||||
inner.last_terminals.retain(|terminal| return !identity.matches_dto(terminal));
|
||||
inner.next_sequence = sequence;
|
||||
inner.slot = RouteRuntimeSlot::Starting { token: sequence };
|
||||
return std::result::Result::Ok(RouteRuntimeReservation { identity, token: sequence, worker_id });
|
||||
if needs_store_open {
|
||||
inner.store_opening_token = std::option::Option::Some(sequence);
|
||||
}
|
||||
inner.routes.push(RouteRuntimeSlot::Starting { identity: identity.clone(), token: sequence });
|
||||
return std::result::Result::Ok(RouteRuntimeReservation { identity, needs_store_open, token: sequence, worker_id });
|
||||
}
|
||||
|
||||
fn install_store(&self, token: u64, store: std::sync::Arc<ksp_store_lib::Store>) -> ksp_core_lib::Result<()> {
|
||||
let inner = self.inner.lock();
|
||||
let mut inner = match inner {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(runtime_lock_error()),
|
||||
};
|
||||
if inner.store_opening_token != std::option::Option::Some(token) || inner.store.is_some() {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_STATE_INVALID,
|
||||
"Raw Transaction Ingest Desk shared Store reservation changed before installation",
|
||||
));
|
||||
}
|
||||
inner.store = std::option::Option::Some(store);
|
||||
inner.store_opening_token = std::option::Option::None;
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
fn shared_store(&self, expected_network: &str) -> ksp_core_lib::Result<std::sync::Arc<ksp_store_lib::Store>> {
|
||||
let inner = self.inner.lock();
|
||||
let inner = match inner {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(runtime_lock_error()),
|
||||
};
|
||||
if inner.network.as_deref() != std::option::Option::Some(expected_network) {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_NETWORK_MISMATCH,
|
||||
"Raw Transaction Ingest Desk shared Store network no longer matches the selected route",
|
||||
));
|
||||
}
|
||||
return match &inner.store {
|
||||
std::option::Option::Some(store) => std::result::Result::Ok(std::sync::Arc::clone(store)),
|
||||
std::option::Option::None => std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_STATE_INVALID,
|
||||
"Raw Transaction Ingest Desk shared Store is not installed",
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
fn activate(
|
||||
@@ -82,117 +159,226 @@ impl crate::RouteRuntimeState {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(runtime_lock_error()),
|
||||
};
|
||||
match &inner.slot {
|
||||
RouteRuntimeSlot::Starting { token } if *token == reservation.token => {},
|
||||
RouteRuntimeSlot::Idle | RouteRuntimeSlot::Starting { .. } | RouteRuntimeSlot::Active { .. } => {
|
||||
let position = inner.routes.iter().position(|slot| return slot.starting_token() == std::option::Option::Some(reservation.token));
|
||||
let position = match position {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_STATE_INVALID,
|
||||
"Raw Transaction Ingest Desk mono-route runtime reservation changed before Worker activation",
|
||||
"Raw Transaction Ingest Desk route reservation changed before Worker activation",
|
||||
));
|
||||
},
|
||||
}
|
||||
};
|
||||
let state = project_worker_state(handle.snapshot_source().current().worker_snapshot().state());
|
||||
inner.slot = RouteRuntimeSlot::Active { handle, identity: reservation.identity.clone(), token: reservation.token };
|
||||
inner.routes[position] = RouteRuntimeSlot::Active { handle, identity: reservation.identity.clone(), token: reservation.token };
|
||||
return std::result::Result::Ok(reservation.identity.dto(state));
|
||||
}
|
||||
|
||||
fn rollback(&self, token: u64) {
|
||||
fn rollback(&self, token: u64) -> ksp_core_lib::Result<std::option::Option<std::sync::Arc<ksp_store_lib::Store>>> {
|
||||
let inner = self.inner.lock();
|
||||
let mut inner = match inner {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(runtime_lock_error()),
|
||||
};
|
||||
if matches!(&inner.slot, RouteRuntimeSlot::Starting { token: current, .. } if *current == token) {
|
||||
inner.slot = RouteRuntimeSlot::Idle;
|
||||
inner.routes.retain(|slot| return slot.token() != token);
|
||||
if inner.store_opening_token == std::option::Option::Some(token) {
|
||||
inner.store_opening_token = std::option::Option::None;
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(&self, token: u64, terminal: crate::RawIngestRouteRuntimeDto) {
|
||||
let inner = self.inner.lock();
|
||||
let mut inner = match inner {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
if matches!(&inner.slot, RouteRuntimeSlot::Active { token: current, .. } if *current == token) {
|
||||
inner.last_terminal = std::option::Option::Some(terminal);
|
||||
inner.slot = RouteRuntimeSlot::Idle;
|
||||
}
|
||||
}
|
||||
|
||||
/// Requests cooperative Stop, waits for terminal Worker state and returns after the terminal monitor has released the mono-route slot.
|
||||
pub(crate) async fn stop_and_wait(&self) -> ksp_core_lib::Result<crate::RawIngestRouteRuntimeDto> {
|
||||
let active = {
|
||||
let inner = self.inner.lock();
|
||||
let inner = match inner {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(runtime_lock_error()),
|
||||
};
|
||||
match &inner.slot {
|
||||
RouteRuntimeSlot::Active { handle, identity, .. } => std::result::Result::Ok((handle.clone(), identity.clone())),
|
||||
RouteRuntimeSlot::Idle => match &inner.last_terminal {
|
||||
std::option::Option::Some(terminal) => return std::result::Result::Ok(terminal.clone()),
|
||||
std::option::Option::None => std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_NOT_ACTIVE,
|
||||
"Raw Transaction Ingest Desk has no active mono-route Worker to stop",
|
||||
)),
|
||||
},
|
||||
RouteRuntimeSlot::Starting { .. } => std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_NOT_ACTIVE,
|
||||
"Raw Transaction Ingest Desk mono-route Worker is still starting and cannot be stopped yet",
|
||||
)),
|
||||
if inner.routes.is_empty() {
|
||||
let store = inner.store.take();
|
||||
if store.is_some() {
|
||||
inner.store_closing_token = std::option::Option::Some(token);
|
||||
} else {
|
||||
inner.network = std::option::Option::None;
|
||||
}
|
||||
return std::result::Result::Ok(store);
|
||||
}
|
||||
return std::result::Result::Ok(std::option::Option::None);
|
||||
}
|
||||
|
||||
fn finish(&self, token: u64, terminal: crate::RawIngestRouteRuntimeDto) -> ksp_core_lib::Result<std::option::Option<std::sync::Arc<ksp_store_lib::Store>>> {
|
||||
let inner = self.inner.lock();
|
||||
let mut inner = match inner {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(runtime_lock_error()),
|
||||
};
|
||||
let (handle, identity) = match active {
|
||||
let before = inner.routes.len();
|
||||
inner.routes.retain(|slot| return slot.token() != token);
|
||||
if inner.routes.len() == before {
|
||||
return std::result::Result::Ok(std::option::Option::None);
|
||||
}
|
||||
inner.last_terminals.retain(|previous| return previous.profile_id != terminal.profile_id || previous.route_id != terminal.route_id);
|
||||
inner.last_terminals.push(terminal);
|
||||
if inner.routes.is_empty() {
|
||||
let store = inner.store.take();
|
||||
if store.is_some() {
|
||||
inner.store_closing_token = std::option::Option::Some(token);
|
||||
} else {
|
||||
inner.network = std::option::Option::None;
|
||||
}
|
||||
return std::result::Result::Ok(store);
|
||||
}
|
||||
return std::result::Result::Ok(std::option::Option::None);
|
||||
}
|
||||
|
||||
fn complete_store_close(&self, token: u64) {
|
||||
let inner = self.inner.lock();
|
||||
let mut inner = match inner {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
if inner.store_closing_token == std::option::Option::Some(token) {
|
||||
inner.store_closing_token = std::option::Option::None;
|
||||
if inner.routes.is_empty() && inner.store.is_none() && inner.store_opening_token.is_none() {
|
||||
inner.network = std::option::Option::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Requests cooperative Stop for one exact logical route and waits for its Worker cleanup.
|
||||
pub(crate) async fn stop_and_wait(&self, request: &crate::RawIngestRouteStopRequestDto) -> ksp_core_lib::Result<crate::RawIngestRouteRuntimeDto> {
|
||||
let target = self.stop_target(request);
|
||||
let target = match target {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let (handle, identity, token) = match target {
|
||||
RouteStopTarget::Active { handle, identity, token } => (handle, identity, token),
|
||||
RouteStopTarget::Terminal(value) => return std::result::Result::Ok(value),
|
||||
RouteStopTarget::TerminalClosing => return self.wait_terminal_store_cleanup(request).await,
|
||||
};
|
||||
let _first_stop_request = handle.request_stop();
|
||||
let terminal = handle.wait_terminal().await;
|
||||
let terminal = match terminal {
|
||||
if let std::result::Result::Err(error) = handle.wait_terminal().await {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_ROUTE_RUNTIME_STATE_INVALID, "Cannot observe terminal Raw Transaction Ingest Desk Worker state")
|
||||
.with_source(error),
|
||||
);
|
||||
}
|
||||
return self.wait_route_cleanup(token, &identity).await;
|
||||
}
|
||||
|
||||
fn stop_target(&self, request: &crate::RawIngestRouteStopRequestDto) -> ksp_core_lib::Result<RouteStopTarget> {
|
||||
let inner = self.inner.lock();
|
||||
let inner = match inner {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_ROUTE_RUNTIME_STATE_INVALID, "Cannot observe terminal Raw Transaction Ingest Desk Worker state")
|
||||
.with_source(error),
|
||||
);
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(runtime_lock_error()),
|
||||
};
|
||||
let slot = inner.routes.iter().find(|slot| return slot.matches_identity(request.profile_id.as_str(), request.route_id));
|
||||
return match slot {
|
||||
std::option::Option::Some(RouteRuntimeSlot::Active { handle, identity, token }) => {
|
||||
std::result::Result::Ok(RouteStopTarget::Active { handle: handle.clone(), identity: identity.clone(), token: *token })
|
||||
},
|
||||
std::option::Option::Some(RouteRuntimeSlot::Starting { .. }) => std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_NOT_ACTIVE,
|
||||
"Raw Transaction Ingest Desk route Worker is still starting and cannot be stopped yet",
|
||||
)),
|
||||
std::option::Option::None => {
|
||||
let terminal =
|
||||
inner.last_terminals.iter().find(|terminal| return terminal.profile_id == request.profile_id && terminal.route_id == request.route_id);
|
||||
match terminal {
|
||||
std::option::Option::Some(value) if inner.store_closing_token.is_none() => {
|
||||
std::result::Result::Ok(RouteStopTarget::Terminal(value.clone()))
|
||||
},
|
||||
std::option::Option::Some(_) => std::result::Result::Ok(RouteStopTarget::TerminalClosing),
|
||||
std::option::Option::None => std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_NOT_ACTIVE,
|
||||
"Raw Transaction Ingest Desk has no active Worker for the selected logical route",
|
||||
)),
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async fn wait_route_cleanup(&self, token: u64, identity: &RouteRuntimeIdentity) -> ksp_core_lib::Result<crate::RawIngestRouteRuntimeDto> {
|
||||
let started = std::time::Instant::now();
|
||||
loop {
|
||||
let idle = self.is_idle();
|
||||
let idle = match idle {
|
||||
let cleanup = self.route_cleanup_complete(token, identity);
|
||||
let cleanup = match cleanup {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
if idle {
|
||||
return std::result::Result::Ok(identity.dto(project_worker_state(terminal)));
|
||||
if let std::option::Option::Some(value) = cleanup {
|
||||
return std::result::Result::Ok(value);
|
||||
}
|
||||
if started.elapsed() >= ROUTE_STOP_CLEANUP_TIMEOUT {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_STORE_SHUTDOWN_FAILED,
|
||||
"Raw Transaction Ingest Desk terminal cleanup did not release the mono-route slot",
|
||||
"Raw Transaction Ingest Desk terminal cleanup did not release the selected route",
|
||||
));
|
||||
}
|
||||
tokio::time::sleep(STORE_RECLAIM_POLL_INTERVAL).await;
|
||||
}
|
||||
}
|
||||
|
||||
fn is_idle(&self) -> ksp_core_lib::Result<bool> {
|
||||
async fn wait_terminal_store_cleanup(&self, request: &crate::RawIngestRouteStopRequestDto) -> ksp_core_lib::Result<crate::RawIngestRouteRuntimeDto> {
|
||||
let started = std::time::Instant::now();
|
||||
loop {
|
||||
let terminal = self.late_terminal_after_store_cleanup(request);
|
||||
let terminal = match terminal {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
if let std::option::Option::Some(value) = terminal {
|
||||
return std::result::Result::Ok(value);
|
||||
}
|
||||
if started.elapsed() >= ROUTE_STOP_CLEANUP_TIMEOUT {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_STORE_SHUTDOWN_FAILED,
|
||||
"Raw Transaction Ingest Desk final shared Store cleanup did not complete for the selected terminal route",
|
||||
));
|
||||
}
|
||||
tokio::time::sleep(STORE_RECLAIM_POLL_INTERVAL).await;
|
||||
}
|
||||
}
|
||||
|
||||
fn late_terminal_after_store_cleanup(
|
||||
&self,
|
||||
request: &crate::RawIngestRouteStopRequestDto,
|
||||
) -> ksp_core_lib::Result<std::option::Option<crate::RawIngestRouteRuntimeDto>> {
|
||||
let inner = self.inner.lock();
|
||||
let inner = match inner {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(runtime_lock_error()),
|
||||
};
|
||||
return std::result::Result::Ok(matches!(inner.slot, RouteRuntimeSlot::Idle));
|
||||
let terminal = inner.last_terminals.iter().find(|terminal| return terminal.profile_id == request.profile_id && terminal.route_id == request.route_id);
|
||||
let terminal = match terminal {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_NOT_ACTIVE,
|
||||
"Raw Transaction Ingest Desk terminal route is no longer retained",
|
||||
));
|
||||
},
|
||||
};
|
||||
if inner.store_closing_token.is_some() {
|
||||
return std::result::Result::Ok(std::option::Option::None);
|
||||
}
|
||||
return std::result::Result::Ok(std::option::Option::Some(terminal.clone()));
|
||||
}
|
||||
|
||||
fn route_cleanup_complete(
|
||||
&self,
|
||||
token: u64,
|
||||
identity: &RouteRuntimeIdentity,
|
||||
) -> ksp_core_lib::Result<std::option::Option<crate::RawIngestRouteRuntimeDto>> {
|
||||
let inner = self.inner.lock();
|
||||
let inner = match inner {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return std::result::Result::Err(runtime_lock_error()),
|
||||
};
|
||||
if inner.routes.iter().any(|slot| return slot.token() == token) || inner.store_closing_token == std::option::Option::Some(token) {
|
||||
return std::result::Result::Ok(std::option::Option::None);
|
||||
}
|
||||
let terminal = inner.last_terminals.iter().find(|value| return identity.matches_dto(value));
|
||||
return std::result::Result::Ok(terminal.cloned());
|
||||
}
|
||||
}
|
||||
|
||||
/// Launch ownership returned only after Store readiness, Worker creation and handle installation have succeeded.
|
||||
/// Launch ownership returned only after shared Store readiness, Worker creation and route-handle installation have succeeded.
|
||||
pub(crate) struct RouteRuntimeLaunch {
|
||||
acknowledgement: crate::RawIngestRouteRuntimeDto,
|
||||
handle: ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestHandle,
|
||||
runtime_state: std::sync::Arc<crate::RouteRuntimeState>,
|
||||
store: std::sync::Arc<ksp_store_lib::Store>,
|
||||
token: u64,
|
||||
}
|
||||
|
||||
@@ -203,7 +389,7 @@ impl crate::RouteRuntimeLaunch {
|
||||
return self.acknowledgement.clone();
|
||||
}
|
||||
|
||||
/// Waits for terminal Worker state, explicitly closes Store after the Worker releases its `Arc`, then frees the mono-route slot.
|
||||
/// Waits for this Worker terminal state, removes only its route and closes Store only after the final same-network route terminates.
|
||||
pub(crate) async fn monitor(self) {
|
||||
let terminal = self.handle.wait_terminal().await;
|
||||
let terminal_state = match terminal {
|
||||
@@ -212,7 +398,7 @@ impl crate::RouteRuntimeLaunch {
|
||||
target: crate::TRACING_TARGET,
|
||||
domain = crate::TRACING_DOMAIN_ROUTE_RUNTIME,
|
||||
worker_state = state.code(),
|
||||
"Raw Transaction Ingest Desk mono-route Worker reached terminal state"
|
||||
"Raw Transaction Ingest Desk route Worker reached terminal state"
|
||||
);
|
||||
project_worker_state(state)
|
||||
},
|
||||
@@ -227,64 +413,99 @@ impl crate::RouteRuntimeLaunch {
|
||||
crate::RawIngestRouteState::Faulted
|
||||
},
|
||||
};
|
||||
let close = close_store_arc(self.store).await;
|
||||
if let std::result::Result::Err(error) = close {
|
||||
ksp_logging_lib::warn!(
|
||||
target: crate::TRACING_TARGET,
|
||||
domain = crate::TRACING_DOMAIN_ROUTE_RUNTIME,
|
||||
error_domain = error.code().domain(),
|
||||
error_code = error.code().code(),
|
||||
"Raw Transaction Ingest Desk Store shutdown failed after terminal Worker"
|
||||
);
|
||||
}
|
||||
let mut terminal = self.acknowledgement;
|
||||
terminal.state = terminal_state;
|
||||
self.runtime_state.finish(self.token, terminal);
|
||||
let store = self.runtime_state.finish(self.token, terminal);
|
||||
let store = match store {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
ksp_logging_lib::warn!(
|
||||
target: crate::TRACING_TARGET,
|
||||
domain = crate::TRACING_DOMAIN_ROUTE_RUNTIME,
|
||||
error_domain = error.code().domain(),
|
||||
error_code = error.code().code(),
|
||||
"Raw Transaction Ingest Desk route cleanup state update failed"
|
||||
);
|
||||
return;
|
||||
},
|
||||
};
|
||||
if let std::option::Option::Some(store) = store {
|
||||
let close = close_store_arc(store).await;
|
||||
if let std::result::Result::Err(error) = close {
|
||||
ksp_logging_lib::warn!(
|
||||
target: crate::TRACING_TARGET,
|
||||
domain = crate::TRACING_DOMAIN_ROUTE_RUNTIME,
|
||||
error_domain = error.code().domain(),
|
||||
error_code = error.code().code(),
|
||||
"Raw Transaction Ingest Desk shared Store shutdown failed after final Worker"
|
||||
);
|
||||
}
|
||||
self.runtime_state.complete_store_close(self.token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Opens one Store, starts one exact Worker route and atomically installs its handle in the mono-route application slot.
|
||||
/// Starts one exact route Worker and reuses the same ready Store for any already-active same-network route.
|
||||
pub(crate) async fn start_route_runtime(
|
||||
runtime_state: std::sync::Arc<crate::RouteRuntimeState>,
|
||||
prepared: crate::PreparedRouteStart,
|
||||
) -> ksp_core_lib::Result<crate::RouteRuntimeLaunch> {
|
||||
let reservation = runtime_state.reserve(&prepared);
|
||||
let reservation = match reservation {
|
||||
let reservation = match runtime_state.reserve(&prepared) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let store = ksp_store_lib::Store::open(prepared.store_settings).await;
|
||||
let store = match store {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
runtime_state.rollback(reservation.token);
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_ROUTE_RUNTIME_START_FAILED, "Cannot open Raw Transaction Ingest Desk Store runtime")
|
||||
let store = if reservation.needs_store_open {
|
||||
let store = ksp_store_lib::Store::open(prepared.store_settings).await;
|
||||
let store = match store {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
let _rollback = runtime_state.rollback(reservation.token);
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_ROUTE_RUNTIME_START_FAILED, "Cannot open Raw Transaction Ingest Desk shared Store runtime")
|
||||
.with_source(error),
|
||||
);
|
||||
},
|
||||
};
|
||||
let health = store.health().await;
|
||||
if health.state() != ksp_store_lib::StoreHealthState::Ready {
|
||||
let close = store.close().await;
|
||||
let _rollback = runtime_state.rollback(reservation.token);
|
||||
if let std::result::Result::Err(error) = close {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_STORE_SHUTDOWN_FAILED,
|
||||
"Cannot close non-ready Raw Transaction Ingest Desk shared Store runtime",
|
||||
)
|
||||
.with_source(error),
|
||||
);
|
||||
},
|
||||
};
|
||||
let health = store.health().await;
|
||||
if health.state() != ksp_store_lib::StoreHealthState::Ready {
|
||||
let close = store.close().await;
|
||||
runtime_state.rollback(reservation.token);
|
||||
if let std::result::Result::Err(error) = close {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_STORE_SHUTDOWN_FAILED,
|
||||
"Cannot close non-ready Raw Transaction Ingest Desk Store runtime",
|
||||
)
|
||||
.with_source(error),
|
||||
);
|
||||
);
|
||||
}
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_STORE_NOT_READY,
|
||||
"Raw Transaction Ingest Desk shared Store readiness probe did not prove Ready",
|
||||
));
|
||||
}
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_STORE_NOT_READY,
|
||||
"Raw Transaction Ingest Desk Store readiness probe did not prove Ready",
|
||||
));
|
||||
}
|
||||
let store = std::sync::Arc::new(store);
|
||||
if let std::result::Result::Err(error) = runtime_state.install_store(reservation.token, std::sync::Arc::clone(&store)) {
|
||||
let close = close_store_arc(store).await;
|
||||
let _rollback = runtime_state.rollback(reservation.token);
|
||||
if let std::result::Result::Err(close_error) = close {
|
||||
return std::result::Result::Err(close_error);
|
||||
}
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
store
|
||||
} else {
|
||||
std::mem::drop(prepared.store_settings);
|
||||
match runtime_state.shared_store(prepared.network.as_str()) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
let _rollback = runtime_state.rollback(reservation.token);
|
||||
return std::result::Result::Err(error);
|
||||
},
|
||||
}
|
||||
};
|
||||
let network = store.runtime_snapshot().network().clone();
|
||||
let settings = ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestSettings::with_defaults(network, reservation.worker_id.clone());
|
||||
let store = std::sync::Arc::new(store);
|
||||
let handle = ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestWorker::start_with_runtime_resources(
|
||||
settings,
|
||||
std::sync::Arc::clone(&store),
|
||||
@@ -293,13 +514,21 @@ pub(crate) async fn start_route_runtime(
|
||||
let handle = match handle {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
let close = close_store_arc(store).await;
|
||||
runtime_state.rollback(reservation.token);
|
||||
if let std::result::Result::Err(close_error) = close {
|
||||
return std::result::Result::Err(close_error);
|
||||
let cleanup_store = runtime_state.rollback(reservation.token);
|
||||
let cleanup_store = match cleanup_store {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(rollback_error) => return std::result::Result::Err(rollback_error),
|
||||
};
|
||||
std::mem::drop(store);
|
||||
if let std::option::Option::Some(value) = cleanup_store {
|
||||
let close = close_store_arc(value).await;
|
||||
runtime_state.complete_store_close(reservation.token);
|
||||
if let std::result::Result::Err(close_error) = close {
|
||||
return std::result::Result::Err(close_error);
|
||||
}
|
||||
}
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_ROUTE_RUNTIME_START_FAILED, "Cannot start Raw Transaction Ingest Desk Worker runtime")
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_ROUTE_RUNTIME_START_FAILED, "Cannot start Raw Transaction Ingest Desk route Worker runtime")
|
||||
.with_source(error),
|
||||
);
|
||||
},
|
||||
@@ -310,14 +539,23 @@ pub(crate) async fn start_route_runtime(
|
||||
std::result::Result::Err(error) => {
|
||||
let _stop_requested = handle.request_stop();
|
||||
let _terminal = handle.wait_terminal().await;
|
||||
let close = close_store_arc(store).await;
|
||||
runtime_state.rollback(reservation.token);
|
||||
if let std::result::Result::Err(close_error) = close {
|
||||
return std::result::Result::Err(close_error);
|
||||
let cleanup_store = runtime_state.rollback(reservation.token);
|
||||
let cleanup_store = match cleanup_store {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(rollback_error) => return std::result::Result::Err(rollback_error),
|
||||
};
|
||||
std::mem::drop(store);
|
||||
if let std::option::Option::Some(value) = cleanup_store {
|
||||
let close = close_store_arc(value).await;
|
||||
runtime_state.complete_store_close(reservation.token);
|
||||
if let std::result::Result::Err(close_error) = close {
|
||||
return std::result::Result::Err(close_error);
|
||||
}
|
||||
}
|
||||
return std::result::Result::Err(error);
|
||||
},
|
||||
};
|
||||
std::mem::drop(store);
|
||||
ksp_logging_lib::info!(
|
||||
target: crate::TRACING_TARGET,
|
||||
domain = crate::TRACING_DOMAIN_ROUTE_RUNTIME,
|
||||
@@ -325,9 +563,9 @@ pub(crate) async fn start_route_runtime(
|
||||
network = reservation.identity.network.as_str(),
|
||||
route_id = reservation.identity.route_id.as_str(),
|
||||
commitment = reservation.identity.commitment.as_str(),
|
||||
"Raw Transaction Ingest Desk mono-route Worker handle installed"
|
||||
"Raw Transaction Ingest Desk independent route Worker handle installed on shared Store"
|
||||
);
|
||||
return std::result::Result::Ok(crate::RouteRuntimeLaunch { acknowledgement, handle, runtime_state, store, token: reservation.token });
|
||||
return std::result::Result::Ok(crate::RouteRuntimeLaunch { acknowledgement, handle, runtime_state, token: reservation.token });
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -350,17 +588,32 @@ impl RouteRuntimeIdentity {
|
||||
state,
|
||||
};
|
||||
}
|
||||
|
||||
fn matches_dto(&self, value: &crate::RawIngestRouteRuntimeDto) -> bool {
|
||||
return self.profile_id == value.profile_id && self.route_id == value.route_id;
|
||||
}
|
||||
}
|
||||
|
||||
enum RouteStopTarget {
|
||||
Active {
|
||||
handle: ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestHandle,
|
||||
identity: RouteRuntimeIdentity,
|
||||
token: u64,
|
||||
},
|
||||
Terminal(crate::RawIngestRouteRuntimeDto),
|
||||
TerminalClosing,
|
||||
}
|
||||
|
||||
struct RouteRuntimeReservation {
|
||||
identity: RouteRuntimeIdentity,
|
||||
needs_store_open: bool,
|
||||
token: u64,
|
||||
worker_id: ksp_worker_api::WorkerId,
|
||||
}
|
||||
|
||||
enum RouteRuntimeSlot {
|
||||
Idle,
|
||||
Starting {
|
||||
identity: RouteRuntimeIdentity,
|
||||
token: u64,
|
||||
},
|
||||
Active {
|
||||
@@ -370,6 +623,28 @@ enum RouteRuntimeSlot {
|
||||
},
|
||||
}
|
||||
|
||||
impl RouteRuntimeSlot {
|
||||
fn matches_identity(&self, profile_id: &str, route_id: crate::RawIngestRouteId) -> bool {
|
||||
let identity = match self {
|
||||
Self::Starting { identity, .. } | Self::Active { identity, .. } => identity,
|
||||
};
|
||||
return identity.profile_id == profile_id && identity.route_id == route_id;
|
||||
}
|
||||
|
||||
fn starting_token(&self) -> std::option::Option<u64> {
|
||||
return match self {
|
||||
Self::Starting { token, .. } => std::option::Option::Some(*token),
|
||||
Self::Active { .. } => std::option::Option::None,
|
||||
};
|
||||
}
|
||||
|
||||
fn token(&self) -> u64 {
|
||||
return match self {
|
||||
Self::Starting { token, .. } | Self::Active { token, .. } => *token,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
async fn close_store_arc(store: std::sync::Arc<ksp_store_lib::Store>) -> ksp_core_lib::Result<()> {
|
||||
let started = std::time::Instant::now();
|
||||
let mut store = store;
|
||||
@@ -382,20 +657,18 @@ async fn close_store_arc(store: std::sync::Arc<ksp_store_lib::Store>) -> ksp_cor
|
||||
std::result::Result::Err(error) => std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_STORE_SHUTDOWN_FAILED,
|
||||
"Cannot close Raw Transaction Ingest Desk Store runtime",
|
||||
"Cannot close Raw Transaction Ingest Desk shared Store runtime",
|
||||
)
|
||||
.with_source(error),
|
||||
),
|
||||
};
|
||||
},
|
||||
std::result::Result::Err(shared) => {
|
||||
store = shared;
|
||||
},
|
||||
std::result::Result::Err(shared) => store = shared,
|
||||
}
|
||||
if started.elapsed() >= STORE_RECLAIM_TIMEOUT {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_ROUTE_RUNTIME_STORE_SHUTDOWN_FAILED,
|
||||
"Raw Transaction Ingest Desk Store remained shared after terminal Worker",
|
||||
"Raw Transaction Ingest Desk shared Store remained referenced after final Worker",
|
||||
));
|
||||
}
|
||||
tokio::time::sleep(STORE_RECLAIM_POLL_INTERVAL).await;
|
||||
@@ -414,7 +687,7 @@ fn project_worker_state(state: ksp_worker_api::WorkerState) -> crate::RawIngestR
|
||||
}
|
||||
|
||||
fn runtime_lock_error() -> ksp_core_lib::Error {
|
||||
return ksp_core_lib::Error::new(crate::ERROR_CODE_APP_STATE_LOCK_FAILED, "Raw Transaction Ingest Desk mono-route runtime state lock is poisoned");
|
||||
return ksp_core_lib::Error::new(crate::ERROR_CODE_APP_STATE_LOCK_FAILED, "Raw Transaction Ingest Desk multi-route runtime state lock is poisoned");
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/src/route_start.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
//! Start-time Config revalidation and exact Transport/Store resource reconstruction for route validation and runtime launch.
|
||||
|
||||
@@ -278,18 +278,22 @@ fn prepare_yellowstone(
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let filter_name = ksp_onchain_transport_lib::YellowstoneSubscribeFilterName::new("raw-ingest-all-transactions");
|
||||
let filter_name = ksp_onchain_transport_lib::YellowstoneSubscribeFilterName::new("raw-ingest-blocks");
|
||||
let filter_name = match filter_name {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut block_filter = ksp_onchain_transport_lib::YellowstoneSubscribeBlockFilter::new();
|
||||
block_filter.set_include_transactions(std::option::Option::Some(false));
|
||||
block_filter.set_include_accounts(std::option::Option::Some(false));
|
||||
block_filter.set_include_entries(std::option::Option::Some(false));
|
||||
let mut request = ksp_onchain_transport_lib::YellowstoneSubscribeRequest::new();
|
||||
let inserted = request.insert_transaction_filter(filter_name, ksp_onchain_transport_lib::YellowstoneSubscribeTransactionFilter::new());
|
||||
let inserted = request.insert_block_filter(filter_name, block_filter);
|
||||
if let std::result::Result::Err(error) = inserted {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
request.set_commitment(std::option::Option::Some(commitment));
|
||||
let role = single_http_role(transport.http_settings(), network, &["getTransaction"]);
|
||||
let role = single_http_role(transport.http_settings(), network, &["getBlock"]);
|
||||
let role = match role {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-raw-transaction-ingest-desk/src/tauri.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
//! Tauri runtime assembly for the KSP Raw Transaction Ingest desktop application.
|
||||
|
||||
@@ -164,8 +164,11 @@ async fn start_route(
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn stop_route(state: tauri::State<'_, crate::AppState>) -> std::result::Result<crate::RawIngestRouteRuntimeDto, crate::CommandErrorDto> {
|
||||
let result = state.stop_route().await;
|
||||
async fn stop_route(
|
||||
request: crate::RawIngestRouteStopRequestDto,
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<crate::RawIngestRouteRuntimeDto, crate::CommandErrorDto> {
|
||||
let result = state.stop_route(&request).await;
|
||||
return match result {
|
||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(project_command_error("stop_route", crate::TRACING_DOMAIN_ROUTE_RUNTIME, &error)),
|
||||
|
||||
Reference in New Issue
Block a user