frostfs-s3-gw/api/cache/objectslist.go

138 lines
3.9 KiB
Go
Raw Normal View History

package cache
import (
"fmt"
"strconv"
"strings"
"time"
"github.com/bluele/gcache"
"github.com/nspcc-dev/neofs-s3-gw/api/data"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"go.uber.org/zap"
)
/*
This is an implementation of cache which keeps unsorted lists of objects' IDs (all versions)
for a specified bucket and a prefix.
The cache contains gcache whose entries have a key: ObjectsListKey struct and a value: list of ids.
After putting a record, it lives for a while (default value is 60 seconds).
When we receive a request from a user, we try to find the suitable and non-expired cache entry, go through the list
and get ObjectInfos from common object cache or with a request to NeoFS.
When we put an object into a container, we invalidate entries with prefixes that are prefixes of the object's name.
*/
type (
// ObjectsListCache contains cache for ListObjects and ListObjectVersions.
ObjectsListCache struct {
cache gcache.Cache
logger *zap.Logger
}
// ObjectsListKey is a key to find a ObjectsListCache's entry.
ObjectsListKey struct {
cid cid.ID
prefix string
latestOnly bool
}
)
const (
// DefaultObjectsListCacheLifetime is a default lifetime of entries in cache of ListObjects.
DefaultObjectsListCacheLifetime = time.Second * 60
// DefaultObjectsListCacheSize is a default size of cache of ListObjects.
DefaultObjectsListCacheSize = 1e5
)
// DefaultObjectsListConfig returns new default cache expiration values.
func DefaultObjectsListConfig(logger *zap.Logger) *Config {
return &Config{
Size: DefaultObjectsListCacheSize,
Lifetime: DefaultObjectsListCacheLifetime,
Logger: logger,
}
}
func (k *ObjectsListKey) String() string {
return k.cid.EncodeToString() + k.prefix + strconv.FormatBool(k.latestOnly)
}
// NewObjectsListCache is a constructor which creates an object of ListObjectsCache with the given lifetime of entries.
func NewObjectsListCache(config *Config) *ObjectsListCache {
gc := gcache.New(config.Size).LRU().Expiration(config.Lifetime).Build()
return &ObjectsListCache{cache: gc, logger: config.Logger}
}
// Get returns a list of ObjectInfo.
func (l *ObjectsListCache) Get(key ObjectsListKey) []oid.ID {
entry, err := l.cache.Get(key)
if err != nil {
return nil
}
result, ok := entry.([]oid.ID)
if !ok {
l.logger.Warn("invalid cache entry type", zap.String("actual", fmt.Sprintf("%T", entry)),
zap.String("expected", fmt.Sprintf("%T", result)))
return nil
}
return result
}
// GetVersions returns a list of ObjectInfo.
func (l *ObjectsListCache) GetVersions(key ObjectsListKey) []*data.NodeVersion {
entry, err := l.cache.Get(key)
if err != nil {
return nil
}
result, ok := entry.([]*data.NodeVersion)
if !ok {
return nil
}
return result
}
// Put puts a list of objects to cache.
func (l *ObjectsListCache) Put(key ObjectsListKey, oids []oid.ID) error {
return l.cache.Set(key, oids)
}
// PutVersions puts a list of object versions to cache.
func (l *ObjectsListCache) PutVersions(key ObjectsListKey, versions []*data.NodeVersion) error {
return l.cache.Set(key, versions)
}
// CleanCacheEntriesContainingObject deletes entries containing specified object.
func (l *ObjectsListCache) CleanCacheEntriesContainingObject(objectName string, cnr cid.ID) {
keys := l.cache.Keys(true)
for _, key := range keys {
k, ok := key.(ObjectsListKey)
if !ok {
l.logger.Warn("invalid cache key type", zap.String("actual", fmt.Sprintf("%T", key)),
zap.String("expected", fmt.Sprintf("%T", k)))
continue
}
if cnr.Equals(k.cid) && strings.HasPrefix(objectName, k.prefix) {
l.cache.Remove(k)
}
}
}
// CreateObjectsListCacheKey returns ObjectsListKey with the given CID, prefix and latestOnly flag.
func CreateObjectsListCacheKey(cnr cid.ID, prefix string, latestOnly bool) ObjectsListKey {
p := ObjectsListKey{
cid: cnr,
prefix: prefix,
latestOnly: latestOnly,
}
return p
}