research: auto-transition processing → reviewing on last run
Hooks the topology_worker's post-terminal path into a new notify_run_completed repo helper that atomically transitions the topic processing → reviewing when the completed run has research_topic_id set AND no siblings for that topic are still queued or running. Guarded on status='processing' so a retry, a re-fire, or a topic already past processing are all no-ops. Best-effort at the worker; DB hiccups are logged and never fail the run. The manual /submit-review endpoint stays as an escape hatch for topics that end up parked in processing with nothing to complete (updated the doc comment).
This commit is contained in:
@@ -187,6 +187,37 @@ pub async fn touch(pool: &PgPool, id: Uuid) -> Result<(), DbError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// If this run belongs to a research topic AND no siblings of that topic
|
||||
/// are still queued or running, transition the topic `processing → reviewing`.
|
||||
/// Guarded by `status = 'processing'` so a repeat call (e.g. a retry) is a
|
||||
/// no-op; a topic already reviewing/publishing/published stays put.
|
||||
/// Returns `true` when the topic was transitioned.
|
||||
pub async fn notify_run_completed(pool: &PgPool, id: Uuid) -> Result<bool, DbError> {
|
||||
// One statement: subquery locates the topic id, subquery counts siblings
|
||||
// still in flight (excluding *this* run — it's about to be flipped to
|
||||
// completed/failed by the caller, but ordering isn't guaranteed here).
|
||||
let row = sqlx::query!(
|
||||
"UPDATE research_topics t
|
||||
SET status = 'reviewing', updated_at = now()
|
||||
WHERE t.id = (
|
||||
SELECT research_topic_id FROM topology_runs
|
||||
WHERE id = $1 AND research_topic_id IS NOT NULL
|
||||
)
|
||||
AND t.status = 'processing'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM topology_runs
|
||||
WHERE research_topic_id = t.id
|
||||
AND id <> $1
|
||||
AND status IN ('queued', 'running')
|
||||
)
|
||||
RETURNING t.id",
|
||||
id,
|
||||
)
|
||||
.fetch_optional(pool)
|
||||
.await?;
|
||||
Ok(row.is_some())
|
||||
}
|
||||
|
||||
/// Mark a job completed and store its final result blob.
|
||||
pub async fn complete(pool: &PgPool, id: Uuid, result: &Value) -> Result<(), DbError> {
|
||||
sqlx::query!(
|
||||
|
||||
Reference in New Issue
Block a user