2021-07-28 13:28:05 +00:00
|
|
|
package layer
|
|
|
|
|
|
|
|
import (
|
2021-07-29 08:14:14 +00:00
|
|
|
"context"
|
2021-07-28 13:28:05 +00:00
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id"
|
|
|
|
)
|
|
|
|
|
|
|
|
/*
|
2021-07-29 08:14:14 +00:00
|
|
|
This is an implementation of a cache for ListObjectsV2/V1 which we can return to users when we receive a ListObjects
|
|
|
|
request.
|
2021-07-28 13:28:05 +00:00
|
|
|
|
2021-07-29 08:14:14 +00:00
|
|
|
The cache is a map which has a key: cacheOptions struct and a value: list of objects. After putting a record we
|
|
|
|
start a timer (via time.AfterFunc) that removes the record after defaultCacheLifetime value.
|
2021-07-28 13:28:05 +00:00
|
|
|
|
2021-07-29 08:14:14 +00:00
|
|
|
When we get a request from the user we just try to find the suitable and non-expired cache and then we return
|
|
|
|
the list of objects. Otherwise we send the request to NeoFS.
|
2021-07-28 13:28:05 +00:00
|
|
|
*/
|
|
|
|
|
2021-07-29 08:14:14 +00:00
|
|
|
// ObjectsListCache provides interface for cache of ListObjectsV2 in a layer struct.
|
2021-07-28 13:28:05 +00:00
|
|
|
type (
|
2021-07-29 08:14:14 +00:00
|
|
|
ObjectsListCache interface {
|
|
|
|
Get(key cacheOptions) []*ObjectInfo
|
|
|
|
Put(key cacheOptions, objects []*ObjectInfo)
|
2021-07-28 13:28:05 +00:00
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2021-07-29 08:14:14 +00:00
|
|
|
const defaultCacheLifetime = time.Second * 60
|
2021-07-28 13:28:05 +00:00
|
|
|
|
|
|
|
type (
|
|
|
|
listObjectsCache struct {
|
2021-07-29 08:14:14 +00:00
|
|
|
cacheLifetime time.Duration
|
|
|
|
caches map[cacheOptions]cache
|
|
|
|
mtx sync.RWMutex
|
2021-07-28 13:28:05 +00:00
|
|
|
}
|
|
|
|
cache struct {
|
|
|
|
list []*ObjectInfo
|
|
|
|
}
|
2021-07-29 08:14:14 +00:00
|
|
|
cacheOptions struct {
|
|
|
|
key string
|
|
|
|
delimiter string
|
|
|
|
prefix string
|
|
|
|
}
|
2021-07-28 13:28:05 +00:00
|
|
|
)
|
|
|
|
|
2021-07-29 08:14:14 +00:00
|
|
|
func newListObjectsCache(lifetime time.Duration) *listObjectsCache {
|
2021-07-28 13:28:05 +00:00
|
|
|
return &listObjectsCache{
|
2021-07-29 08:14:14 +00:00
|
|
|
caches: make(map[cacheOptions]cache),
|
|
|
|
cacheLifetime: lifetime,
|
2021-07-28 13:28:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-29 08:14:14 +00:00
|
|
|
func (l *listObjectsCache) Get(key cacheOptions) []*ObjectInfo {
|
2021-07-28 13:28:05 +00:00
|
|
|
l.mtx.RLock()
|
|
|
|
defer l.mtx.RUnlock()
|
|
|
|
if val, ok := l.caches[key]; ok {
|
2021-07-29 08:14:14 +00:00
|
|
|
return val.list
|
2021-07-28 13:28:05 +00:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-07-29 08:14:14 +00:00
|
|
|
func (l *listObjectsCache) Put(key cacheOptions, objects []*ObjectInfo) {
|
|
|
|
if len(objects) == 0 {
|
|
|
|
return
|
|
|
|
}
|
2021-07-28 13:28:05 +00:00
|
|
|
var c cache
|
|
|
|
l.mtx.Lock()
|
|
|
|
defer l.mtx.Unlock()
|
|
|
|
c.list = objects
|
|
|
|
l.caches[key] = c
|
2021-07-29 08:14:14 +00:00
|
|
|
time.AfterFunc(l.cacheLifetime, func() {
|
2021-07-28 13:28:05 +00:00
|
|
|
l.mtx.Lock()
|
|
|
|
delete(l.caches, key)
|
|
|
|
l.mtx.Unlock()
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-07-29 08:14:14 +00:00
|
|
|
func createKey(ctx context.Context, cid *cid.ID, prefix, delimiter string) (cacheOptions, error) {
|
|
|
|
box, err := GetBoxData(ctx)
|
|
|
|
if err != nil {
|
|
|
|
return cacheOptions{}, err
|
|
|
|
}
|
|
|
|
p := cacheOptions{
|
|
|
|
key: box.Gate.AccessKey + cid.String(),
|
|
|
|
delimiter: delimiter,
|
|
|
|
prefix: prefix,
|
|
|
|
}
|
|
|
|
|
|
|
|
return p, nil
|
2021-07-28 13:28:05 +00:00
|
|
|
}
|