You've already forked agentic-coding-workflow
bugfix
This commit is contained in:
@@ -125,7 +125,7 @@ func TestComplete_ClearsLease(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
leased, err := repo.Lease(ctx, "h")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, repo.Complete(ctx, leased.ID))
|
||||
require.NoError(t, repo.Complete(ctx, leased.ID, "h"))
|
||||
got, err := repo.Get(ctx, job.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, jobs.StatusCompleted, got.Status)
|
||||
@@ -133,6 +133,49 @@ func TestComplete_ClearsLease(t *testing.T) {
|
||||
assert.Nil(t, got.LeasedBy)
|
||||
}
|
||||
|
||||
// TestComplete_LeaseLostIsNoOp verifies the fencing predicate: a write-back
|
||||
// from a worker that no longer holds the lease (e.g. the reaper re-leased the
|
||||
// job) matches 0 rows and returns ErrLeaseLost without clobbering the new run.
|
||||
func TestComplete_LeaseLostIsNoOp(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
repo, pool := setupRepo(t)
|
||||
_, err := repo.Enqueue(ctx, jobs.TypeWebhookDeliver, json.RawMessage(`{}`), time.Now(), 5)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Worker A leases the job, then its handler stalls past the lease timeout.
|
||||
leased, err := repo.Lease(ctx, "workerA")
|
||||
require.NoError(t, err)
|
||||
// Reaper resets it to pending; worker B re-leases it (now leased_by=workerB).
|
||||
_, err = pool.Exec(ctx, `UPDATE jobs SET leased_at = now() - INTERVAL '10 minutes' WHERE id=$1`, leased.ID)
|
||||
require.NoError(t, err)
|
||||
ids, err := repo.ReapStuckRunningJobs(ctx, time.Now().Add(-5*time.Minute))
|
||||
require.NoError(t, err)
|
||||
require.Len(t, ids, 1)
|
||||
reLeased, err := repo.Lease(ctx, "workerB")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, leased.ID, reLeased.ID)
|
||||
|
||||
// Stale worker A's terminal write-backs must be no-ops (ErrLeaseLost) and
|
||||
// must not change the row that workerB now owns.
|
||||
assert.ErrorIs(t, repo.Complete(ctx, leased.ID, "workerA"), jobs.ErrLeaseLost)
|
||||
assert.ErrorIs(t, repo.FailWithRetry(ctx, leased.ID, "workerA", time.Now(), "stale"), jobs.ErrLeaseLost)
|
||||
assert.ErrorIs(t, repo.DeadLetter(ctx, leased.ID, "workerA", "stale"), jobs.ErrLeaseLost)
|
||||
|
||||
got, err := repo.Get(ctx, leased.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, jobs.StatusRunning, got.Status, "row must still be owned by workerB's run")
|
||||
require.NotNil(t, got.LeasedBy)
|
||||
assert.Equal(t, "workerB", *got.LeasedBy)
|
||||
assert.Nil(t, got.LastError, "stale write must not have set an error")
|
||||
|
||||
// Worker B (the true owner) completes successfully.
|
||||
require.NoError(t, repo.Complete(ctx, leased.ID, "workerB"))
|
||||
got, err = repo.Get(ctx, leased.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, jobs.StatusCompleted, got.Status)
|
||||
}
|
||||
|
||||
func TestFailWithRetry_ReschedulesAndKeepsAttempts(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := context.Background()
|
||||
@@ -142,7 +185,7 @@ func TestFailWithRetry_ReschedulesAndKeepsAttempts(t *testing.T) {
|
||||
leased, err := repo.Lease(ctx, "h")
|
||||
require.NoError(t, err)
|
||||
next := time.Now().Add(30 * time.Second)
|
||||
require.NoError(t, repo.FailWithRetry(ctx, leased.ID, next, "boom"))
|
||||
require.NoError(t, repo.FailWithRetry(ctx, leased.ID, "h", next, "boom"))
|
||||
got, err := repo.Get(ctx, leased.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, jobs.StatusPending, got.Status)
|
||||
@@ -160,7 +203,7 @@ func TestDeadLetter_FinalState(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
leased, err := repo.Lease(ctx, "h")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, repo.DeadLetter(ctx, leased.ID, "max attempts"))
|
||||
require.NoError(t, repo.DeadLetter(ctx, leased.ID, "h", "max attempts"))
|
||||
got, err := repo.Get(ctx, leased.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, jobs.StatusDead, got.Status)
|
||||
@@ -204,7 +247,10 @@ func TestPurge_DeletesOldCompletedAndDead(t *testing.T) {
|
||||
deadOld := mkOld(jobs.StatusDead)
|
||||
freshComp, err := repo.Enqueue(ctx, jobs.TypeWebhookDeliver, json.RawMessage(`{}`), time.Now(), 5)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, repo.Complete(ctx, freshComp.ID))
|
||||
leasedFresh, err := repo.Lease(ctx, "h")
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, freshComp.ID, leasedFresh.ID)
|
||||
require.NoError(t, repo.Complete(ctx, freshComp.ID, "h"))
|
||||
|
||||
rows, err := repo.PurgeCompleted(ctx, time.Now().Add(-7*24*time.Hour))
|
||||
require.NoError(t, err)
|
||||
|
||||
Reference in New Issue
Block a user