diff --git a/eventfilter_test.go b/eventfilter_test.go index a5049b18..95f87e3a 100644 --- a/eventfilter_test.go +++ b/eventfilter_test.go @@ -56,7 +56,7 @@ func TestShardNonNumerical(t *testing.T) { left, right []string total = 1000 ) - for i := 0; i < total; i++ { + for range total { uid, err := uuid.NewUUID() require.NoError(t, err) diff --git a/filter.go b/filter.go index 41c4abe9..7af50370 100644 --- a/filter.go +++ b/filter.go @@ -1,6 +1,7 @@ package workflow import ( + "slices" "strconv" "strings" "time" @@ -63,13 +64,7 @@ func (f Filter) Matches(findValue string) bool { return f.value == findValue } - for _, filterValue := range f.MultiValues() { - if filterValue == findValue { - return true - } - } - - return false + return slices.Contains(f.MultiValues(), findValue) } func (f Filter) MultiValues() []string { @@ -113,15 +108,15 @@ func FilterByForeignID(foreignIDs ...string) RecordFilter { return } - var val string + var val strings.Builder for i, foreignID := range foreignIDs { if i != 0 { - val += multiValueDelimiter + val.WriteString(multiValueDelimiter) } - val += foreignID + val.WriteString(foreignID) } - filters.byForeignID = makeFilterValue(val, true) + filters.byForeignID = makeFilterValue(val.String(), true) } } @@ -133,15 +128,15 @@ func FilterByStatus[statusType ~int | ~int8 | ~int16 | ~int32 | ~int64](statuses return } - var val string + var val strings.Builder for i, status := range statuses { if i != 0 { - val += multiValueDelimiter + val.WriteString(multiValueDelimiter) } - val += strconv.FormatInt(int64(status), 10) + val.WriteString(strconv.FormatInt(int64(status), 10)) } - filters.byStatus = makeFilterValue(val, true) + filters.byStatus = makeFilterValue(val.String(), true) } } @@ -153,15 +148,15 @@ func FilterByRunState(runStates ...RunState) RecordFilter { return } - var val string + var val strings.Builder for i, rs := range runStates { if i != 0 { - val += multiValueDelimiter + val.WriteString(multiValueDelimiter) } - val += strconv.FormatInt(int64(rs), 10) + val.WriteString(strconv.FormatInt(int64(rs), 10)) } - filters.byRunState = makeFilterValue(val, true) + filters.byRunState = makeFilterValue(val.String(), true) } } diff --git a/internal/cron/cron.go b/internal/cron/cron.go index 40543d03..7475ecdb 100644 --- a/internal/cron/cron.go +++ b/internal/cron/cron.go @@ -153,7 +153,7 @@ type cronSchedule struct { func (c *cronSchedule) Next(t time.Time) (time.Time, bool) { next := t.Add(time.Minute).Truncate(time.Minute) - for i := 0; i < 4*365*24*60; i++ { + for range 4 * 365 * 24 * 60 { if c.matches(next) { return next, true } diff --git a/internal/stack/stack.go b/internal/stack/stack.go index dbab866a..50bd0847 100644 --- a/internal/stack/stack.go +++ b/internal/stack/stack.go @@ -16,7 +16,7 @@ func Trace() string { trace := string(stack[:n]) lines := strings.Split(trace, "\n") - var stackTrace string + var stackTrace strings.Builder for _, line := range lines { if strings.Contains(line, "github.com/luno/workflow") { continue @@ -30,8 +30,8 @@ func Trace() string { continue } - stackTrace += line + "\n" + stackTrace.WriteString(line + "\n") } - return stackTrace + return stackTrace.String() } diff --git a/run.go b/run.go index 7e0c5887..f2905c58 100644 --- a/run.go +++ b/run.go @@ -57,13 +57,13 @@ type ( // newRunPool creates a new sync.Pool for Run objects with 10 pre-allocated instances func newRunPool[Type any, Status StatusType]() *sync.Pool { pool := sync.Pool{ - New: func() interface{} { + New: func() any { return &Run[Type, Status]{} }, } // Pre-allocate 10 Run objects in the pool for better performance - for i := 0; i < 10; i++ { + for range 10 { pool.Put(&Run[Type, Status]{}) } diff --git a/state.go b/state.go index 7adb75ed..dae76b18 100644 --- a/state.go +++ b/state.go @@ -1,6 +1,7 @@ package workflow import ( + "maps" "strconv" "github.com/luno/workflow/internal/metrics" @@ -45,9 +46,7 @@ func (w *Workflow[Type, Status]) States() map[string]State { defer w.internalStateMu.Unlock() states := make(map[string]State) - for k, v := range w.internalState { - states[k] = v - } + maps.Copy(states, w.internalState) return states } diff --git a/update.go b/update.go index c0ff2f58..0b48a663 100644 --- a/update.go +++ b/update.go @@ -3,6 +3,7 @@ package workflow import ( "context" "fmt" + "slices" "k8s.io/utils/clock" @@ -86,11 +87,8 @@ func validateTransition[Status StatusType](current, next Status, graph *graph.Gr var found bool // Attempt to find the next status amongst the list of valid transitions - for _, node := range nodes { - if node == int(next) { - found = true - break - } + if slices.Contains(nodes, int(next)) { + found = true } // If no valid transition matches that of the next status then error. diff --git a/workflow_internal_test.go b/workflow_internal_test.go index 9201d6d2..fe84574b 100644 --- a/workflow_internal_test.go +++ b/workflow_internal_test.go @@ -185,31 +185,31 @@ func TestWorkflow_RunStopRace(t *testing.T) { // It specifically tests that w.cancel is properly protected by a mutex during concurrent // access from Run() (write) and Stop() (read). // Run with: go test -race - + ctx := context.Background() - + // Run multiple iterations to increase the chance of detecting the race condition - for i := 0; i < 100; i++ { + for range 100 { // Build a minimal workflow using the Builder b := NewBuilder[string, testStatus]("race-test") b.AddStep(statusStart, func(ctx context.Context, r *Run[string, testStatus]) (testStatus, error) { return statusEnd, nil }, statusEnd) - + wf := b.Build( &noopEventStreamer{}, &noopRecordStore{}, &noopScheduler{}, WithoutOutbox(), ) - + // Start Run in a goroutine - this will write to w.cancel inside once.Do done := make(chan struct{}) go func() { wf.Run(ctx) close(done) }() - + // Immediately call Stop - this reads w.cancel // Without the mutex protection, this would race with the write in Run() wf.Stop()