Skip to content

Commit

Permalink
[nspcc-dev#112] api: Add cache for ListObjectsV2
Browse files Browse the repository at this point in the history
Signed-off-by: Angira Kekteeva <kira@nspcc.ru>
  • Loading branch information
masterSplinter01 committed Jul 26, 2021
1 parent f975dbb commit 59f3589
Showing 1 changed file with 111 additions and 0 deletions.
111 changes: 111 additions & 0 deletions api/layer/object_cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package layer

import (
"sync"
"time"

cid "github.com/nspcc-dev/neofs-api-go/pkg/container/id"
)

/*
This is an implementation of a cache for ListObjectsV2 which we return to users by ContinuationToken.
The cache consists of a map with data and a timer.
The map has a key: (access_key from AccessBox) + container_id and a value: list of objects with creation time.
The timer every timerDefaultTickerTime checks the map with caches and removes the expired caches.
NB: Because the timer checks all caches at one time the caches can live longer than timerDefaultTickerTime.
E.g. if timerDefaultTickerTime is 1 minute and check happened when some cache is alive for 50 seconds, this cache
will not be cleaned, it will be cleaned next time => this cache will live for almost 2 minutes.
That's why IRL the caches will live timerDefaultTickerTime * 2.
ContinuationToken in our gateway is an objectID in NeoFS.
We don't keep ContinuationToken in this structure because we assume that users who received the token can reconnect
to other gateways and they should be able to get a list of objects.
When we receive the token from the user we just try to find the cache and then we return the list of objects which
starts from this token (i.e. objectID).
*/

// ObjectsListV2Cache provides interface for cache of ListObjectsV2 in a layer struct.
type ObjectsListV2Cache interface {
Timer
GetCache(token string, key string) []*ObjectInfo
PutCache(key string, objects []*ObjectInfo)
}

var (
timerDefaultTickerTime = time.Second * 60
ticker = time.NewTicker(timerDefaultTickerTime)
)

type (
listObjectsCache struct {
caches map[string]cache
quit chan struct{}
mtx sync.RWMutex
}
cache struct {
list []*ObjectInfo
created time.Time
}
)

func (l *listObjectsCache) GetCache(token, key string) []*ObjectInfo {
if val, ok := l.caches[key]; ok {
for i, obj := range val.list {
if obj.ID().String() == token {
return val.list[i:]
}
}
}

return nil
}

func (l *listObjectsCache) PutCache(key string, objects []*ObjectInfo) {
var (
c cache
)
l.mtx.Lock()
defer l.mtx.Unlock()
c.list = objects
c.created = time.Now().UTC()
l.caches[key] = c
}

func (l *listObjectsCache) StartTimer() {
for {
select {
case <-ticker.C:
cur := time.Now()
for key, obj := range l.caches {
if cur.Sub(obj.created) >= time.Minute {
l.mtx.Lock()
delete(l.caches, key)
l.mtx.Unlock()
}
}
case <-l.quit:
ticker.Stop()
return
}
}
}

func (l *listObjectsCache) StopTimer() {
l.quit <- struct{}{}
}

func createKey(accessKey *string, cid *cid.ID) string {
return *accessKey + cid.String()
}

func newListObjectsCache() *listObjectsCache {
c := listObjectsCache{}

c.quit = make(chan struct{})
c.caches = make(map[string]cache)

return &c
}

0 comments on commit 59f3589

Please sign in to comment.