fix: decouple oneliner-heal from request handler tasks
heal_orphans_if_idle called regenerate_missing_oneliners_for_user inline on the request task, blocking the browser for up to N×60s when orphans existed (observed with 16 cases after manual cleanup). Now: CAS on new OnelinerHealBusy flag + tokio::spawn + BusyGuard reset-on-drop. Handler returns immediately; the existing OnelinerUpdated SSE event delivers results via the usual reload. Startup recovery shares the flag so a concurrent page-load during boot cannot fire a parallel regen loop against Ollama. Regression test uses wiremock + timeout(150ms) + .expect(5) to verify both non-blocking return and dedup of parallel invocations.
This commit is contained in:
@@ -38,6 +38,12 @@ pub type WorkerBusy = Arc<AtomicBool>;
|
||||
pub struct AnalyzeBusy(pub WorkerBusy);
|
||||
#[derive(Clone)]
|
||||
pub struct TranscribeBusy(pub WorkerBusy);
|
||||
/// Dedup flag for the detached oneliner-heal `tokio::spawn` fired from
|
||||
/// `heal_orphans_if_idle`. The handler CAS's this to `true`; the spawned
|
||||
/// task resets via [`BusyGuard`] on drop. Keeps concurrent page-loads
|
||||
/// from launching parallel Ollama-call loops against the same data.
|
||||
#[derive(Clone)]
|
||||
pub struct OnelinerHealBusy(pub WorkerBusy);
|
||||
|
||||
/// RAII guard that flips a [`WorkerBusy`] true on construction and false on
|
||||
/// drop — panic-safe. Shared across analyze + transcribe workers.
|
||||
@@ -68,6 +74,7 @@ pub struct PipelineState {
|
||||
pub analyze_tx: AnalyzeSender,
|
||||
pub transcribe_busy: TranscribeBusy,
|
||||
pub transcribe_tx: TranscribeSender,
|
||||
pub oneliner_heal_busy: OnelinerHealBusy,
|
||||
}
|
||||
|
||||
/// Shared application state. Clonable — all fields are cheap to clone
|
||||
@@ -81,6 +88,7 @@ pub struct AppState {
|
||||
pub magic_link_store: MagicLinkStore,
|
||||
pub analyze_busy: AnalyzeBusy,
|
||||
pub transcribe_busy: TranscribeBusy,
|
||||
pub oneliner_heal_busy: OnelinerHealBusy,
|
||||
pub events_tx: events::EventSender,
|
||||
pub http_client: reqwest::Client,
|
||||
pub vocab: Arc<Gazetteer>,
|
||||
@@ -128,6 +136,12 @@ impl FromRef<AppState> for TranscribeBusy {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRef<AppState> for OnelinerHealBusy {
|
||||
fn from_ref(state: &AppState) -> Self {
|
||||
state.oneliner_heal_busy.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRef<AppState> for PipelineState {
|
||||
fn from_ref(state: &AppState) -> Self {
|
||||
PipelineState {
|
||||
@@ -135,6 +149,7 @@ impl FromRef<AppState> for PipelineState {
|
||||
analyze_tx: state.analyze_tx.clone(),
|
||||
transcribe_busy: state.transcribe_busy.clone(),
|
||||
transcribe_tx: state.transcribe_tx.clone(),
|
||||
oneliner_heal_busy: state.oneliner_heal_busy.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -171,6 +186,7 @@ pub fn create_router(config: Arc<Config>) -> Router {
|
||||
magic_link_store: magic_link::new_store(),
|
||||
analyze_busy: AnalyzeBusy(Arc::new(AtomicBool::new(false))),
|
||||
transcribe_busy: TranscribeBusy(Arc::new(AtomicBool::new(false))),
|
||||
oneliner_heal_busy: OnelinerHealBusy(Arc::new(AtomicBool::new(false))),
|
||||
events_tx: events::channel(),
|
||||
http_client: reqwest::Client::new(),
|
||||
vocab: Arc::new(Gazetteer::empty()),
|
||||
|
||||
Reference in New Issue
Block a user