v0.3.7-pre.008
This commit is contained in:
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;
|
||||
Reference in New Issue
Block a user