v0.3.7-pre.010

This commit is contained in:
2026-09-02 18:56:09 +02:00
parent 97f84e5c58
commit 313d2a1ea6
20 changed files with 533 additions and 40 deletions

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-backfill-desk/src/app_state.rs
// version: 7
// version: 8
//! Shared backend state owned by the Backfill Desk Tauri application.
@@ -123,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.008-runtime-start".to_owned(),
shell_phase: "pre.010-cancel-races".to_owned(),
startup_diagnostic: runtime.startup_diagnostic.clone(),
});
}
@@ -277,6 +277,29 @@ impl crate::AppState {
};
}
/// Requests cooperative cancellation of one exact backend-issued Backfill Job identity.
pub(crate) fn cancel_backfill(&self, job_id: &str) -> ksp_core_lib::Result<crate::BackfillCancelResponseDto> {
let cancelled = self.backfill_runs.cancel(job_id);
let cancelled = match cancelled {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
ksp_logging_lib::debug!(
target: crate::TRACING_TARGET,
domain = crate::TRACING_DOMAIN_RUN,
accepted = cancelled.accepted,
job_id = cancelled.job_id.as_str(),
state = cancelled.state.as_str(),
"processed Backfill Desk cooperative cancellation request"
);
return std::result::Result::Ok(cancelled);
}
/// Requests best-effort cooperative cancellation of the active run during application shutdown.
pub(crate) fn cancel_active_backfill_for_shutdown(&self) -> ksp_core_lib::Result<bool> {
return self.backfill_runs.cancel_active_for_shutdown();
}
/// 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();

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-backfill-desk/src/backfill_run.rs
// version: 2
// version: 3
//! Single-active-run admission and launch ownership for Backfill Desk.
@@ -130,6 +130,91 @@ impl crate::BackfillRunState {
return std::result::Result::Ok(cancellation_requested);
}
/// Requests cooperative cancellation for the exact backend-issued active Job identity.
pub(crate) fn cancel(&self, job_id: &str) -> ksp_core_lib::Result<crate::BackfillCancelResponseDto> {
let active = self.active.lock();
let 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 cancellation state lock is poisoned",
));
},
};
if let std::option::Option::Some(current) = active.as_ref() {
if current.job_id.as_str() != job_id {
return std::result::Result::Err(ksp_core_lib::Error::new(
crate::ERROR_CODE_BACKFILL_RUN_MISMATCH,
"Backfill Desk cancellation target does not match the active Job",
));
}
let source = current.handle.snapshots();
let notification = <ksp_job_backfill_lib::BackfillSnapshotSource as ksp_job_api::JobSnapshotSource>::current(&source);
let state = notification.state();
if state.is_terminal() {
return std::result::Result::Ok(crate::BackfillCancelResponseDto {
accepted: false,
job_id: current.job_id.as_str().to_owned(),
state: state.code().to_owned(),
});
}
let accepted = current.handle.cancel();
let state_code = if current.handle.is_cancellation_requested() { "cancelling" } else { state.code() };
return std::result::Result::Ok(crate::BackfillCancelResponseDto {
accepted,
job_id: current.job_id.as_str().to_owned(),
state: state_code.to_owned(),
});
}
drop(active);
let terminal = self.last_terminal.lock();
let terminal = match terminal {
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 terminal cancellation state lock is poisoned",
));
},
};
if let std::option::Option::Some(notification) = terminal.as_ref() {
if notification.id().as_str() != job_id {
return std::result::Result::Err(ksp_core_lib::Error::new(
crate::ERROR_CODE_BACKFILL_RUN_MISMATCH,
"Backfill Desk cancellation target does not match the retained terminal Job",
));
}
return std::result::Result::Ok(crate::BackfillCancelResponseDto {
accepted: false,
job_id: notification.id().as_str().to_owned(),
state: notification.state().code().to_owned(),
});
}
return std::result::Result::Err(ksp_core_lib::Error::new(
crate::ERROR_CODE_BACKFILL_RUN_NOT_ACTIVE,
"Backfill Desk has no active Backfill run to cancel",
));
}
/// Requests best-effort cooperative cancellation of whichever run is active after shutdown wins admission.
pub(crate) fn cancel_active_for_shutdown(&self) -> ksp_core_lib::Result<bool> {
let active = self.active.lock();
let 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 shutdown cancellation state lock is poisoned",
));
},
};
return match active.as_ref() {
std::option::Option::Some(current) => std::result::Result::Ok(current.handle.cancel()),
std::option::Option::None => std::result::Result::Ok(false),
};
}
/// Returns the complete current active snapshot or the retained latest terminal snapshot for resynchronization.
pub(crate) fn current_notification(
&self,

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-backfill-desk/src/dto_backfill.rs
// version: 2
// version: 3
//! Application-owned Backfill campaign DTOs and backend-derived request limits.
@@ -76,6 +76,19 @@ pub(crate) struct BackfillStartResponseDto {
pub(crate) state: String,
}
/// Safe acknowledgement of one targeted cooperative cancellation request.
#[derive(Clone, Debug, serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_backfill_desk/dto_backfill/BackfillCancelResponseDto.ts")]
pub(crate) struct BackfillCancelResponseDto {
/// Whether this call won the first pre-terminal cancellation request for the targeted run.
pub(crate) accepted: bool,
/// Backend-generated Job identifier that the cancellation request targeted.
pub(crate) job_id: String,
/// Safe current or cancellation-intent lifecycle code at command completion.
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

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-backfill-desk/src/errors.rs
// version: 7
// version: 8
//! Application-local error codes for Backfill Desk composition and desktop runtime surfaces.
@@ -14,6 +14,10 @@ pub(crate) const ERROR_CODE_BACKFILL_COMPOSITION_NOT_READY: ksp_core_lib::ErrorC
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 received a stale or mismatched Job identity for an active-run control operation.
pub(crate) const ERROR_CODE_BACKFILL_RUN_MISMATCH: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("backfill_desk", "backfill_run_mismatch");
/// Backfill Desk has no active or retained matching run for the requested control operation.
pub(crate) const ERROR_CODE_BACKFILL_RUN_NOT_ACTIVE: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("backfill_desk", "backfill_run_not_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.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-backfill-desk/src/lib.rs
// version: 8
// version: 9
//! Tauri desktop application scaffold for controlling and inspecting KSP RAW backfill jobs.
@@ -92,6 +92,8 @@ pub(crate) use self::constants::TRACING_TARGET_FRONTEND;
pub(crate) use self::constants::TRACING_TARGET_FRONTEND_MAIN;
/// Owning target for splash-window frontend events.
pub(crate) use self::constants::TRACING_TARGET_FRONTEND_SPLASH;
/// Safe acknowledgement returned by targeted cooperative cancellation.
pub(crate) use self::dto_backfill::BackfillCancelResponseDto;
/// Backend-derived request bounds and Desk defaults for the campaign form.
pub(crate) use self::dto_backfill::BackfillRequestLimitsDto;
/// Safe request preview produced after strict backend mapping.
@@ -124,6 +126,10 @@ pub(crate) use self::errors::ERROR_CODE_BACKFILL_COMPOSITION_NOT_READY;
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 rejects stale or mismatched Job identity control requests.
pub(crate) use self::errors::ERROR_CODE_BACKFILL_RUN_MISMATCH;
/// Backfill Desk has no active or retained matching run for the requested control operation.
pub(crate) use self::errors::ERROR_CODE_BACKFILL_RUN_NOT_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.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-backfill-desk/src/tauri.rs
// version: 7
// version: 8
//! Tauri runtime assembly for the KSP Backfill desktop application.
@@ -77,6 +77,7 @@ fn configure_plugins(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<taur
#[allow(clippy::question_mark_used)] // Tauri generates the question-mark operator internally for async command dispatch.
fn configure_commands(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tauri::Wry> {
return builder.invoke_handler(tauri::generate_handler![
backfill_cancel,
backfill_options,
backfill_start,
backfill_status,
@@ -114,11 +115,26 @@ fn configure_window_events(builder: tauri::Builder<tauri::Wry>) -> tauri::Builde
if !state.begin_shutdown() {
return;
}
ksp_logging_lib::debug!(
target: crate::TRACING_TARGET,
domain = crate::TRACING_DOMAIN_WINDOWS,
"Backfill Desk main-window close requested; starting bounded Store shutdown"
);
let cancellation = state.cancel_active_backfill_for_shutdown();
match cancellation {
std::result::Result::Ok(accepted) => {
ksp_logging_lib::debug!(
target: crate::TRACING_TARGET,
domain = crate::TRACING_DOMAIN_WINDOWS,
cancellation_accepted = accepted,
"Backfill Desk main-window close requested; cooperative Backfill cancellation attempted before bounded Store shutdown"
);
},
std::result::Result::Err(error) => {
ksp_logging_lib::warn!(
target: crate::TRACING_TARGET,
domain = crate::TRACING_DOMAIN_WINDOWS,
error_domain = error.code().domain(),
error_code = error.code().code(),
"Backfill Desk could not request cooperative cancellation during shutdown; continuing bounded shutdown"
);
},
}
tauri::async_runtime::spawn(async move {
let state = app_handle.state::<crate::AppState>();
let closed = state.close_store().await;
@@ -158,6 +174,15 @@ fn project_command_error(command: &'static str, domain: &'static str, error: &ks
return crate::CommandErrorDto::from_error(error);
}
#[tauri::command]
fn backfill_cancel(job_id: String, state: tauri::State<'_, crate::AppState>) -> std::result::Result<crate::BackfillCancelResponseDto, crate::CommandErrorDto> {
let result = state.cancel_backfill(job_id.as_str());
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("backfill_cancel", crate::TRACING_DOMAIN_RUN, &error)),
};
}
#[tauri::command]
fn backfill_options(state: tauri::State<'_, crate::AppState>) -> std::result::Result<crate::BackfillDeskOptionsDto, crate::CommandErrorDto> {
let result = state.backfill_options();