diff --git a/CHANGELOG.md b/CHANGELOG.md index 25ed2b9a99..80f4b6ae74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ Changelog for NeoFS Node - `neofs-cli lock object`'s `lifetime` flag handling (#1972) - Do not move write-cache in read-only mode for flushing (#1906) - Child object collection on CLI side with a bearer token (#2000) +- Fix concurrent map writes in `Object.Put` service ### Removed ### Updated diff --git a/pkg/services/object/put/distributed.go b/pkg/services/object/put/distributed.go index 640e37049e..1ed21cadc0 100644 --- a/pkg/services/object/put/distributed.go +++ b/pkg/services/object/put/distributed.go @@ -48,6 +48,9 @@ type traversal struct { // need of additional broadcast after the object is saved extraBroadcastEnabled bool + // mtx protects mExclude map. + mtx sync.RWMutex + // container nodes which was processed during the primary object placement mExclude map[string]struct{} } @@ -71,17 +74,23 @@ func (x *traversal) submitPrimaryPlacementFinish() bool { // marks the container node as processed during the primary object placement. func (x *traversal) submitProcessed(n placement.Node) { if x.extraBroadcastEnabled { + key := string(n.PublicKey()) + + x.mtx.Lock() if x.mExclude == nil { x.mExclude = make(map[string]struct{}, 1) } - x.mExclude[string(n.PublicKey())] = struct{}{} + x.mExclude[key] = struct{}{} + x.mtx.Unlock() } } // checks if specified node was processed during the primary object placement. -func (x traversal) processed(n placement.Node) bool { +func (x *traversal) processed(n placement.Node) bool { + x.mtx.RLock() _, ok := x.mExclude[string(n.PublicKey())] + x.mtx.RUnlock() return ok }