fix(queue): 回收失效任务并阻止不确定提交重放
Worker 进程心跳不能证明单个 River job goroutine 仍存活,改以任务执行租约作为回收所有权栅栏。\n\n排队任务若上一次 attempt 在租约中断时处于 submitting 或 response_received,则转入人工复核并生成 release 记录,不再重新提交上游;明确被上游拒绝的响应仍允许重试。\n\n验证:go vet ./...;env -u AI_GATEWAY_TEST_DATABASE_URL go test ./... -count=1;真实 PostgreSQL 下相关 store 与 River 集成测试通过。
This commit is contained in:
@@ -297,10 +297,10 @@ FROM deleted`, staleAfter.String(), limit).Scan(&yielded); err != nil {
|
||||
return yielded, nil
|
||||
}
|
||||
|
||||
// RecoverOrphanedAsyncRiverJobs retries River jobs whose owning worker is no
|
||||
// longer alive after the gateway execution lease has already returned the task
|
||||
// to queued. Normal long-running jobs remain protected by their running task
|
||||
// state and the active worker heartbeat.
|
||||
// RecoverOrphanedAsyncRiverJobs retries stale River jobs after the gateway
|
||||
// execution lease has already returned the task to queued. A process heartbeat
|
||||
// does not prove that an individual River job goroutine is still alive; the
|
||||
// task execution lease is the ownership fence.
|
||||
func (s *Store) RecoverOrphanedAsyncRiverJobs(
|
||||
ctx context.Context,
|
||||
workerStaleAfter time.Duration,
|
||||
@@ -335,14 +335,6 @@ WITH orphaned AS MATERIALIZED (
|
||||
AND task.async_mode = true
|
||||
AND task.status = 'queued'
|
||||
AND (task.execution_lease_expires_at IS NULL OR task.execution_lease_expires_at <= now())
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM gateway_worker_instances worker
|
||||
WHERE worker.status = 'active'
|
||||
AND worker.heartbeat_at > now() - $1::interval
|
||||
AND COALESCE(job.attempted_by[array_length(job.attempted_by, 1)], '')
|
||||
LIKE worker.instance_id || '-exec-%'
|
||||
)
|
||||
ORDER BY job.attempted_at ASC, job.id ASC
|
||||
LIMIT $2
|
||||
FOR UPDATE OF job SKIP LOCKED
|
||||
|
||||
Reference in New Issue
Block a user