frostfs-node/pkg/util/worker_pool.go
Aleksey Savchuk b947896005
All checks were successful
Tests and linters / Run gofumpt (pull_request) Successful in 1m31s
DCO action / DCO (pull_request) Successful in 1m56s
Vulncheck / Vulncheck (pull_request) Successful in 2m37s
Pre-commit hooks / Pre-commit (pull_request) Successful in 2m49s
Build / Build Components (pull_request) Successful in 2m56s
Tests and linters / gopls check (pull_request) Successful in 3m15s
Tests and linters / Staticcheck (pull_request) Successful in 3m17s
Tests and linters / Lint (pull_request) Successful in 4m3s
Tests and linters / Tests (pull_request) Successful in 4m42s
Tests and linters / Tests with -race (pull_request) Successful in 6m18s
[#1450] engine: Group object by shard before Inhume
```
goos: linux
goarch: amd64
pkg: git.frostfs.info/TrueCloudLab/frostfs-node/pkg/local_object_storage/engine
cpu: 12th Gen Intel(R) Core(TM) i5-1235U
                                 │    old.txt     │               new2.txt               │
                                 │     sec/op     │    sec/op     vs base                │
InhumeMultipart/objects=1-12          11.42m ± 1%   10.73m ±  0%   -6.12% (p=0.000 n=10)
InhumeMultipart/objects=10-12        113.51m ± 0%   11.00m ±  1%  -90.31% (p=0.000 n=10)
InhumeMultipart/objects=100-12      1135.41m ± 1%   22.36m ±  1%  -98.03% (p=0.000 n=10)
InhumeMultipart/objects=1000-12    11357.77m ± 0%   41.97m ± 24%  -99.63% (p=0.000 n=10)
InhumeMultipart/objects=10000-12   113250.7m ± 0%   273.9m ±  3%  -99.76% (p=0.000 n=10)
geomean                                1.136        31.36m        -97.24%
```

Signed-off-by: Aleksey Savchuk <a.savchuk@yadro.com>
2024-11-18 16:19:32 +03:00

110 lines
2.4 KiB
Go

package util
import (
"context"
"sync"
"sync/atomic"
"github.com/panjf2000/ants/v2"
)
// WorkerPool represents a tool to control
// the execution of go-routine pool.
type WorkerPool interface {
// Submit queues a function for execution
// in a separate routine.
//
// Implementation must return any error encountered
// that prevented the function from being queued.
Submit(func()) error
// Release releases worker pool resources. All `Submit` calls will
// finish with ErrPoolClosed. It doesn't wait until all submitted
// functions have returned so synchronization must be achieved
// via other means (e.g. sync.WaitGroup).
Release()
}
// pseudoWorkerPool represents a pseudo worker pool which executes the submitted job immediately in the caller's routine.
type pseudoWorkerPool struct {
closed atomic.Bool
}
// ErrPoolClosed is returned when submitting task to a closed pool.
var ErrPoolClosed = ants.ErrPoolClosed
// NewPseudoWorkerPool returns a new instance of a synchronous worker pool.
func NewPseudoWorkerPool() WorkerPool {
return &pseudoWorkerPool{}
}
// Submit executes the passed function immediately.
//
// Always returns nil.
func (p *pseudoWorkerPool) Submit(fn func()) error {
if p.closed.Load() {
return ErrPoolClosed
}
fn()
return nil
}
// Release implements the WorkerPool interface.
func (p *pseudoWorkerPool) Release() {
p.closed.Store(true)
}
type WorkerTask func(ctx context.Context) error
type WorkerPoolSubmitError struct {
err error
}
func (e *WorkerPoolSubmitError) Error() string {
return e.err.Error()
}
func (e *WorkerPoolSubmitError) Unwrap() error {
return e.err
}
// ExecuteWithWorkerPool runs tasks in parallel using a pool and waits for all
// tasks to be complete.
//
// Returns [WorkerPoolSubmitError] when it couldn't submit a task.
func ExecuteWithWorkerPool(ctx context.Context, pool WorkerPool, tasks []WorkerTask) error {
taskCtx, taskCancel := context.WithCancelCause(ctx)
defer taskCancel(nil)
var wg sync.WaitGroup
loop:
for _, task := range tasks {
select {
case <-ctx.Done():
taskCancel(context.Cause(ctx))
break loop
default:
}
wg.Add(1)
if err := pool.Submit(func() {
defer wg.Done()
if err := task(taskCtx); err != nil {
taskCancel(err)
}
}); err != nil {
wg.Done()
taskCancel(err)
wg.Wait()
return &WorkerPoolSubmitError{err}
}
}
wg.Wait()
return context.Cause(taskCtx)
}