From d36928afd3781ccfaf592c869167a9d408978efb Mon Sep 17 00:00:00 2001 From: "Alexander A. Klimov" Date: Thu, 8 Apr 2021 16:28:42 +0200 Subject: [PATCH] Delta#start(): avoid a race across maps by using a mutex Imagine an Icinga restart w/o any config changes and a full dump already being done. One goroutine reads Redis, the other the database. Both get the same object at the same time and check it in the map of the other goroutine - not present. So they store it in their own map. I.e. the same object hasn't been changed, but has to be deleted and inserted. If the insert comes first, that causes a duplicate key error. --- pkg/icingadb/delta.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkg/icingadb/delta.go b/pkg/icingadb/delta.go index 1deed748..4510997a 100644 --- a/pkg/icingadb/delta.go +++ b/pkg/icingadb/delta.go @@ -46,6 +46,7 @@ func (delta *Delta) start(ctx context.Context, actualCh, desiredCh <-chan contra } actual := sync.Map{} desired := sync.Map{} + var mtx sync.Mutex g, ctx := errgroup.WithContext(ctx) g.Go(func() error { @@ -61,14 +62,18 @@ func (delta *Delta) start(ctx context.Context, actualCh, desiredCh <-chan contra } id := a.ID().String() + mtx.Lock() + if d, ok := desired.Load(id); ok { desired.Delete(id) + mtx.Unlock() if delta.WithChecksum && !a.(contracts.Checksumer).Checksum().Equal(d.(contracts.Checksumer).Checksum()) { update.Store(id, d) } } else { actual.Store(id, a) + mtx.Unlock() } cnt.Inc() @@ -91,14 +96,18 @@ func (delta *Delta) start(ctx context.Context, actualCh, desiredCh <-chan contra } id := d.ID().String() + mtx.Lock() + if a, ok := actual.Load(id); ok { actual.Delete(id) + mtx.Unlock() if delta.WithChecksum && !a.(contracts.Checksumer).Checksum().Equal(d.(contracts.Checksumer).Checksum()) { update.Store(id, d) } } else { desired.Store(id, d) + mtx.Unlock() } cnt.Inc()