diff --git a/cmd/frostfs-node/cache.go b/cmd/frostfs-node/cache.go index 6a5d5d182..cd383a5c2 100644 --- a/cmd/frostfs-node/cache.go +++ b/cmd/frostfs-node/cache.go @@ -6,13 +6,11 @@ import ( "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/container" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/core/netmap" - cntClient "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/morph/client/container" putsvc "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/object/put" utilSync "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/sync" apistatus "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client/status" cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id" netmapSDK "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/netmap" - "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/user" lru "github.com/hashicorp/golang-lru/v2" ) @@ -244,117 +242,6 @@ func (s *lruNetmapSource) Epoch() (uint64, error) { return s.netState.CurrentEpoch(), nil } -// wrapper over TTL cache of values read from the network -// that implements container lister. -type ttlContainerLister struct { - inner *ttlNetCache[string, *cacheItemContainerList] - client *cntClient.Client -} - -// value type for ttlNetCache used by ttlContainerLister. -type cacheItemContainerList struct { - // protects list from concurrent add/remove ops - mtx sync.RWMutex - // actual list of containers owner by the particular user - list []cid.ID -} - -func newCachedContainerLister(c *cntClient.Client, ttl time.Duration) ttlContainerLister { - const containerListerCacheSize = 100 - - lruCnrListerCache := newNetworkTTLCache(containerListerCacheSize, ttl, func(strID string) (*cacheItemContainerList, error) { - var id *user.ID - - if strID != "" { - id = new(user.ID) - - err := id.DecodeString(strID) - if err != nil { - return nil, err - } - } - - list, err := c.ContainersOf(id) - if err != nil { - return nil, err - } - - return &cacheItemContainerList{ - list: list, - }, nil - }) - - return ttlContainerLister{inner: lruCnrListerCache, client: c} -} - -// List returns list of container IDs from the cache. If list is missing in the -// cache or expired, then it returns container IDs from side chain and updates -// the cache. -func (s ttlContainerLister) List(id *user.ID) ([]cid.ID, error) { - if id == nil { - return s.client.ContainersOf(nil) - } - - item, err := s.inner.get(id.EncodeToString()) - if err != nil { - return nil, err - } - - item.mtx.RLock() - res := make([]cid.ID, len(item.list)) - copy(res, item.list) - item.mtx.RUnlock() - - return res, nil -} - -// updates cached list of owner's containers: cnr is added if flag is true, otherwise it's removed. -// Concurrent calls can lead to some races: -// - two parallel additions to missing owner's cache can lead to only one container to be cached -// - async cache value eviction can lead to idle addition -// -// All described race cases aren't critical since cache values expire anyway, we just try -// to increase cache actuality w/o huge overhead on synchronization. -func (s *ttlContainerLister) update(owner user.ID, cnr cid.ID, add bool) { - strOwner := owner.EncodeToString() - - val, ok := s.inner.cache.Peek(strOwner) - if !ok { - // we could cache the single cnr but in this case we will disperse - // with the Sidechain a lot - return - } - - if s.inner.ttl <= time.Since(val.t) { - return - } - - item := val.v - - item.mtx.Lock() - { - found := false - - for i := range item.list { - if found = item.list[i].Equals(cnr); found { - if !add { - item.list = append(item.list[:i], item.list[i+1:]...) - // if list became empty we don't remove the value from the cache - // since empty list is a correct value, and we don't want to insta - // re-request it from the Sidechain - } - - break - } - } - - if add && !found { - item.list = append(item.list, cnr) - } - } - item.mtx.Unlock() -} - type cachedIRFetcher struct { *ttlNetCache[struct{}, [][]byte] } diff --git a/cmd/frostfs-node/container.go b/cmd/frostfs-node/container.go index 9239df640..5550af013 100644 --- a/cmd/frostfs-node/container.go +++ b/cmd/frostfs-node/container.go @@ -63,7 +63,6 @@ func configureEACLAndContainerSources(c *cfg, client *cntClient.Client, cnrSrc c // use RPC node as source of Container contract items (with caching) cachedContainerStorage := newCachedContainerStorage(cnrSrc, c.cfgMorph.cacheTTL) cachedEACLStorage := newCachedEACLStorage(eACLFetcher, c.cfgMorph.cacheTTL) - cachedContainerLister := newCachedContainerLister(client, c.cfgMorph.cacheTTL) subscribeToContainerCreation(c, func(e event.Event) { ev := e.(containerEvent.PutSuccess) @@ -74,7 +73,6 @@ func configureEACLAndContainerSources(c *cfg, client *cntClient.Client, cnrSrc c // creation success are most commonly tracked by polling GET op. cnr, err := cnrSrc.Get(ev.ID) if err == nil { - cachedContainerLister.update(cnr.Value.Owner(), ev.ID, true) cachedContainerStorage.containerCache.set(ev.ID, cnr, nil) } else { // unlike removal, we expect successful receive of the container @@ -93,15 +91,6 @@ func configureEACLAndContainerSources(c *cfg, client *cntClient.Client, cnrSrc c subscribeToContainerRemoval(c, func(e event.Event) { ev := e.(containerEvent.DeleteSuccess) - // read owner of the removed container in order to update the listing cache. - // It's strange to read already removed container, but we can successfully hit - // the cache. - // TODO: use owner directly from the event after neofs-contract#256 will become resolved - cnr, err := cachedContainerStorage.Get(ev.ID) - if err == nil { - cachedContainerLister.update(cnr.Value.Owner(), ev.ID, false) - } - cachedContainerStorage.handleRemoval(ev.ID) c.log.Debug(logs.FrostFSNodeContainerRemovalEventsReceipt, zap.Stringer("id", ev.ID), @@ -111,7 +100,7 @@ func configureEACLAndContainerSources(c *cfg, client *cntClient.Client, cnrSrc c c.cfgObject.eaclSource = cachedEACLStorage c.cfgObject.cnrSource = cachedContainerStorage - cnrRdr.lister = cachedContainerLister + cnrRdr.lister = client cnrRdr.eacl = c.cfgObject.eaclSource cnrRdr.src = c.cfgObject.cnrSource