HH-574: enforce stale job claim limit (#168)

* fix(HH-574): enforce stale job claim limit

* fix(HH-574): guard exhausted retry claims

---------

Co-authored-by: Rogee <rogee@ipao.vip>
This commit is contained in:
Rogee
2026-08-24 16:46:35 +08:00
committed by GitHub
co-authored by rogee
parent 84d701dce5
commit 4455f8938d
4 changed files with 95 additions and 10 deletions
@@ -159,9 +159,10 @@ SSE 不保存离线或慢消费者的确认状态,所以该契约保证发布
该保证从 job 成功写入 `background_jobs` 开始。事务内入队失败会使业务事务回滚;
非事务入队按 account、pubsub token 顺序逐个执行,失败 target 没有 durable job,
同一事件的其他 target 可能已成功入队。每个 target job 的 `MaxAttempts=3`;达到上限
(或遇到 permanent error)进入 `dead` 后不会再被自动
重试,也不再保证发布,消费者仍需通过 REST reconciliation 恢复持久化状态。
同一事件的其他 target 可能已成功入队。每个 target job 的 `MaxAttempts=3` 是绝对
claim 次数上限(进程在 claim 后崩溃也消耗一次);达到上限(或遇到
permanent error)进入 `dead` 后不会再被自动重试,也不再保证发布,消费者仍需
通过 REST reconciliation 恢复持久化状态。
消费者必须把 realtime 事件作为可重放通知处理:
@@ -429,7 +429,7 @@ func (wp *WorkerPool) sweepDueJobs(ctx context.Context) error {
**当前**:扫描 `status=running AND locked_at < cutoff`,重置为 `retrying`。
**迁移后**:逻辑不变,仍由 `Start()` 在启动时调用一次(回收上次崩溃时正在执行的 job)。但新增:在 sweep 循环中也定期调用,回收当前实例运行期间卡住的 job。
**迁移后**:仍由 `Start()` 在启动时调用一次(回收上次崩溃时正在执行的 job),并在 sweep 循环中定期调用。stale job 未耗尽 `max_attempts` 时转为 `retrying`;已耗尽时原子转为 `dead` 并写入 `failed_at`,不再发生下一次 claim。
```go
// Start() 中调用(不变)
@@ -447,7 +447,7 @@ func (wp *WorkerPool) sweepLoop(ctx context.Context) {
}
```
**影响分析**:`RequeueStaleJobs` 方法签名和 DB 操作不变。stale lock 回收后 job 状态变为 `retrying`,会被下一轮 sweep 扫描到并重新 XADD 到 Redis。
**影响分析**:`RequeueStaleJobs` 方法签名不变。只有未耗尽的 stale job 会被下一轮 sweep 重新 XADD 到 Redis;耗尽的 job 保持 `dead` 终态。
### 3.6 Start/Stop 变更