v0.3.7-pre.008
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
// file: crates/ksp-app-backfill-desk/src/app_state.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
//! Shared backend state owned by the Backfill Desk Tauri application.
|
||||
|
||||
/// Shared Backfill Desk application state managed by Tauri.
|
||||
pub(crate) struct AppState {
|
||||
backfill_runs: crate::BackfillRunState,
|
||||
config_management: ksp_config_lib::ConfigManagement,
|
||||
logging_runtime: std::sync::Mutex<LoggingRuntimeState>,
|
||||
shutdown_started: std::sync::atomic::AtomicBool,
|
||||
@@ -76,6 +77,7 @@ impl crate::AppState {
|
||||
"resolved Backfill Desk splash timings"
|
||||
);
|
||||
return std::result::Result::Ok(Self {
|
||||
backfill_runs: crate::BackfillRunState::new(),
|
||||
config_management,
|
||||
logging_runtime: std::sync::Mutex::new(LoggingRuntimeState {
|
||||
guard: logging_startup.guard,
|
||||
@@ -121,7 +123,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.007-request-mapping".to_owned(),
|
||||
shell_phase: "pre.008-runtime-start".to_owned(),
|
||||
startup_diagnostic: runtime.startup_diagnostic.clone(),
|
||||
});
|
||||
}
|
||||
@@ -194,6 +196,103 @@ impl crate::AppState {
|
||||
return std::result::Result::Ok(preview);
|
||||
}
|
||||
|
||||
/// Prepares one concrete Backfill runtime, installs its control handle atomically and lends execution resources before spawn.
|
||||
pub(crate) fn prepare_backfill_start(&self, request: crate::BackfillStartRequestDto) -> ksp_core_lib::Result<crate::BackfillRunLaunch> {
|
||||
if self.shutdown_started.load(std::sync::atomic::Ordering::Acquire) {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_BACKFILL_COMPOSITION_NOT_READY,
|
||||
"Backfill Desk cannot start a Backfill run while application shutdown is in progress",
|
||||
));
|
||||
}
|
||||
let options = self.backfill_options();
|
||||
let options = match options {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let job_id = self.backfill_runs.next_job_id();
|
||||
let job_id = match job_id {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mapped = crate::map_backfill_request(request, &options, job_id.clone());
|
||||
let mapped = match mapped {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let runtime = ksp_job_backfill_lib::BackfillJobRuntime::new(mapped);
|
||||
let runtime = match runtime {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let handle = runtime.handle();
|
||||
let installed = self.backfill_runs.install(job_id.clone(), handle);
|
||||
if let std::result::Result::Err(error) = installed {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
let transport = self.transport_runtime.as_ref();
|
||||
let transport = match transport {
|
||||
std::option::Option::Some(value) => value.pool(),
|
||||
std::option::Option::None => {
|
||||
let rollback = self.backfill_runs.rollback(&job_id);
|
||||
if let std::result::Result::Err(error) = rollback {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_BACKFILL_COMPOSITION_NOT_READY,
|
||||
"Backfill Desk cannot start a Backfill run without a ready HTTP Transport pool",
|
||||
));
|
||||
},
|
||||
};
|
||||
let store = self.store_startup.take_for_run();
|
||||
let store = match store {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
let rollback = self.backfill_runs.rollback(&job_id);
|
||||
if let std::result::Result::Err(rollback_error) = rollback {
|
||||
return std::result::Result::Err(rollback_error);
|
||||
}
|
||||
return std::result::Result::Err(error);
|
||||
},
|
||||
};
|
||||
ksp_logging_lib::debug!(
|
||||
target: crate::TRACING_TARGET,
|
||||
domain = crate::TRACING_DOMAIN_RUN,
|
||||
job_id = job_id.as_str(),
|
||||
"admitted Backfill Desk run and installed control handle before async spawn"
|
||||
);
|
||||
return std::result::Result::Ok(crate::BackfillRunLaunch { job_id, runtime, store, transport });
|
||||
}
|
||||
|
||||
/// Executes one already-admitted Backfill launch and restores application resources after its terminal result.
|
||||
pub(crate) async fn execute_backfill_run(&self, launch: crate::BackfillRunLaunch) -> ksp_core_lib::Result<()> {
|
||||
let job_id = launch.job_id.clone();
|
||||
let result = launch.runtime.run(&launch.transport, &launch.store).await;
|
||||
let restore = self.store_startup.restore_after_run(launch.store);
|
||||
let finished = self.backfill_runs.finish(&job_id);
|
||||
let cancellation_requested = match finished {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
if let std::result::Result::Err(error) = restore {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
return match result {
|
||||
std::result::Result::Ok(snapshot) => {
|
||||
ksp_logging_lib::debug!(
|
||||
target: crate::TRACING_TARGET,
|
||||
domain = crate::TRACING_DOMAIN_RUN,
|
||||
cancellation_requested = cancellation_requested,
|
||||
contiguous_completed = snapshot.contiguous_completed(),
|
||||
job_id = job_id.as_str(),
|
||||
phase = snapshot.phase().code(),
|
||||
"Backfill Desk run reached a terminal snapshot and released the single-run slot"
|
||||
);
|
||||
std::result::Result::Ok(())
|
||||
},
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
}
|
||||
|
||||
/// Marks graceful application shutdown as started and reports whether this caller won the one-shot transition.
|
||||
pub(crate) fn begin_shutdown(&self) -> bool {
|
||||
return self.shutdown_started.compare_exchange(false, true, std::sync::atomic::Ordering::AcqRel, std::sync::atomic::Ordering::Acquire).is_ok();
|
||||
|
||||
131
crates/ksp-app-backfill-desk/src/backfill_run.rs
Normal file
131
crates/ksp-app-backfill-desk/src/backfill_run.rs
Normal file
@@ -0,0 +1,131 @@
|
||||
// file: crates/ksp-app-backfill-desk/src/backfill_run.rs
|
||||
// version: 1
|
||||
|
||||
//! Single-active-run admission and launch ownership for Backfill Desk.
|
||||
|
||||
/// Fully prepared Backfill execution moved into the Tauri async runtime after admission.
|
||||
pub(crate) struct BackfillRunLaunch {
|
||||
/// Backend-generated logical Job identity.
|
||||
pub(crate) job_id: ksp_job_api::JobId,
|
||||
/// Concrete Backfill runtime owning discovery, hydration, persistence and latest-value publication.
|
||||
pub(crate) runtime: ksp_job_backfill_lib::BackfillJobRuntime,
|
||||
/// Store facade temporarily borrowed from the application Store runtime for the duration of this run.
|
||||
pub(crate) store: ksp_store_lib::Store,
|
||||
/// Shareable HTTP Transport pool selected by the active composite profile.
|
||||
pub(crate) transport: ksp_onchain_transport_lib::HttpTransportPool,
|
||||
}
|
||||
|
||||
impl crate::BackfillRunLaunch {
|
||||
/// Builds the safe acknowledgement returned immediately after the non-blocking Start admission.
|
||||
#[must_use]
|
||||
pub(crate) fn response(&self) -> crate::BackfillStartResponseDto {
|
||||
return crate::BackfillStartResponseDto { job_id: self.job_id.as_str().to_owned(), state: ksp_job_api::JobState::Created.code().to_owned() };
|
||||
}
|
||||
}
|
||||
|
||||
/// Single-run control slot retained by the application while one Backfill runtime is active.
|
||||
pub(crate) struct BackfillRunState {
|
||||
active: std::sync::Mutex<std::option::Option<ActiveBackfillRun>>,
|
||||
next_sequence: std::sync::atomic::AtomicU64,
|
||||
}
|
||||
|
||||
impl crate::BackfillRunState {
|
||||
/// Creates an empty single-run state for one desktop application session.
|
||||
#[must_use]
|
||||
pub(crate) const fn new() -> Self {
|
||||
return Self {
|
||||
active: std::sync::Mutex::new(std::option::Option::None),
|
||||
next_sequence: std::sync::atomic::AtomicU64::new(1),
|
||||
};
|
||||
}
|
||||
|
||||
/// Allocates one bounded backend-owned Job identity without accepting caller-supplied identity material.
|
||||
pub(crate) fn next_job_id(&self) -> ksp_core_lib::Result<ksp_job_api::JobId> {
|
||||
let sequence = self
|
||||
.next_sequence
|
||||
.fetch_update(std::sync::atomic::Ordering::AcqRel, std::sync::atomic::Ordering::Acquire, |current| return current.checked_add(1));
|
||||
let sequence = match sequence {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_APP_STATE_INVALID,
|
||||
"Backfill Desk Job identity sequence is exhausted",
|
||||
));
|
||||
},
|
||||
};
|
||||
return ksp_job_api::JobId::new(format!("backfill-desk-{sequence}"));
|
||||
}
|
||||
|
||||
/// Installs one control handle atomically and rejects concurrent Starts while another run owns the slot.
|
||||
pub(crate) fn install(&self, job_id: ksp_job_api::JobId, handle: ksp_job_backfill_lib::BackfillJobHandle) -> ksp_core_lib::Result<()> {
|
||||
let active = self.active.lock();
|
||||
let mut active = match active {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_APP_STATE_LOCK_FAILED,
|
||||
"Backfill Desk active-run state lock is poisoned",
|
||||
));
|
||||
},
|
||||
};
|
||||
if active.is_some() {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_BACKFILL_RUN_ACTIVE,
|
||||
"Backfill Desk already has an active Backfill run",
|
||||
));
|
||||
}
|
||||
*active = std::option::Option::Some(ActiveBackfillRun { handle, job_id });
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
/// Clears the matching terminal run and reports whether cancellation had been requested on its retained handle.
|
||||
pub(crate) fn finish(&self, job_id: &ksp_job_api::JobId) -> ksp_core_lib::Result<bool> {
|
||||
let active = self.active.lock();
|
||||
let mut active = match active {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_APP_STATE_LOCK_FAILED,
|
||||
"Backfill Desk active-run state lock is poisoned",
|
||||
));
|
||||
},
|
||||
};
|
||||
let current = active.as_ref();
|
||||
let current = match current {
|
||||
std::option::Option::Some(value) if &value.job_id == job_id => value,
|
||||
std::option::Option::Some(_) => {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_APP_STATE_INVALID,
|
||||
"Backfill Desk terminal run does not match the active Job identity",
|
||||
));
|
||||
},
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_APP_STATE_INVALID,
|
||||
"Backfill Desk terminal run has no active admission slot",
|
||||
));
|
||||
},
|
||||
};
|
||||
let cancellation_requested = current.handle.is_cancellation_requested();
|
||||
*active = std::option::Option::None;
|
||||
return std::result::Result::Ok(cancellation_requested);
|
||||
}
|
||||
|
||||
/// Rolls back a just-installed admission when execution resources cannot be acquired before spawn.
|
||||
pub(crate) fn rollback(&self, job_id: &ksp_job_api::JobId) -> ksp_core_lib::Result<()> {
|
||||
let finished = self.finish(job_id);
|
||||
return match finished {
|
||||
std::result::Result::Ok(_) => std::result::Result::Ok(()),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
struct ActiveBackfillRun {
|
||||
handle: ksp_job_backfill_lib::BackfillJobHandle,
|
||||
job_id: ksp_job_api::JobId,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "../unit_tests/backfill_run.rs"]
|
||||
mod tests;
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/src/constants.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
//! Application-owned tracing targets and domains.
|
||||
|
||||
@@ -23,6 +23,8 @@ pub(crate) const TRACING_DOMAIN_BOOTSTRAP: &str = "backfill.bootstrap";
|
||||
pub(crate) const TRACING_DOMAIN_FRONTEND: &str = "frontend";
|
||||
/// Structured domain used by Backfill campaign admission and request mapping.
|
||||
pub(crate) const TRACING_DOMAIN_REQUEST: &str = "backfill.request";
|
||||
/// Structured domain used by concrete Backfill run admission and execution.
|
||||
pub(crate) const TRACING_DOMAIN_RUN: &str = "backfill.run";
|
||||
/// Structured domain used by the Backfill Desk shell.
|
||||
pub(crate) const TRACING_DOMAIN_SHELL: &str = "backfill.shell";
|
||||
/// Structured domain used by Store readiness and shutdown operations.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/src/dto_backfill.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
//! Application-owned Backfill campaign DTOs and backend-derived request limits.
|
||||
|
||||
@@ -65,6 +65,17 @@ pub(crate) struct BackfillStartRequestDto {
|
||||
pub(crate) scope_kind: String,
|
||||
}
|
||||
|
||||
/// Safe immediate acknowledgement returned after one Backfill run is admitted and spawned.
|
||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_backfill_desk/dto_backfill/BackfillStartResponseDto.ts")]
|
||||
pub(crate) struct BackfillStartResponseDto {
|
||||
/// Backend-generated bounded Job identifier for this in-session run.
|
||||
pub(crate) job_id: String,
|
||||
/// Initial lifecycle state at the time Start returns to the frontend.
|
||||
pub(crate) state: String,
|
||||
}
|
||||
|
||||
/// Safe projection proving that one app request mapped to the KSP Backfill contract.
|
||||
///
|
||||
/// Address and signature values are intentionally reduced to presence/count metadata. This DTO is
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/src/errors.rs
|
||||
// version: 6
|
||||
// version: 7
|
||||
|
||||
//! Application-local error codes for Backfill Desk composition and desktop runtime surfaces.
|
||||
|
||||
@@ -12,6 +12,8 @@ pub(crate) const ERROR_CODE_BACKFILL_COMPOSITION_NOT_READY: ksp_core_lib::ErrorC
|
||||
ksp_core_lib::ErrorCode::new("backfill_desk", "backfill_composition_not_ready");
|
||||
/// Backfill Desk received a campaign field or logical route that cannot map to the Backfill contract.
|
||||
pub(crate) const ERROR_CODE_BACKFILL_REQUEST_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("backfill_desk", "backfill_request_invalid");
|
||||
/// Backfill Desk refuses a concurrent Start while another Backfill run owns the single-run slot.
|
||||
pub(crate) const ERROR_CODE_BACKFILL_RUN_ACTIVE: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("backfill_desk", "backfill_run_active");
|
||||
/// Backfill Desk composite configuration is missing or references an unexpected document.
|
||||
pub(crate) const ERROR_CODE_CONFIG_COMPOSITE_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("backfill_desk", "config_composite_invalid");
|
||||
/// Frontend logging requested an unsupported level.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/src/lib.rs
|
||||
// version: 6
|
||||
// version: 7
|
||||
|
||||
//! Tauri desktop application scaffold for controlling and inspecting KSP RAW backfill jobs.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
mod app_state;
|
||||
mod backfill_request;
|
||||
mod backfill_run;
|
||||
mod bootstrap;
|
||||
mod constants;
|
||||
mod dto_backfill;
|
||||
@@ -32,6 +33,10 @@ pub(crate) use self::app_state::AppState;
|
||||
pub(crate) use self::backfill_request::map_backfill_request;
|
||||
/// Projects one validated Backfill request without returning address or signature values.
|
||||
pub(crate) use self::backfill_request::project_backfill_request;
|
||||
/// Fully prepared Backfill launch moved into the async runtime after single-run admission.
|
||||
pub(crate) use self::backfill_run::BackfillRunLaunch;
|
||||
/// Single-active-run admission state retained by the application.
|
||||
pub(crate) use self::backfill_run::BackfillRunState;
|
||||
/// Crate-internal Logging startup state shared by the application state.
|
||||
pub(crate) use self::bootstrap::LoggingStartup;
|
||||
/// Builds the Config management facade from the common KSP CLI bootstrap contract.
|
||||
@@ -62,6 +67,8 @@ pub(crate) use self::constants::TRACING_DOMAIN_BOOTSTRAP;
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_FRONTEND;
|
||||
/// Structured domain used by Backfill campaign admission and request mapping.
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_REQUEST;
|
||||
/// Structured domain used by concrete Backfill run admission and execution.
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_RUN;
|
||||
/// Structured domain used by the Backfill Desk shell.
|
||||
pub(crate) use self::constants::TRACING_DOMAIN_SHELL;
|
||||
/// Structured domain used by Store readiness and shutdown operations.
|
||||
@@ -84,6 +91,8 @@ pub(crate) use self::dto_backfill::BackfillRequestLimitsDto;
|
||||
pub(crate) use self::dto_backfill::BackfillRequestPreviewDto;
|
||||
/// App-owned campaign request received from the frontend.
|
||||
pub(crate) use self::dto_backfill::BackfillStartRequestDto;
|
||||
/// Safe immediate acknowledgement returned after one Backfill run is admitted and spawned.
|
||||
pub(crate) use self::dto_backfill::BackfillStartResponseDto;
|
||||
/// Returns commitment codes admitted by the current Backfill contract.
|
||||
pub(crate) use self::dto_backfill::backfill_commitment_codes;
|
||||
/// Builds frontend-safe campaign limits from Job-owned public constants.
|
||||
@@ -106,6 +115,8 @@ pub(crate) use self::errors::ERROR_CODE_APP_STATE_LOCK_FAILED;
|
||||
pub(crate) use self::errors::ERROR_CODE_BACKFILL_COMPOSITION_NOT_READY;
|
||||
/// Backfill Desk received a campaign field that cannot map to the Backfill contract.
|
||||
pub(crate) use self::errors::ERROR_CODE_BACKFILL_REQUEST_INVALID;
|
||||
/// Backfill Desk refuses a concurrent Start while another Backfill run owns the single-run slot.
|
||||
pub(crate) use self::errors::ERROR_CODE_BACKFILL_RUN_ACTIVE;
|
||||
/// Backfill Desk composite configuration is missing or references an unexpected document.
|
||||
pub(crate) use self::errors::ERROR_CODE_CONFIG_COMPOSITE_INVALID;
|
||||
/// Frontend logging requested an unsupported level.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/src/store_runtime.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
//! Composite-selected Store readiness and shutdown lifecycle owned by Backfill Desk.
|
||||
|
||||
@@ -21,6 +21,36 @@ impl StoreStartup {
|
||||
options.composition_ready = options.transport_ready && options.network_coherent && options.store_ready;
|
||||
}
|
||||
|
||||
/// Temporarily lends the ready Store facade to one admitted Backfill execution.
|
||||
pub(crate) fn take_for_run(&self) -> ksp_core_lib::Result<ksp_store_lib::Store> {
|
||||
let runtime = self.runtime.as_ref();
|
||||
let runtime = match runtime {
|
||||
std::option::Option::Some(value) if value.health_ready() => value,
|
||||
_ => {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_BACKFILL_COMPOSITION_NOT_READY,
|
||||
"Backfill Desk cannot start a Backfill run without a ready Store",
|
||||
));
|
||||
},
|
||||
};
|
||||
return runtime.take_for_run();
|
||||
}
|
||||
|
||||
/// Restores the Store facade after one Backfill execution reaches its terminal result.
|
||||
pub(crate) fn restore_after_run(&self, store: ksp_store_lib::Store) -> ksp_core_lib::Result<()> {
|
||||
let runtime = self.runtime.as_ref();
|
||||
let runtime = match runtime {
|
||||
std::option::Option::Some(value) => value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_APP_STATE_INVALID,
|
||||
"Backfill Desk cannot restore Store after runtime ownership disappeared",
|
||||
));
|
||||
},
|
||||
};
|
||||
return runtime.restore_after_run(store);
|
||||
}
|
||||
|
||||
/// Closes the retained Store runtime if startup reached the physical Store-open phase.
|
||||
pub(crate) async fn close(&self) -> ksp_core_lib::Result<()> {
|
||||
let runtime = self.runtime.as_ref();
|
||||
@@ -46,6 +76,44 @@ impl StoreRuntime {
|
||||
return self.health_ready;
|
||||
}
|
||||
|
||||
/// Temporarily removes the Store from the application slot for one single-active Backfill execution.
|
||||
pub(crate) fn take_for_run(&self) -> ksp_core_lib::Result<ksp_store_lib::Store> {
|
||||
let store = take_store(&self.store);
|
||||
let store = match store {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return match store {
|
||||
std::option::Option::Some(value) => std::result::Result::Ok(value),
|
||||
std::option::Option::None => std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_BACKFILL_RUN_ACTIVE,
|
||||
"Backfill Desk Store is already owned by an active Backfill run",
|
||||
)),
|
||||
};
|
||||
}
|
||||
|
||||
/// Restores the Store after one terminal Backfill execution so a later campaign can be admitted.
|
||||
pub(crate) fn restore_after_run(&self, store: ksp_store_lib::Store) -> ksp_core_lib::Result<()> {
|
||||
let locked = self.store.lock();
|
||||
let mut locked = match locked {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_APP_STATE_LOCK_FAILED,
|
||||
"Backfill Desk Store runtime state lock is poisoned",
|
||||
));
|
||||
},
|
||||
};
|
||||
if locked.is_some() {
|
||||
return std::result::Result::Err(ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_APP_STATE_INVALID,
|
||||
"Backfill Desk cannot restore Store into an occupied runtime slot",
|
||||
));
|
||||
}
|
||||
*locked = std::option::Option::Some(store);
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
/// Explicitly closes the Store exactly once through its backend-neutral facade.
|
||||
pub(crate) async fn close(&self) -> ksp_core_lib::Result<()> {
|
||||
let store = take_store(&self.store);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/src/tauri.rs
|
||||
// version: 5
|
||||
// version: 6
|
||||
|
||||
//! Tauri runtime assembly for the KSP Backfill desktop application.
|
||||
|
||||
@@ -77,6 +77,7 @@ fn configure_plugins(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<taur
|
||||
fn configure_commands(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tauri::Wry> {
|
||||
return builder.invoke_handler(tauri::generate_handler![
|
||||
backfill_options,
|
||||
backfill_start,
|
||||
backfill_validate_request,
|
||||
emit_frontend_log,
|
||||
get_runtime_status,
|
||||
@@ -164,6 +165,36 @@ fn backfill_options(state: tauri::State<'_, crate::AppState>) -> std::result::Re
|
||||
};
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn backfill_start(
|
||||
app: tauri::AppHandle,
|
||||
request: crate::BackfillStartRequestDto,
|
||||
state: tauri::State<'_, crate::AppState>,
|
||||
) -> std::result::Result<crate::BackfillStartResponseDto, crate::CommandErrorDto> {
|
||||
let launch = state.prepare_backfill_start(request);
|
||||
let launch = match launch {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
return std::result::Result::Err(project_command_error("backfill_start", crate::TRACING_DOMAIN_RUN, &error));
|
||||
},
|
||||
};
|
||||
let response = launch.response();
|
||||
let _run_task = tauri::async_runtime::spawn(async move {
|
||||
let state = app.state::<crate::AppState>();
|
||||
let executed = state.execute_backfill_run(launch).await;
|
||||
if let std::result::Result::Err(error) = executed {
|
||||
ksp_logging_lib::warn!(
|
||||
target: crate::TRACING_TARGET,
|
||||
domain = crate::TRACING_DOMAIN_RUN,
|
||||
error_domain = error.code().domain(),
|
||||
error_code = error.code().code(),
|
||||
"Backfill Desk async run terminated with an error"
|
||||
);
|
||||
}
|
||||
});
|
||||
return std::result::Result::Ok(response);
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
fn backfill_validate_request(
|
||||
request: crate::BackfillStartRequestDto,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-app-backfill-desk/src/transport_runtime.rs
|
||||
// version: 4
|
||||
// version: 5
|
||||
|
||||
//! Composite-selected HTTP Transport readiness and route inventory owned by Backfill Desk.
|
||||
|
||||
@@ -10,6 +10,12 @@ pub(crate) struct TransportRuntime {
|
||||
}
|
||||
|
||||
impl TransportRuntime {
|
||||
/// Returns a shareable clone of the Transport-owned HTTP pool for one admitted Backfill execution.
|
||||
#[must_use]
|
||||
pub(crate) fn pool(&self) -> ksp_onchain_transport_lib::HttpTransportPool {
|
||||
return self.pool.clone();
|
||||
}
|
||||
|
||||
/// Returns the composite-selected Transport profile identifier.
|
||||
#[must_use]
|
||||
pub(crate) fn profile_id(&self) -> &str {
|
||||
|
||||
Reference in New Issue
Block a user