diff --git a/cmd/neofs-node/cache.go b/cmd/neofs-node/cache.go index b50ba6277..be4c98d59 100644 --- a/cmd/neofs-node/cache.go +++ b/cmd/neofs-node/cache.go @@ -7,7 +7,9 @@ import ( lru "github.com/hashicorp/golang-lru" eaclSDK "github.com/nspcc-dev/neofs-api-go/pkg/acl/eacl" containerSDK "github.com/nspcc-dev/neofs-api-go/pkg/container" + netmapSDK "github.com/nspcc-dev/neofs-api-go/pkg/netmap" "github.com/nspcc-dev/neofs-node/pkg/core/container" + "github.com/nspcc-dev/neofs-node/pkg/core/netmap" "github.com/nspcc-dev/neofs-node/pkg/services/object/acl/eacl" ) @@ -78,6 +80,50 @@ func (c *ttlNetCache) get(key interface{}) (interface{}, error) { return val, nil } +// entity that provides LRU cache interface. +type lruNetCache struct { + mtx sync.Mutex + + cache *lru.Cache + + netRdr netValueReader +} + +// complicates netValueReader with LRU caching mechanism. +func newNetworkLRUCache(sz int, netRdr netValueReader) *lruNetCache { + cache, err := lru.New(sz) + fatalOnErr(err) + + return &lruNetCache{ + cache: cache, + netRdr: netRdr, + } +} + +// reads value by the key. +// +// updates the value from the network on cache miss. +// +// returned value should not be modified. +func (c *lruNetCache) get(key interface{}) (interface{}, error) { + c.mtx.Lock() + defer c.mtx.Unlock() + + val, ok := c.cache.Get(key) + if ok { + return val, nil + } + + val, err := c.netRdr(key) + if err != nil { + return nil, err + } + + c.cache.Add(key, val) + + return val, nil +} + // wrapper over TTL cache of values read from the network // that implements container storage. type ttlContainerStorage ttlNetCache @@ -141,3 +187,43 @@ func (s *ttlEACLStorage) GetEACL(cid *containerSDK.ID) (*eaclSDK.Table, error) { return val.(*eaclSDK.Table), nil } + +type lruNetmapSource struct { + netState netmap.State + + cache *lruNetCache +} + +func newCachedNetmapStorage(s netmap.State, v netmap.Source) netmap.Source { + const netmapCacheSize = 10 + + lruNetmapCache := newNetworkLRUCache(netmapCacheSize, func(key interface{}) (interface{}, error) { + return v.GetNetMapByEpoch(key.(uint64)) + }) + + return &lruNetmapSource{ + netState: s, + cache: lruNetmapCache, + } +} + +func (s *lruNetmapSource) GetNetMap(diff uint64) (*netmapSDK.Netmap, error) { + return s.getNetMapByEpoch(s.netState.CurrentEpoch() - diff) +} + +func (s *lruNetmapSource) GetNetMapByEpoch(epoch uint64) (*netmapSDK.Netmap, error) { + return s.getNetMapByEpoch(epoch) +} + +func (s *lruNetmapSource) getNetMapByEpoch(epoch uint64) (*netmapSDK.Netmap, error) { + val, err := s.cache.get(epoch) + if err != nil { + return nil, err + } + + return val.(*netmapSDK.Netmap), nil +} + +func (s *lruNetmapSource) Epoch() (uint64, error) { + return s.netState.CurrentEpoch(), nil +} diff --git a/cmd/neofs-node/morph.go b/cmd/neofs-node/morph.go index 68b58e767..df78733ba 100644 --- a/cmd/neofs-node/morph.go +++ b/cmd/neofs-node/morph.go @@ -65,7 +65,7 @@ func initMorphComponents(c *cfg) { wrap, err := wrapper.New(cli) fatalOnErr(err) - c.cfgObject.netMapStorage = wrap + c.cfgObject.netMapStorage = newCachedNetmapStorage(c.cfgNetmap.state, wrap) c.cfgNetmap.wrapper = wrap }