This commit is contained in:
@@ -206,6 +206,27 @@ func TestClaimNextReturnsNilOnEmptyQueue(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCancelJobCancelsEveryUnfinishedTask(t *testing.T) {
|
||||
pool := testPool(t)
|
||||
ctx := context.Background()
|
||||
job, _ := seedJob(t, pool, 3)
|
||||
clk := fixedClock{now: time.Now().UTC()}
|
||||
uc := usecase.NewCancelJob(NewJobRepo(pool), NewTaskRepo(pool), NewTxManager(pool), clk)
|
||||
|
||||
cancelled, err := uc.Execute(ctx, job.ID)
|
||||
if err != nil || cancelled != 3 {
|
||||
t.Fatalf("cancel = (%d, %v), want (3, nil)", cancelled, err)
|
||||
}
|
||||
stored, err := NewJobRepo(pool).Get(ctx, job.ID)
|
||||
if err != nil || stored.Status != domain.JobCancelled {
|
||||
t.Fatalf("job after cancel = (%+v, %v)", stored, err)
|
||||
}
|
||||
counts, err := NewTaskRepo(pool).CountByStatus(ctx, job.ID)
|
||||
if err != nil || counts[domain.TaskCancelled] != 3 {
|
||||
t.Fatalf("cancelled tasks = %d, err = %v", counts[domain.TaskCancelled], err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateRejectsStaleVersion(t *testing.T) {
|
||||
pool := testPool(t)
|
||||
ctx := context.Background()
|
||||
|
||||
@@ -295,6 +295,29 @@ func (r *TaskRepo) CountByStatus(ctx context.Context, jobID uuid.UUID) (map[doma
|
||||
return counts, rows.Err()
|
||||
}
|
||||
|
||||
// cancelByJobSQL mirrors domain.Task.Cancel in one set-based update. It runs in
|
||||
// the same transaction as the job-status update, so no claimable shard remains
|
||||
// after an operator receives a successful cancellation response.
|
||||
const cancelByJobSQL = `
|
||||
UPDATE tasks
|
||||
SET status = 'cancelled'::task_status,
|
||||
lease_owner = NULL,
|
||||
lease_expires_at = NULL,
|
||||
error_code = NULL,
|
||||
error_message = NULL,
|
||||
completed_at = $2,
|
||||
version = version + 1
|
||||
WHERE job_id = $1
|
||||
AND status IN ('pending','leased','running')`
|
||||
|
||||
func (r *TaskRepo) CancelByJob(ctx context.Context, jobID uuid.UUID, now time.Time) (int64, error) {
|
||||
tag, err := conn(ctx, r.pool).Exec(ctx, cancelByJobSQL, jobID, now)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return tag.RowsAffected(), nil
|
||||
}
|
||||
|
||||
// expireLeasesSQL applies the lease-expiry rule set-based, mirroring
|
||||
// domain.Task.ExpireLease: requeue while attempts remain, otherwise fail.
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user