forked from TrueCloudLab/frostfs-node
[#806] morph: Remove container list cache
Signed-off-by: Dmitrii Stepanov <d.stepanov@yadro.com>
This commit is contained in:
parent
02450a9a16
commit
12ca452638
2 changed files with 1 additions and 125 deletions
|
@ -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]
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in a new issue