Add job cancellation and dataset row limit
coordinator / test (push) Canceled after 0s

This commit is contained in:
Emil
2026-07-23 23:14:25 +03:00
parent 6bac7dad3c
commit 7547a30bde
30 changed files with 438 additions and 54 deletions
@@ -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.
//