From 856da3a5ffeaaf833305674ca47066f2727c249b Mon Sep 17 00:00:00 2001 From: Dmitry Selivanov Date: Tue, 9 May 2023 17:41:06 +0200 Subject: [PATCH 01/11] feat: service tags --- discovery/consul_discovery.go | 3 +-- service/service.go | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/discovery/consul_discovery.go b/discovery/consul_discovery.go index b4d9d30..7066295 100644 --- a/discovery/consul_discovery.go +++ b/discovery/consul_discovery.go @@ -66,6 +66,5 @@ func (d *ConsulDiscovery) createNodesFromServices(consulServices []*consul.Servi func (d *ConsulDiscovery) createServiceFromConsul(srv *consul.ServiceEntry) service.IService { addr := d.transport.FormatAddress(srv.Service.Address) logger.Log().Debug(fmt.Sprintf("discovered new service: %s", addr)) - - return service.NewService(fmt.Sprintf("%s:%d", addr, srv.Service.Port), srv.Node.Node) + return service.NewService(fmt.Sprintf("%s:%d", addr, srv.Service.Port), srv.Node.Node, srv.Service.Tags) } diff --git a/service/service.go b/service/service.go index 30e7156..3ccd3b1 100644 --- a/service/service.go +++ b/service/service.go @@ -21,6 +21,8 @@ type IService interface { // NodeName return node name from discovery NodeName() string + + Tags() []string } // TODO split address field to host and port @@ -28,19 +30,21 @@ type IService interface { // BaseService represent basic service // model implementation type BaseService struct { - id string // service unique id - sha256(address) - status Status // service current status - address string // service address to connect - nodeName string // node name from discovery + id string // service unique id - sha256(address) + status Status // service current status + address string // service address to connect + nodeName string // node name from discovery + tags []string // service tags } // NewService create new BaseService with address and discovery -func NewService(address, nodeName string) IService { +func NewService(address, nodeName string, tags []string) IService { return &BaseService{ id: generateServiceID(address), status: StatusUnHealthy, address: address, nodeName: nodeName, + tags: tags, } } @@ -75,6 +79,10 @@ func (n *BaseService) SetStatus(status Status) { n.status = status } +func (n *BaseService) Tags() []string { + return n.tags +} + // generateServiceID create BaseService unique id by // hashing given address string func generateServiceID(addr string) string { From c8eda45868df370432f857ce1eff1a15d06075fd Mon Sep 17 00:00:00 2001 From: Dmitry Selivanov Date: Tue, 9 May 2023 17:47:08 +0200 Subject: [PATCH 02/11] fix/fix-linter-timeout-issues --- .github/workflows/main.yaml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml index 0f70282..9483e3a 100644 --- a/.github/workflows/main.yaml +++ b/.github/workflows/main.yaml @@ -19,28 +19,33 @@ jobs: GO111MODULE: on steps: - name: Check out code - uses: actions/checkout@v2 + uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0 - name: Configure git for private modules env: TOKEN: ${{ secrets.PROXY_PIPELINES_GITHUB_TOKEN }} run: git config --global url."https://oauth2:${TOKEN}@github.com/gateway-fm".insteadOf "https://github.com/gateway-fm" - - name: Setup Golang - uses: actions/setup-go@v2 + # Required by golangci-lint, according to docs + - name: Setup Golang Environment + uses: actions/setup-go@4d34df0c2316fe8122ab82dc22947d607c0c91f9 # v4.0.0 with: - go-version: "1.19.*" # The Go version to download (if necessary) and use. + go-version-file: go.mod + cache: true - run: | go version go clean -modcache - go mod tidy + go mod tidy - name: Run linter - uses: golangci/golangci-lint-action@v2 + uses: golangci/golangci-lint-action@v3 with: - version: latest + version: v1.52 + skip-cache: true skip-build-cache: true skip-pkg-cache: true + skip-go-installation: true + args: --timeout=10m - name: Test run: go test ./... From a449b2ce9ad0412382ce78c2229ec651fd5bae8d Mon Sep 17 00:00:00 2001 From: Dmitry Selivanov Date: Wed, 10 May 2023 09:50:00 +0200 Subject: [PATCH 03/11] fix: make Tags() method return map[string]struct{} --- discovery/consul_discovery.go | 8 +++++++- service/service.go | 16 ++++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/discovery/consul_discovery.go b/discovery/consul_discovery.go index 7066295..199cae3 100644 --- a/discovery/consul_discovery.go +++ b/discovery/consul_discovery.go @@ -66,5 +66,11 @@ func (d *ConsulDiscovery) createNodesFromServices(consulServices []*consul.Servi func (d *ConsulDiscovery) createServiceFromConsul(srv *consul.ServiceEntry) service.IService { addr := d.transport.FormatAddress(srv.Service.Address) logger.Log().Debug(fmt.Sprintf("discovered new service: %s", addr)) - return service.NewService(fmt.Sprintf("%s:%d", addr, srv.Service.Port), srv.Node.Node, srv.Service.Tags) + + tagsMap := make(map[string]struct{}) + for _, t := range srv.Service.Tags { + tagsMap[t] = struct{}{} + } + + return service.NewService(fmt.Sprintf("%s:%d", addr, srv.Service.Port), srv.Node.Node, tagsMap) } diff --git a/service/service.go b/service/service.go index 3ccd3b1..7317b0a 100644 --- a/service/service.go +++ b/service/service.go @@ -22,7 +22,7 @@ type IService interface { // NodeName return node name from discovery NodeName() string - Tags() []string + Tags() map[string]struct{} } // TODO split address field to host and port @@ -30,15 +30,15 @@ type IService interface { // BaseService represent basic service // model implementation type BaseService struct { - id string // service unique id - sha256(address) - status Status // service current status - address string // service address to connect - nodeName string // node name from discovery - tags []string // service tags + id string // service unique id - sha256(address) + status Status // service current status + address string // service address to connect + nodeName string // node name from discovery + tags map[string]struct{} // service tags } // NewService create new BaseService with address and discovery -func NewService(address, nodeName string, tags []string) IService { +func NewService(address, nodeName string, tags map[string]struct{}) IService { return &BaseService{ id: generateServiceID(address), status: StatusUnHealthy, @@ -79,7 +79,7 @@ func (n *BaseService) SetStatus(status Status) { n.status = status } -func (n *BaseService) Tags() []string { +func (n *BaseService) Tags() map[string]struct{} { return n.tags } From c0b68f24525a484bb34dfd986bfdc4ae559e4ded Mon Sep 17 00:00:00 2001 From: Dmitry Selivanov Date: Wed, 10 May 2023 11:59:59 +0200 Subject: [PATCH 04/11] wip: added callback param to DiscoverServices() --- discovery/manual_discovery.go | 2 +- services_list_test.go | 2 +- services_pool.go | 14 ++++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/discovery/manual_discovery.go b/discovery/manual_discovery.go index 1c90513..59fab2b 100644 --- a/discovery/manual_discovery.go +++ b/discovery/manual_discovery.go @@ -21,7 +21,7 @@ func NewManualDiscovery(transport TransportProtocol, addrs ...string) (IServiceD // blockchain addresses for requested networks func (d *ManualDiscovery) Discover(string) (nodes []service.IService, err error) { for _, n := range d.addresses { - nodes = append(nodes, service.NewService(d.transport.FormatAddress(n), "")) + nodes = append(nodes, service.NewService(d.transport.FormatAddress(n), "", nil)) } return } diff --git a/services_list_test.go b/services_list_test.go index 60f8d60..9e459ae 100644 --- a/services_list_test.go +++ b/services_list_test.go @@ -10,7 +10,7 @@ import ( ) func newHealthyService(addr string) service.IService { - srv := service.NewService(addr, "") + srv := service.NewService(addr, "", nil) baseSrv := srv.(*service.BaseService) baseSrv.SetStatus(service.StatusHealthy) diff --git a/services_pool.go b/services_pool.go index 81280cd..2057cd6 100644 --- a/services_pool.go +++ b/services_pool.go @@ -19,7 +19,7 @@ type IServicesPool interface { // DiscoverServices discover all visible active // services via service-discovery - DiscoverServices() error + DiscoverServices(callback func(srv service.IService) error) error // NextService returns next active service // to take a connection @@ -83,7 +83,7 @@ func NewServicesPool(opts *ServicesPoolsOpts) IServicesPool { } - if err := pool.DiscoverServices(); err != nil { + if err := pool.DiscoverServices(nil); err != nil { logger.Log().Error(fmt.Errorf("error discovering %s services: %w", pool.name, err).Error()) } @@ -99,7 +99,7 @@ func (p *ServicesPool) Start() { // DiscoverServices discover all visible active // services via service-discovery -func (p *ServicesPool) DiscoverServices() error { +func (p *ServicesPool) DiscoverServices(callback func(srv service.IService) error) error { newServices, err := p.discovery.Discover(p.name) if err != nil { return fmt.Errorf("error discovering %s active: %w", p.name, err) @@ -123,6 +123,12 @@ func (p *ServicesPool) DiscoverServices() error { } p.list.Add(mutatedService) + + if callback != nil { + if err := callback(mutatedService); err != nil { + logger.Log().Warn(fmt.Sprintf("callback on new discovered service: %s", err)) + } + } } return nil } @@ -163,7 +169,7 @@ func (p *ServicesPool) discoverServicesLoop() { logger.Log().Warn("Stop discovery loop") return default: - if err := p.DiscoverServices(); err != nil { + if err := p.DiscoverServices(nil); err != nil { logger.Log().Warn(fmt.Errorf("error discovery services: %w", err).Error()) } From 5ef143b5f7d3484fb002f54cda1e5f96d20c99cc Mon Sep 17 00:00:00 2001 From: Dmitry Selivanov Date: Wed, 10 May 2023 12:52:00 +0200 Subject: [PATCH 05/11] wip: added callback param to Start() --- services_pool.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/services_pool.go b/services_pool.go index 2057cd6..2eb829e 100644 --- a/services_pool.go +++ b/services_pool.go @@ -15,7 +15,7 @@ import ( type IServicesPool interface { // Start run service pool discovering // and healthchecks loops - Start() + Start(callback func(srv service.IService) error) // DiscoverServices discover all visible active // services via service-discovery @@ -92,8 +92,8 @@ func NewServicesPool(opts *ServicesPoolsOpts) IServicesPool { // Start run service pool discovering // and healthchecks loops -func (p *ServicesPool) Start() { - go p.discoverServicesLoop() +func (p *ServicesPool) Start(callback func(srv service.IService) error) { + go p.discoverServicesLoop(callback) go p.list.HealthChecksLoop() } @@ -159,7 +159,7 @@ func (p *ServicesPool) Close() { // discoverServicesLoop spawn discovery for // services periodically -func (p *ServicesPool) discoverServicesLoop() { +func (p *ServicesPool) discoverServicesLoop(callback func(srv service.IService) error) { logger.Log().Info("start discovery loop") onceShuffled := false @@ -169,7 +169,7 @@ func (p *ServicesPool) discoverServicesLoop() { logger.Log().Warn("Stop discovery loop") return default: - if err := p.DiscoverServices(nil); err != nil { + if err := p.DiscoverServices(callback); err != nil { logger.Log().Warn(fmt.Errorf("error discovery services: %w", err).Error()) } From 5ac2cfeedb6bea46bc5af2a8a6905e1bbd0e3d04 Mon Sep 17 00:00:00 2001 From: Dmitry Selivanov Date: Wed, 10 May 2023 13:24:10 +0200 Subject: [PATCH 06/11] wip: remove DiscoverServices() from NewServicesPool --- services_pool.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/services_pool.go b/services_pool.go index 2eb829e..5069a22 100644 --- a/services_pool.go +++ b/services_pool.go @@ -83,10 +83,6 @@ func NewServicesPool(opts *ServicesPoolsOpts) IServicesPool { } - if err := pool.DiscoverServices(nil); err != nil { - logger.Log().Error(fmt.Errorf("error discovering %s services: %w", pool.name, err).Error()) - } - return pool } From 1d90652f220893c92ad3f2be191b1de2bdefbd0f Mon Sep 17 00:00:00 2001 From: Dmitry Selivanov Date: Thu, 11 May 2023 09:37:10 +0200 Subject: [PATCH 07/11] feat: add healthchecks bool param to Start() --- services_pool.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/services_pool.go b/services_pool.go index 5069a22..2f9e1d7 100644 --- a/services_pool.go +++ b/services_pool.go @@ -15,7 +15,7 @@ import ( type IServicesPool interface { // Start run service pool discovering // and healthchecks loops - Start(callback func(srv service.IService) error) + Start(healthchecks bool, callback func(srv service.IService) error) // DiscoverServices discover all visible active // services via service-discovery @@ -88,9 +88,12 @@ func NewServicesPool(opts *ServicesPoolsOpts) IServicesPool { // Start run service pool discovering // and healthchecks loops -func (p *ServicesPool) Start(callback func(srv service.IService) error) { +func (p *ServicesPool) Start(healthchecks bool, callback func(srv service.IService) error) { go p.discoverServicesLoop(callback) - go p.list.HealthChecksLoop() + + if healthchecks { + go p.list.HealthChecksLoop() + } } // DiscoverServices discover all visible active From b2965552bddc886dc3fbe8d17a2770d057e9b635 Mon Sep 17 00:00:00 2001 From: Dmitry Selivanov Date: Thu, 11 May 2023 09:41:26 +0200 Subject: [PATCH 08/11] feat: add callbacks on the event when discovery completed --- services_pool.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/services_pool.go b/services_pool.go index 2f9e1d7..c611b5f 100644 --- a/services_pool.go +++ b/services_pool.go @@ -15,11 +15,11 @@ import ( type IServicesPool interface { // Start run service pool discovering // and healthchecks loops - Start(healthchecks bool, callback func(srv service.IService) error) + Start(healthchecks bool, onNewDiscCallback func(srv service.IService) error, onDiscCompletedCallback func()) // DiscoverServices discover all visible active // services via service-discovery - DiscoverServices(callback func(srv service.IService) error) error + DiscoverServices(onNewDiscCallback func(srv service.IService) error, onDiscCompletedCallback func()) error // NextService returns next active service // to take a connection @@ -88,8 +88,8 @@ func NewServicesPool(opts *ServicesPoolsOpts) IServicesPool { // Start run service pool discovering // and healthchecks loops -func (p *ServicesPool) Start(healthchecks bool, callback func(srv service.IService) error) { - go p.discoverServicesLoop(callback) +func (p *ServicesPool) Start(healthchecks bool, onNewDiscCallback func(srv service.IService) error, onDiscCompletedCallback func()) { + go p.discoverServicesLoop(onNewDiscCallback, onDiscCompletedCallback) if healthchecks { go p.list.HealthChecksLoop() @@ -98,7 +98,7 @@ func (p *ServicesPool) Start(healthchecks bool, callback func(srv service.IServi // DiscoverServices discover all visible active // services via service-discovery -func (p *ServicesPool) DiscoverServices(callback func(srv service.IService) error) error { +func (p *ServicesPool) DiscoverServices(onNewDiscCallback func(srv service.IService) error, onDiscCompletedCallback func()) error { newServices, err := p.discovery.Discover(p.name) if err != nil { return fmt.Errorf("error discovering %s active: %w", p.name, err) @@ -123,12 +123,14 @@ func (p *ServicesPool) DiscoverServices(callback func(srv service.IService) erro p.list.Add(mutatedService) - if callback != nil { - if err := callback(mutatedService); err != nil { + if onNewDiscCallback != nil { + if err := onNewDiscCallback(mutatedService); err != nil { logger.Log().Warn(fmt.Sprintf("callback on new discovered service: %s", err)) } } } + + onDiscCompletedCallback() return nil } @@ -158,7 +160,7 @@ func (p *ServicesPool) Close() { // discoverServicesLoop spawn discovery for // services periodically -func (p *ServicesPool) discoverServicesLoop(callback func(srv service.IService) error) { +func (p *ServicesPool) discoverServicesLoop(onNewDiscCallback func(srv service.IService) error, onDiscCompletedCallback func()) { logger.Log().Info("start discovery loop") onceShuffled := false @@ -168,7 +170,7 @@ func (p *ServicesPool) discoverServicesLoop(callback func(srv service.IService) logger.Log().Warn("Stop discovery loop") return default: - if err := p.DiscoverServices(callback); err != nil { + if err := p.DiscoverServices(onNewDiscCallback, onDiscCompletedCallback); err != nil { logger.Log().Warn(fmt.Errorf("error discovery services: %w", err).Error()) } From f0fb2f3368ed89ce727a1bb3892486c22c04308b Mon Sep 17 00:00:00 2001 From: Dmitry Selivanov Date: Thu, 11 May 2023 09:50:11 +0200 Subject: [PATCH 09/11] feat: minor refactor --- services_pool.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/services_pool.go b/services_pool.go index c611b5f..6709aa3 100644 --- a/services_pool.go +++ b/services_pool.go @@ -19,7 +19,7 @@ type IServicesPool interface { // DiscoverServices discover all visible active // services via service-discovery - DiscoverServices(onNewDiscCallback func(srv service.IService) error, onDiscCompletedCallback func()) error + DiscoverServices(onNewDiscCallback func(srv service.IService) error) error // NextService returns next active service // to take a connection @@ -98,7 +98,7 @@ func (p *ServicesPool) Start(healthchecks bool, onNewDiscCallback func(srv servi // DiscoverServices discover all visible active // services via service-discovery -func (p *ServicesPool) DiscoverServices(onNewDiscCallback func(srv service.IService) error, onDiscCompletedCallback func()) error { +func (p *ServicesPool) DiscoverServices(onNewDiscCallback func(srv service.IService) error) error { newServices, err := p.discovery.Discover(p.name) if err != nil { return fmt.Errorf("error discovering %s active: %w", p.name, err) @@ -129,8 +129,6 @@ func (p *ServicesPool) DiscoverServices(onNewDiscCallback func(srv service.IServ } } } - - onDiscCompletedCallback() return nil } @@ -170,7 +168,7 @@ func (p *ServicesPool) discoverServicesLoop(onNewDiscCallback func(srv service.I logger.Log().Warn("Stop discovery loop") return default: - if err := p.DiscoverServices(onNewDiscCallback, onDiscCompletedCallback); err != nil { + if err := p.DiscoverServices(onNewDiscCallback); err != nil { logger.Log().Warn(fmt.Errorf("error discovery services: %w", err).Error()) } @@ -178,6 +176,7 @@ func (p *ServicesPool) discoverServicesLoop(onNewDiscCallback func(srv service.I // and then Start() again if !onceShuffled { p.list.Shuffle() + onDiscCompletedCallback() onceShuffled = true } From 76639b4c4091af456bc1142929b74d9913f16323 Mon Sep 17 00:00:00 2001 From: Dmitry Selivanov Date: Thu, 11 May 2023 09:56:47 +0200 Subject: [PATCH 10/11] fix: nil pointer dereference --- services_pool.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/services_pool.go b/services_pool.go index 6709aa3..41f1d72 100644 --- a/services_pool.go +++ b/services_pool.go @@ -176,8 +176,11 @@ func (p *ServicesPool) discoverServicesLoop(onNewDiscCallback func(srv service.I // and then Start() again if !onceShuffled { p.list.Shuffle() - onDiscCompletedCallback() onceShuffled = true + + if onDiscCompletedCallback != nil { + onDiscCompletedCallback() + } } Sleep(p.discoveryInterval, p.stop) From 5db51396960e41f31b826b294b5d8a49e5405940 Mon Sep 17 00:00:00 2001 From: Dmitry Selivanov Date: Tue, 16 May 2023 12:46:52 +0200 Subject: [PATCH 11/11] fix: move discover callback function to Service struct instead of methods argument --- services_pool.go | 48 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/services_pool.go b/services_pool.go index 41f1d72..6de7ed6 100644 --- a/services_pool.go +++ b/services_pool.go @@ -15,11 +15,11 @@ import ( type IServicesPool interface { // Start run service pool discovering // and healthchecks loops - Start(healthchecks bool, onNewDiscCallback func(srv service.IService) error, onDiscCompletedCallback func()) + Start(healthchecks bool) // DiscoverServices discover all visible active // services via service-discovery - DiscoverServices(onNewDiscCallback func(srv service.IService) error) error + DiscoverServices() error // NextService returns next active service // to take a connection @@ -34,6 +34,10 @@ type IServicesPool interface { // Close Stop all service pool Close() + + SetOnNewDiscCallback(f OnNewDiscCallback) + + SetOnDiscCompletedCallback(f func()) } // ServicesPool holds information about reachable @@ -50,6 +54,10 @@ type ServicesPool struct { stop chan struct{} MutationFnc func(srv service.IService) (service.IService, error) + + onNewDiscCallback OnNewDiscCallback + + onDiscCompletedCallback func() } // ServicesPoolsOpts is options that needs @@ -65,6 +73,8 @@ type ServicesPoolsOpts struct { CustomList IServicesList } +type OnNewDiscCallback func(srv service.IService) error + // NewServicesPool create new Services Pool // based on given params func NewServicesPool(opts *ServicesPoolsOpts) IServicesPool { @@ -88,8 +98,8 @@ func NewServicesPool(opts *ServicesPoolsOpts) IServicesPool { // Start run service pool discovering // and healthchecks loops -func (p *ServicesPool) Start(healthchecks bool, onNewDiscCallback func(srv service.IService) error, onDiscCompletedCallback func()) { - go p.discoverServicesLoop(onNewDiscCallback, onDiscCompletedCallback) +func (p *ServicesPool) Start(healthchecks bool) { + go p.discoverServicesLoop() if healthchecks { go p.list.HealthChecksLoop() @@ -98,7 +108,7 @@ func (p *ServicesPool) Start(healthchecks bool, onNewDiscCallback func(srv servi // DiscoverServices discover all visible active // services via service-discovery -func (p *ServicesPool) DiscoverServices(onNewDiscCallback func(srv service.IService) error) error { +func (p *ServicesPool) DiscoverServices() error { newServices, err := p.discovery.Discover(p.name) if err != nil { return fmt.Errorf("error discovering %s active: %w", p.name, err) @@ -123,8 +133,8 @@ func (p *ServicesPool) DiscoverServices(onNewDiscCallback func(srv service.IServ p.list.Add(mutatedService) - if onNewDiscCallback != nil { - if err := onNewDiscCallback(mutatedService); err != nil { + if p.onNewDiscCallback != nil { + if err := p.onNewDiscCallback(mutatedService); err != nil { logger.Log().Warn(fmt.Sprintf("callback on new discovered service: %s", err)) } } @@ -156,9 +166,25 @@ func (p *ServicesPool) Close() { close(p.stop) } +func (p *ServicesPool) SetOnNewDiscCallback(f OnNewDiscCallback) { + if p == nil { + return + } + + p.onNewDiscCallback = f +} + +func (p *ServicesPool) SetOnDiscCompletedCallback(f func()) { + if p == nil { + return + } + + p.onDiscCompletedCallback = f +} + // discoverServicesLoop spawn discovery for // services periodically -func (p *ServicesPool) discoverServicesLoop(onNewDiscCallback func(srv service.IService) error, onDiscCompletedCallback func()) { +func (p *ServicesPool) discoverServicesLoop() { logger.Log().Info("start discovery loop") onceShuffled := false @@ -168,7 +194,7 @@ func (p *ServicesPool) discoverServicesLoop(onNewDiscCallback func(srv service.I logger.Log().Warn("Stop discovery loop") return default: - if err := p.DiscoverServices(onNewDiscCallback); err != nil { + if err := p.DiscoverServices(); err != nil { logger.Log().Warn(fmt.Errorf("error discovery services: %w", err).Error()) } @@ -178,8 +204,8 @@ func (p *ServicesPool) discoverServicesLoop(onNewDiscCallback func(srv service.I p.list.Shuffle() onceShuffled = true - if onDiscCompletedCallback != nil { - onDiscCompletedCallback() + if p.onDiscCompletedCallback != nil { + p.onDiscCompletedCallback() } }