Refactor busy flags and worker guards
The `WorkerBusy` type was previously a single `Arc<AtomicBool>` shared between the analyze and transcribe workers. This commit refactors this to: - Introduce `AnalyzeBusy` and `TranscribeBusy` newtype wrappers around `WorkerBusy` to distinguish between the two flags. This allows `axum::extract::State` to target them individually. - Move the `BusyGuard` RAII guard into `src/lib.rs` and make it generic to work with any `WorkerBusy` instance. - Update the `main.rs`, `worker.rs`, and `routes/user_web.rs` files to use the new types and guards, ensuring correct state management for both pipelines. - Enhance the transcription recovery scan (`transcribe::recovery::scan_and_enqueue`) to iterate over user directories and call `enqueue_pending_for_user` for each, making it more robust and aligned with the per-user self-healing mechanism. - Add a check for `transcribe_busy` in the `case_detail.html` template to correctly display "Transkription läuft…" only when the transcribe worker is actually active.
This commit is contained in:
@@ -12,7 +12,8 @@ use crate::auth::AuthenticatedWebUser;
|
||||
use crate::config::Config;
|
||||
use crate::error::AppError;
|
||||
use crate::routes::web::{scan_recordings, RecordingView};
|
||||
use crate::WorkerBusy;
|
||||
use crate::transcribe::{recovery as transcribe_recovery, TranscribeSender};
|
||||
use crate::{AnalyzeBusy, TranscribeBusy};
|
||||
|
||||
struct UserCaseView {
|
||||
case_id: String,
|
||||
@@ -67,6 +68,10 @@ struct CaseDetailTemplate {
|
||||
llm_missing: bool,
|
||||
analyzing: bool,
|
||||
has_document: bool,
|
||||
/// True iff the transcribe worker is currently running. Gate for the
|
||||
/// per-recording "Transkription läuft…" label — suppresses the lie when
|
||||
/// a transcript is missing but no worker is active.
|
||||
transcribe_busy: bool,
|
||||
}
|
||||
|
||||
/// Flags derived from filesystem state + config.
|
||||
@@ -142,30 +147,45 @@ pub(crate) async fn any_document_exists(case_dir: &Path) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Self-heal: if the analyze worker is idle but orphan inputs (an input file
|
||||
/// without its document) exist for this user, re-enqueue them. Page-load is
|
||||
/// the trigger; no cron, no periodic task.
|
||||
/// Self-heal: if a worker is idle but orphans exist on disk for this user,
|
||||
/// re-enqueue them. Page-load is the trigger; no cron, no periodic task.
|
||||
/// Runs for both pipelines so a page-load on either view cleans both.
|
||||
async fn heal_orphans_if_idle(
|
||||
user_root: &Path,
|
||||
worker_busy: &WorkerBusy,
|
||||
slug: &str,
|
||||
analyze_busy: &AnalyzeBusy,
|
||||
analyze_tx: &AnalyzeSender,
|
||||
transcribe_busy: &TranscribeBusy,
|
||||
transcribe_tx: &TranscribeSender,
|
||||
) {
|
||||
if worker_busy.load(Ordering::Acquire) {
|
||||
return;
|
||||
if !analyze_busy.0.load(Ordering::Acquire) {
|
||||
analyze_recovery::enqueue_pending_for_user(user_root, analyze_tx).await;
|
||||
}
|
||||
if !transcribe_busy.0.load(Ordering::Acquire) {
|
||||
transcribe_recovery::enqueue_pending_for_user(user_root, slug, transcribe_tx).await;
|
||||
}
|
||||
analyze_recovery::enqueue_pending_for_user(user_root, analyze_tx).await;
|
||||
}
|
||||
|
||||
pub async fn handle_my_cases(
|
||||
user: AuthenticatedWebUser,
|
||||
State(config): State<Arc<Config>>,
|
||||
State(worker_busy): State<WorkerBusy>,
|
||||
State(analyze_busy): State<AnalyzeBusy>,
|
||||
State(analyze_tx): State<AnalyzeSender>,
|
||||
State(transcribe_busy): State<TranscribeBusy>,
|
||||
State(transcribe_tx): State<TranscribeSender>,
|
||||
) -> Result<Html<String>, AppError> {
|
||||
let user_root = config.data_path.join(&user.slug);
|
||||
heal_orphans_if_idle(&user_root, &worker_busy, &analyze_tx).await;
|
||||
heal_orphans_if_idle(
|
||||
&user_root,
|
||||
&user.slug,
|
||||
&analyze_busy,
|
||||
&analyze_tx,
|
||||
&transcribe_busy,
|
||||
&transcribe_tx,
|
||||
)
|
||||
.await;
|
||||
|
||||
let busy = worker_busy.load(Ordering::Acquire);
|
||||
let busy = analyze_busy.0.load(Ordering::Acquire);
|
||||
let cases = scan_user_cases(&config, &user.slug, busy).await;
|
||||
let undo_count = crate::routes::case_actions::summarize_latest_batch(&user_root)
|
||||
.await
|
||||
@@ -186,8 +206,10 @@ pub async fn handle_my_cases(
|
||||
pub async fn handle_case_detail(
|
||||
user: AuthenticatedWebUser,
|
||||
State(config): State<Arc<Config>>,
|
||||
State(worker_busy): State<WorkerBusy>,
|
||||
State(analyze_busy): State<AnalyzeBusy>,
|
||||
State(analyze_tx): State<AnalyzeSender>,
|
||||
State(transcribe_busy): State<TranscribeBusy>,
|
||||
State(transcribe_tx): State<TranscribeSender>,
|
||||
AxumPath(case_id): AxumPath<String>,
|
||||
) -> Result<Html<String>, AppError> {
|
||||
uuid::Uuid::parse_str(&case_id)
|
||||
@@ -195,7 +217,15 @@ pub async fn handle_case_detail(
|
||||
|
||||
// IDOR guard: case_dir must live under the session's user_slug.
|
||||
let user_root = config.data_path.join(&user.slug);
|
||||
heal_orphans_if_idle(&user_root, &worker_busy, &analyze_tx).await;
|
||||
heal_orphans_if_idle(
|
||||
&user_root,
|
||||
&user.slug,
|
||||
&analyze_busy,
|
||||
&analyze_tx,
|
||||
&transcribe_busy,
|
||||
&transcribe_tx,
|
||||
)
|
||||
.await;
|
||||
|
||||
let case_dir = match locate_case(&user_root, &case_id).await {
|
||||
Some(p) => p,
|
||||
@@ -213,8 +243,9 @@ pub async fn handle_case_detail(
|
||||
.map(|s| s.trim().to_string())
|
||||
.filter(|s| !s.is_empty());
|
||||
|
||||
let busy = worker_busy.load(Ordering::Acquire);
|
||||
let flags = compute_flags(&case_dir, &recordings, config.llm_configured(), busy).await;
|
||||
let a_busy = analyze_busy.0.load(Ordering::Acquire);
|
||||
let t_busy = transcribe_busy.0.load(Ordering::Acquire);
|
||||
let flags = compute_flags(&case_dir, &recordings, config.llm_configured(), a_busy).await;
|
||||
let case_id_short = case_id.chars().take(8).collect();
|
||||
|
||||
CaseDetailTemplate {
|
||||
@@ -227,6 +258,7 @@ pub async fn handle_case_detail(
|
||||
llm_missing: flags.llm_missing,
|
||||
analyzing: flags.analyzing,
|
||||
has_document: flags.has_document,
|
||||
transcribe_busy: t_busy,
|
||||
}
|
||||
.render()
|
||||
.map(Html)
|
||||
|
||||
Reference in New Issue
Block a user