mirror of
https://github.com/project-zot/zot.git
synced 2026-06-17 04:48:26 +08:00
fix(scheduler): fix data race (#2085)
* fix(scheduler): data race when pushing new tasks the problem here is that scheduler can be closed in two ways: - canceling the context given as argument to scheduler.RunScheduler() - running scheduler.Shutdown() because of this shutdown can trigger a data race between calling scheduler.inShutdown() and actually pushing tasks into the pool workers solved that by keeping a quit channel and listening on both quit channel and ctx.Done() and closing the worker chan and scheduler afterwards. Signed-off-by: Petu Eusebiu <peusebiu@cisco.com> * refactor(scheduler): refactor into a single shutdown before this we could stop scheduler either by closing the context provided to RunScheduler(ctx) or by running Shutdown(). simplify things by getting rid of the external context in RunScheduler(). keep an internal context in the scheduler itself and pass it down to all tasks. Signed-off-by: Petu Eusebiu <peusebiu@cisco.com> --------- Signed-off-by: Petu Eusebiu <peusebiu@cisco.com>
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
@@ -62,44 +61,34 @@ func Location(baseURL string, resp *resty.Response) string {
|
||||
}
|
||||
|
||||
type Controller interface {
|
||||
Init(ctx context.Context) error
|
||||
Run(ctx context.Context) error
|
||||
Init() error
|
||||
Run() error
|
||||
Shutdown()
|
||||
GetPort() int
|
||||
}
|
||||
|
||||
type ControllerManager struct {
|
||||
controller Controller
|
||||
// used to stop background tasks(goroutines)
|
||||
cancelRoutinesFunc context.CancelFunc
|
||||
}
|
||||
|
||||
func (cm *ControllerManager) RunServer(ctx context.Context) {
|
||||
func (cm *ControllerManager) RunServer() {
|
||||
// Useful to be able to call in the same goroutine for testing purposes
|
||||
if err := cm.controller.Run(ctx); !errors.Is(err, http.ErrServerClosed) {
|
||||
if err := cm.controller.Run(); !errors.Is(err, http.ErrServerClosed) {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (cm *ControllerManager) StartServer() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cm.cancelRoutinesFunc = cancel
|
||||
|
||||
if err := cm.controller.Init(ctx); err != nil {
|
||||
if err := cm.controller.Init(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
cm.RunServer(ctx)
|
||||
cm.RunServer()
|
||||
}()
|
||||
}
|
||||
|
||||
func (cm *ControllerManager) StopServer() {
|
||||
// stop background tasks
|
||||
if cm.cancelRoutinesFunc != nil {
|
||||
cm.cancelRoutinesFunc()
|
||||
}
|
||||
|
||||
cm.controller.Shutdown()
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package common_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
@@ -53,11 +52,9 @@ func TestControllerManager(t *testing.T) {
|
||||
ctlr := api.NewController(conf)
|
||||
ctlrManager := tcommon.NewControllerManager(ctlr)
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
err := ctlr.Init(ctx)
|
||||
err := ctlr.Init()
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(func() { ctlrManager.RunServer(ctx) }, ShouldPanic)
|
||||
So(func() { ctlrManager.RunServer() }, ShouldPanic)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package mocks
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
||||
type SyncRemote struct {
|
||||
// Get temporary ImageReference, is used by functions in containers/image package
|
||||
GetImageReferenceFn func(repo string, tag string) (types.ImageReference, error)
|
||||
|
||||
// Get local oci layout context, is used by functions in containers/image package
|
||||
GetContextFn func() *types.SystemContext
|
||||
|
||||
// Get a list of repos (catalog)
|
||||
GetRepositoriesFn func(ctx context.Context) ([]string, error)
|
||||
|
||||
// Get a list of tags given a repo
|
||||
GetRepoTagsFn func(repo string) ([]string, error)
|
||||
|
||||
// Get manifest content, mediaType, digest given an ImageReference
|
||||
GetManifestContentFn func(imageReference types.ImageReference) ([]byte, string, digest.Digest, error)
|
||||
}
|
||||
|
||||
func (remote SyncRemote) GetImageReference(repo string, tag string) (types.ImageReference, error) {
|
||||
if remote.GetImageReferenceFn != nil {
|
||||
return remote.GetImageReferenceFn(repo, tag)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (remote SyncRemote) GetContext() *types.SystemContext {
|
||||
if remote.GetContextFn != nil {
|
||||
return remote.GetContextFn()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (remote SyncRemote) GetRepositories(ctx context.Context) ([]string, error) {
|
||||
if remote.GetRepositoriesFn != nil {
|
||||
return remote.GetRepositoriesFn(ctx)
|
||||
}
|
||||
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (remote SyncRemote) GetRepoTags(repo string) ([]string, error) {
|
||||
if remote.GetRepoTagsFn != nil {
|
||||
return remote.GetRepoTagsFn(repo)
|
||||
}
|
||||
|
||||
return []string{}, nil
|
||||
}
|
||||
|
||||
func (remote SyncRemote) GetManifestContent(imageReference types.ImageReference) (
|
||||
[]byte, string, digest.Digest, error,
|
||||
) {
|
||||
if remote.GetManifestContentFn != nil {
|
||||
return remote.GetManifestContentFn(imageReference)
|
||||
}
|
||||
|
||||
return nil, "", "", nil
|
||||
}
|
||||
Reference in New Issue
Block a user