research: rerun cancels orphan runs first so 409 doesn't block restart
ci / gates (push) Failing after 6s
ci / rust (push) Skipped
ci / frontend (push) Skipped
ci / e2e (push) Skipped
ci / publish (push) Skipped

The previous rerun guard (standby OR (processing AND in_flight==0))
still 409'd when a lingering queued/running row existed — typically
an orphan from a server restart mid-pipeline, before the reaper or
stale-checkpoint requeuer had marked it failed.

Now: allow rerun on any 'processing' topic; before enqueuing the
fresh run, batch-cancel every in-flight run for the topic so the
new run isn't racing them. Terminal states (reviewing / publishing
/ published) still 409 as before.
This commit is contained in:
Omar Sobh
2026-07-15 17:42:36 -07:00
parent 671d3ac4f0
commit 6af9d509b0
+17 -7
View File
@@ -679,16 +679,26 @@ pub async fn start_topic(
.await?
.ok_or(ApiError::NotFound)?;
// Allow the fresh-start path (standby) AND the rerun path (topic
// parked in `processing` after all runs failed / no runs remain in
// flight). Blocks accidental double-fires on a live pipeline
// (in_flight > 0) and terminal states (reviewing / publishing /
// published).
let in_flight =
cm_db::repo::topology_runs::active_runs_for_research_topic(&state.pool, id).await?;
let can_start = topic.status == "standby" || (topic.status == "processing" && in_flight == 0);
// parked in `processing`). For rerun, first cancel any lingering
// queued/running runs (typically orphans left over from a server
// restart mid-pipeline) so the fresh enqueue isn't racing them.
// Terminal states (reviewing / publishing / published) still 409.
let can_start = topic.status == "standby" || topic.status == "processing";
if !can_start {
return Err(ApiError::Conflict);
}
if topic.status == "processing" {
let orphan_ids =
cm_db::repo::topology_runs::active_run_ids_for_research_topic(&state.pool, id).await?;
for run_id in orphan_ids {
// Best-effort — cancel just marks the row; the actual worker
// (if still alive) stops at its next step boundary. If the
// worker is dead (server restart), the row transitions and
// stops being counted as in-flight immediately.
let _ =
cm_db::repo::topology_runs::cancel(&state.pool, run_id, user.workspace_id).await;
}
}
// D1 fold — refuse to double-fire when a scheduled research loop
// already owns this topic. Otherwise clicking the legacy "Start
// research" button while a loop iteration is in flight would spawn