feat(jobs/runners): job_reaper (lease timeout) + job_purge (retention)

This commit is contained in:
2026-05-06 08:20:29 +08:00
parent f6add1598e
commit 2d0c45a52b
3 changed files with 204 additions and 0 deletions
+43
View File
@@ -0,0 +1,43 @@
package runners
import (
"context"
"log/slog"
"time"
"github.com/google/uuid"
)
// JobsReapRepo is the minimal jobs repository surface for the reaper.
type JobsReapRepo interface {
ReapStuckRunningJobs(ctx context.Context, leasedBefore time.Time) ([]uuid.UUID, error)
}
// JobReaper resets jobs.running rows whose lease has exceeded the timeout
// (worker probably crashed). Periodic; idempotent; no retry needed.
type JobReaper struct {
repo JobsReapRepo
leaseTimeout time.Duration
log *slog.Logger
}
// NewJobReaper builds the reaper.
func NewJobReaper(repo JobsReapRepo, leaseTimeout time.Duration, log *slog.Logger) *JobReaper {
if log == nil {
log = slog.Default()
}
return &JobReaper{repo: repo, leaseTimeout: leaseTimeout, log: log}
}
// Run reaps stuck rows once.
func (r *JobReaper) Run(ctx context.Context) {
cutoff := time.Now().Add(-r.leaseTimeout)
ids, err := r.repo.ReapStuckRunningJobs(ctx, cutoff)
if err != nil {
r.log.Error("job_reaper.reap", "err", err.Error())
return
}
if len(ids) > 0 {
r.log.Warn("job_reaper.reaped", "count", len(ids), "ids", ids)
}
}