Fix: harden bootstrap worker flow
This commit is contained in:
@@ -57,7 +57,13 @@ func (runner *Runner) Run(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
return err
|
||||
runner.logger.Error("worker claim failed", "error", err)
|
||||
|
||||
if err := waitForNextPoll(ctx, runner.pollInterval); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if job == nil {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"moku-backend/internal/jobs"
|
||||
)
|
||||
@@ -109,13 +110,53 @@ func TestRunnerMarksFailedWhenHandlerMissing(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunnerRetriesClaimErrors(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
store := &fakeJobStore{
|
||||
claimErrors: []error{errors.New("relation \"background_jobs\" does not exist")},
|
||||
job: &jobs.Job{
|
||||
ID: "job-4",
|
||||
Kind: jobs.KindBootstrapStructureMaterialize,
|
||||
},
|
||||
cancel: cancel,
|
||||
}
|
||||
runner := NewRunner(store, slog.New(slog.NewTextHandler(io.Discard, nil)), time.Millisecond)
|
||||
|
||||
handlerCalled := false
|
||||
runner.Register(jobs.KindBootstrapStructureMaterialize, func(ctx context.Context, job jobs.Job) error {
|
||||
handlerCalled = true
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := runner.Run(ctx); err != nil {
|
||||
t.Fatalf("runner returned error: %v", err)
|
||||
}
|
||||
|
||||
if !handlerCalled {
|
||||
t.Fatal("expected handler to be called after claim retry")
|
||||
}
|
||||
if store.claimAttempts < 2 {
|
||||
t.Fatalf("expected at least two claim attempts, got %d", store.claimAttempts)
|
||||
}
|
||||
if len(store.succeeded) != 1 || store.succeeded[0] != "job-4" {
|
||||
t.Fatalf("expected job to be marked succeeded once, got %#v", store.succeeded)
|
||||
}
|
||||
if len(store.failed) != 0 {
|
||||
t.Fatalf("expected no failed jobs, got %#v", store.failed)
|
||||
}
|
||||
}
|
||||
|
||||
type fakeJobStore struct {
|
||||
mu sync.Mutex
|
||||
job *jobs.Job
|
||||
claimed bool
|
||||
succeeded []string
|
||||
failed []fakeFailure
|
||||
cancel context.CancelFunc
|
||||
mu sync.Mutex
|
||||
job *jobs.Job
|
||||
claimed bool
|
||||
claimErrors []error
|
||||
claimAttempts int
|
||||
succeeded []string
|
||||
failed []fakeFailure
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
type fakeFailure struct {
|
||||
@@ -126,6 +167,13 @@ type fakeFailure struct {
|
||||
func (store *fakeJobStore) ClaimNext(ctx context.Context) (*jobs.Job, error) {
|
||||
store.mu.Lock()
|
||||
defer store.mu.Unlock()
|
||||
store.claimAttempts++
|
||||
|
||||
if len(store.claimErrors) > 0 {
|
||||
err := store.claimErrors[0]
|
||||
store.claimErrors = store.claimErrors[1:]
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if store.claimed || store.job == nil {
|
||||
return nil, nil
|
||||
|
||||
Reference in New Issue
Block a user