Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ name: build

on:
push:
branches:
tags:
branches: ["**"]
tags: ["**"]
pull_request:

jobs:
Expand All @@ -12,10 +12,10 @@ jobs:

steps:
- name: checkout
uses: actions/checkout@v4
uses: actions/checkout@v7

- name: set up go
uses: actions/setup-go@v5
uses: actions/setup-go@v7
with:
go-version: "1.24"
id: go
Expand All @@ -29,9 +29,9 @@ jobs:
go build -race

- name: golangci-lint
uses: golangci/golangci-lint-action@v7
uses: golangci/golangci-lint-action@v9
with:
version: v2.7
version: v2.12.2

- name: install goveralls
run: |
Expand Down
60 changes: 30 additions & 30 deletions bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func TestPoolPerf(t *testing.T) {

var egDuration time.Duration
t.Run("errgroup", func(t *testing.T) {
var count2 int32
var count2 atomic.Int32
st := time.Now()
defer func() {
egDuration = time.Since(st)
Expand All @@ -47,20 +47,20 @@ func TestPoolPerf(t *testing.T) {
for range 1000000 {
g.Go(func() error {
benchTask(n)
atomic.AddInt32(&count2, 1)
count2.Add(1)
return nil
})
}
require.NoError(t, g.Wait())
assert.Equal(t, int32(1000000), atomic.LoadInt32(&count2))
assert.Equal(t, int32(1000000), count2.Load())
})

t.Run("pool default", func(t *testing.T) {
// pool with 8 workers
var count1 int32
var count1 atomic.Int32
worker := WorkerFunc[int](func(context.Context, int) error {
benchTask(n)
atomic.AddInt32(&count1, 1)
count1.Add(1)
return nil
})

Expand All @@ -74,17 +74,17 @@ func TestPoolPerf(t *testing.T) {
assert.NoError(t, p.Close(ctx))
}()
require.NoError(t, p.Wait(ctx))
assert.Equal(t, int32(1000000), atomic.LoadInt32(&count1))
assert.Equal(t, int32(1000000), count1.Load())
t.Logf("elapsed pool: %v", time.Since(st))
assert.Less(t, time.Since(st), egDuration)
})

t.Run("pool with 100 chan size", func(t *testing.T) {
// pool with 8 workers
var count1 int32
var count1 atomic.Int32
worker := WorkerFunc[int](func(context.Context, int) error {
benchTask(n)
atomic.AddInt32(&count1, 1)
count1.Add(1)
return nil
})

Expand All @@ -98,17 +98,17 @@ func TestPoolPerf(t *testing.T) {
assert.NoError(t, p.Close(ctx))
}()
require.NoError(t, p.Wait(ctx))
assert.Equal(t, int32(1000000), atomic.LoadInt32(&count1))
assert.Equal(t, int32(1000000), count1.Load())
t.Logf("elapsed pool: %v", time.Since(st))
assert.Less(t, time.Since(st), egDuration)
})

t.Run("pool with 100 chan size and 100 batch size", func(t *testing.T) {
// pool with 8 workers
var count1 int32
var count1 atomic.Int32
worker := WorkerFunc[int](func(context.Context, int) error {
benchTask(n)
atomic.AddInt32(&count1, 1)
count1.Add(1)
return nil
})

Expand All @@ -122,17 +122,17 @@ func TestPoolPerf(t *testing.T) {
assert.NoError(t, p.Close(ctx))
}()
require.NoError(t, p.Wait(ctx))
assert.Equal(t, int32(1000000), atomic.LoadInt32(&count1))
assert.Equal(t, int32(1000000), count1.Load())
t.Logf("elapsed pool: %v", time.Since(st))
assert.Less(t, time.Since(st), egDuration)
})

t.Run("pool with 100 chan size and 100 batch size and chunking", func(t *testing.T) {
// pool with 8 workers
var count1 int32
var count1 atomic.Int32
worker := WorkerFunc[int](func(context.Context, int) error {
benchTask(n)
atomic.AddInt32(&count1, 1)
count1.Add(1)
return nil
})

Expand All @@ -148,7 +148,7 @@ func TestPoolPerf(t *testing.T) {
assert.NoError(t, p.Close(ctx))
}()
require.NoError(t, p.Wait(ctx))
assert.Equal(t, int32(1000000), atomic.LoadInt32(&count1))
assert.Equal(t, int32(1000000), count1.Load())
t.Logf("elapsed pool: %v", time.Since(st))
assert.Less(t, time.Since(st), egDuration)
})
Expand All @@ -164,29 +164,29 @@ func BenchmarkPoolCompare(b *testing.B) {
b.Run("errgroup", func(b *testing.B) {
b.ResetTimer()
for range b.N {
var count int32
var count atomic.Int32
g, _ := errgroup.WithContext(ctx)
g.SetLimit(workers)

for range iterations {
g.Go(func() error {
benchTask(n)
atomic.AddInt32(&count, 1)
count.Add(1)
return nil
})
}
require.NoError(b, g.Wait())
require.Equal(b, int32(iterations), atomic.LoadInt32(&count))
require.Equal(b, int32(iterations), count.Load())
}
})

b.Run("pool default", func(b *testing.B) {
b.ResetTimer()
for range b.N {
var count int32
var count atomic.Int32
p := New[int](workers, WorkerFunc[int](func(context.Context, int) error {
benchTask(n)
atomic.AddInt32(&count, 1)
count.Add(1)
return nil
}))

Expand All @@ -198,17 +198,17 @@ func BenchmarkPoolCompare(b *testing.B) {
p.Close(ctx)
}()
require.NoError(b, p.Wait(ctx))
require.Equal(b, int32(iterations), atomic.LoadInt32(&count))
require.Equal(b, int32(iterations), count.Load())
}
})

b.Run("pool with chan=100", func(b *testing.B) {
b.ResetTimer()
for range b.N {
var count int32
var count atomic.Int32
p := New[int](workers, WorkerFunc[int](func(context.Context, int) error {
benchTask(n)
atomic.AddInt32(&count, 1)
count.Add(1)
return nil
})).WithWorkerChanSize(100)

Expand All @@ -220,17 +220,17 @@ func BenchmarkPoolCompare(b *testing.B) {
p.Close(ctx)
}()
require.NoError(b, p.Wait(ctx))
require.Equal(b, int32(iterations), atomic.LoadInt32(&count))
require.Equal(b, int32(iterations), count.Load())
}
})

b.Run("pool with batching", func(b *testing.B) {
b.ResetTimer()
for range b.N {
var count int32
var count atomic.Int32
p := New[int](workers, WorkerFunc[int](func(context.Context, int) error {
benchTask(n)
atomic.AddInt32(&count, 1)
count.Add(1)
return nil
})).WithWorkerChanSize(100).WithBatchSize(100)

Expand All @@ -242,17 +242,17 @@ func BenchmarkPoolCompare(b *testing.B) {
p.Close(ctx)
}()
require.NoError(b, p.Wait(ctx))
require.Equal(b, int32(iterations), atomic.LoadInt32(&count))
require.Equal(b, int32(iterations), count.Load())
}
})

b.Run("pool with batching and chunking", func(b *testing.B) {
b.ResetTimer()
for range b.N {
var count int32
var count atomic.Int32
p := New[int](workers, WorkerFunc[int](func(context.Context, int) error {
benchTask(n)
atomic.AddInt32(&count, 1)
count.Add(1)
return nil
})).WithWorkerChanSize(100).WithBatchSize(100).WithChunkFn(func(v int) string {
return strconv.Itoa(v % workers)
Expand All @@ -266,7 +266,7 @@ func BenchmarkPoolCompare(b *testing.B) {
p.Close(ctx)
}()
require.NoError(b, p.Wait(ctx))
require.Equal(b, int32(iterations), atomic.LoadInt32(&count))
require.Equal(b, int32(iterations), count.Load())
}
})
}
Expand Down
9 changes: 5 additions & 4 deletions pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"fmt"
"hash/fnv"
"math/rand"
"slices"
"sync"
"sync/atomic"
"time"
Expand Down Expand Up @@ -566,9 +567,9 @@ func (p *WorkerGroup[T]) Use(middlewares ...Middleware[T]) *WorkerGroup[T] {
// apply middlewares in order from last to first
// this makes first middleware outermost
wrapped := worker
for i := len(middlewares) - 1; i >= 0; i-- {
for _, v := range slices.Backward(middlewares) {
prev := wrapped
wrapped = middlewares[i](prev)
wrapped = v(prev)
}
return wrapped
}
Expand All @@ -577,9 +578,9 @@ func (p *WorkerGroup[T]) Use(middlewares ...Middleware[T]) *WorkerGroup[T] {

// for stateless worker, just wrap it directly
wrapped := p.worker
for i := len(middlewares) - 1; i >= 0; i-- {
for _, v := range slices.Backward(middlewares) {
prev := wrapped
wrapped = middlewares[i](prev)
wrapped = v(prev)
}
p.worker = wrapped
return p
Expand Down
12 changes: 6 additions & 6 deletions pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,10 +333,10 @@ func TestPool_Distribution(t *testing.T) {

func TestPool_Metrics(t *testing.T) {
t.Run("basic metrics", func(t *testing.T) {
var processed int32
var processed atomic.Int32
worker := WorkerFunc[int](func(ctx context.Context, _ int) error {
time.Sleep(time.Millisecond) // simulate work
atomic.AddInt32(&processed, 1)
processed.Add(1)
return nil
})

Expand All @@ -349,7 +349,7 @@ func TestPool_Metrics(t *testing.T) {
require.NoError(t, p.Close(context.Background()))

stats := p.Metrics().GetStats()
assert.Equal(t, int(atomic.LoadInt32(&processed)), stats.Processed)
assert.Equal(t, int(processed.Load()), stats.Processed)
assert.Equal(t, 0, stats.Errors)
assert.Equal(t, 0, stats.Dropped)
assert.Greater(t, stats.ProcessingTime, time.Duration(0))
Expand Down Expand Up @@ -948,12 +948,12 @@ func TestMiddleware_Practical(t *testing.T) {
return nil
})

var totalTime int64
var totalTime atomic.Int64
timingMiddleware := func(next Worker[string]) Worker[string] {
return WorkerFunc[string](func(ctx context.Context, v string) error {
start := time.Now()
err := next.Do(ctx, v)
atomic.AddInt64(&totalTime, time.Since(start).Microseconds())
totalTime.Add(time.Since(start).Microseconds())
return err
})
}
Expand All @@ -964,7 +964,7 @@ func TestMiddleware_Practical(t *testing.T) {
p.Submit("test")
require.NoError(t, p.Close(context.Background()))

assert.Greater(t, atomic.LoadInt64(&totalTime), int64(1000),
assert.Greater(t, totalTime.Load(), int64(1000),
"should measure time greater than 1ms")
})
}
Expand Down