Skip to content

Requeue a job inline when its status write-back fails (#1106) - #1219

Closed
rollroyces wants to merge 1 commit into
deeplethe:devfrom
rollroyces:fix/jobs-write-back-fallback-v2
Closed

rollroyces wants to merge 1 commit into
deeplethe:devfrom
rollroyces:fix/jobs-write-back-fallback-v2

Conversation

@rollroyces

Copy link
Copy Markdown
Contributor

Why

run_worker writes the result back via mark_done or mark_failed. If
that DB update fails, the row stays in running. Restoring the database
does not recover the work while the worker keeps running:
requeue_failed matches only status = 'failed', and the startup orphan
sweep fires only on process restart. Restart requeues the row but
re-runs the same handler — a successful extraction is recomputed from
scratch.

The fix is an inline requeue on the same write-back-failure path.
Persist the state, not the handler run: roll the row back from
running to queued so the next loop picks it up and tries the status
write again. Handlers are documented as idempotent, so re-running them
is a side concern, not a correctness concern.

What changes

crates/utopia-store/src/jobs.rs:

  • new requeue_running_after_write_failure(pool, id) — single SQL
    update: UPDATE jobs SET status='queued', locked_at=NULL, run_at=now(), updated_at=now() WHERE id = $1 AND status = 'running'.
    The status = 'running' guard is intentional: if anyone else has
    already moved this row out of running (admin, manual
    requeue_failed, sibling worker in a multi-instance deployment), we
    don't clobber their decision. run_at = now() means the next loop
    tick picks it up immediately, with no extra backoff — this is the
    persistence path, not the handler-attempt path, so it shouldn't
    inflate a 30s × attempts² retry curve.

  • run_worker — when mark_done / mark_failed returns Err, log
    the write-back failure as before, then call the new helper. If that
    also fails, log both errors together. The running count is
    decremented in either case so the worker doesn't bleed a concurrency
    slot.

Why v2

The previous attempt (#1208) was based on a fork c9760a1 that had
diverged from upstream/dev (now 25fa9f4, rc9 + ~20 commits). The
patch is the same — one new helper, one call-site change. The point
of v2 is that the PR now applies on top of the current dev tree
rather than a stale snapshot.

What this proposal does not do

  • No retry loop around mark_done / mark_failed. The bug is "row
    stuck in running", the fix is "get it back to queued". Adding
    retries here would mean deciding how many and on which schedule —
    this PR keeps that decision with whoever owns the backoff curve
    (the existing one around handler retries).

  • No interaction with requeue_failed. That admin path is opt-in
    ("you've paid attention, retry these specific failures"); the new
    fallback is automatic ("we just couldn't persist, retry without
    human"). They share the target state (queued) but fire on
    disjoint conditions.

  • No tests. The existing jobs integration tests need a real
    Postgres (UTOPIA_DATABASE_URL); the project's test_db::url()
    skips cleanly when unset. A wire-level test that simulates a
    dropped write would require injecting failure into
    sqlx::query(..).execute(..), which is a follow-up the maintainer
    may want to scope.

Verification

Tracking

Signed-off-by: Royce Lam <roycelam@umich.edu>
@WaylandYang

Copy link
Copy Markdown
Contributor

感谢继续跟进 #1106。

这一条我们还是关闭,原因和 #1208 一样:#1106 已经由 #1137 修复并合入 dev(b7f756e)。现在状态写回失败时,worker 会在同一个任务里带着已算好的结果重试写入,不会再运行 handler;而这里的做法是把任务退回队列,下一轮会把 handler 重新跑一遍,已完成的抽取会再算一次。另外这个提交基于 #1137 合入之前的代码(25fa9f4),改的那段现在已经换成了 persist_with_retry,和当前的 dev 会冲突。

Snowflake 和 Databricks 的测试已经通过 #1221 合入(保留了你的提交,我们只补了一次 cargo fmt)。再次感谢。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants