[#1321] node: Register GC event channel before shard init

Morph "NewEpoch" event handling was registered in a closure over
`addNewEpochNotificationHandler` func. That may lead to the data race:
if a shard was initialized before the event registration, everything works
as planned, but if registration was made earlier, it was not able to
include GC handlers since a shard has not called `eventChanInit` yet and,
therefore, it has not registered handler yet.

Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
This commit is contained in:
Pavel Karpy 2022-04-21 16:37:42 +03:00 committed by LeL
parent d4569946c5
commit bd27837364
4 changed files with 14 additions and 26 deletions

View file

@ -401,6 +401,11 @@ func initShardOptions(c *cfg) {
metaPerm := metabaseCfg.Perm()
fatalOnErr(util.MkdirAllX(filepath.Dir(metaPath), metaPerm))
gcEventChannel := make(chan shard.Event)
addNewEpochNotificationHandler(c, func(ev event.Event) {
gcEventChannel <- shard.EventNewEpoch(ev.(netmap2.NewEpoch).EpochNumber())
})
opts = append(opts, []shard.Option{
shard.WithLogger(c.log),
shard.WithRefillMetabase(sc.RefillMetabase()),
@ -435,15 +440,7 @@ func initShardOptions(c *cfg) {
return pool
}),
shard.WithGCEventChannelInitializer(func() <-chan shard.Event {
ch := make(chan shard.Event)
addNewEpochNotificationHandler(c, func(ev event.Event) {
ch <- shard.EventNewEpoch(ev.(netmap2.NewEpoch).EpochNumber())
})
return ch
}),
shard.WithGCEventChannel(gcEventChannel),
})
})

View file

@ -38,9 +38,7 @@ func TestLockUserScenario(t *testing.T) {
e := testEngineFromShardOpts(t, 2, func(i int) []shard.Option {
return []shard.Option{
shard.WithGCEventChannelInitializer(func() <-chan shard.Event {
return chEvents[i]
}),
shard.WithGCEventChannel(chEvents[i]),
shard.WithGCWorkerPoolInitializer(func(sz int) util.WorkerPool {
pool, err := ants.NewPool(sz)
require.NoError(t, err)
@ -136,9 +134,7 @@ func TestLockExpiration(t *testing.T) {
e := testEngineFromShardOpts(t, 2, func(i int) []shard.Option {
return []shard.Option{
shard.WithGCEventChannelInitializer(func() <-chan shard.Event {
return chEvents[i]
}),
shard.WithGCEventChannel(chEvents[i]),
shard.WithGCWorkerPoolInitializer(func(sz int) util.WorkerPool {
pool, err := ants.NewPool(sz)
require.NoError(t, err)

View file

@ -64,7 +64,7 @@ type gc struct {
}
type gcCfg struct {
eventChanInit func() <-chan Event
eventChan <-chan Event
removerInterval time.Duration
@ -78,9 +78,7 @@ func defaultGCCfg() *gcCfg {
close(ch)
return &gcCfg{
eventChanInit: func() <-chan Event {
return ch
},
eventChan: ch,
removerInterval: 10 * time.Second,
log: zap.L(),
workerPoolInit: func(int) util.WorkerPool {
@ -105,10 +103,8 @@ func (gc *gc) init() {
}
func (gc *gc) listenEvents() {
eventChan := gc.eventChanInit()
for {
event, ok := <-eventChan
event, ok := <-gc.eventChan
if !ok {
gc.log.Warn("stop event listener by closed channel")
return

View file

@ -167,11 +167,10 @@ func WithGCWorkerPoolInitializer(wpInit func(int) util.WorkerPool) Option {
}
}
// WithGCEventChannelInitializer returns option to set set initializer of
// GC event channel.
func WithGCEventChannelInitializer(chInit func() <-chan Event) Option {
// WithGCEventChannel returns option to set a GC event channel.
func WithGCEventChannel(eventChan <-chan Event) Option {
return func(c *cfg) {
c.gcCfg.eventChanInit = chInit
c.gcCfg.eventChan = eventChan
}
}