forked from TrueCloudLab/frostfs-node
104 lines
2 KiB
Go
104 lines
2 KiB
Go
|
package container
|
||
|
|
||
|
import (
|
||
|
"sync"
|
||
|
|
||
|
utilSync "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/util/sync"
|
||
|
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/client"
|
||
|
cid "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/container/id"
|
||
|
)
|
||
|
|
||
|
type Info struct {
|
||
|
Indexed bool
|
||
|
Removed bool
|
||
|
}
|
||
|
|
||
|
type infoValue struct {
|
||
|
info Info
|
||
|
err error
|
||
|
}
|
||
|
|
||
|
type InfoProvider interface {
|
||
|
Info(id cid.ID) (Info, error)
|
||
|
}
|
||
|
|
||
|
type infoProvider struct {
|
||
|
mtx *sync.RWMutex
|
||
|
cache map[cid.ID]infoValue
|
||
|
kl *utilSync.KeyLocker[cid.ID]
|
||
|
|
||
|
source Source
|
||
|
sourceErr error
|
||
|
sourceOnce *sync.Once
|
||
|
sourceFactory func() (Source, error)
|
||
|
}
|
||
|
|
||
|
func NewInfoProvider(sourceFactory func() (Source, error)) InfoProvider {
|
||
|
return &infoProvider{
|
||
|
mtx: &sync.RWMutex{},
|
||
|
cache: make(map[cid.ID]infoValue),
|
||
|
sourceOnce: &sync.Once{},
|
||
|
kl: utilSync.NewKeyLocker[cid.ID](),
|
||
|
sourceFactory: sourceFactory,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (r *infoProvider) Info(id cid.ID) (Info, error) {
|
||
|
v, found := r.tryGetFromCache(id)
|
||
|
if found {
|
||
|
return v.info, v.err
|
||
|
}
|
||
|
|
||
|
return r.getFromSource(id)
|
||
|
}
|
||
|
|
||
|
func (r *infoProvider) tryGetFromCache(id cid.ID) (infoValue, bool) {
|
||
|
r.mtx.RLock()
|
||
|
defer r.mtx.RUnlock()
|
||
|
|
||
|
value, found := r.cache[id]
|
||
|
return value, found
|
||
|
}
|
||
|
|
||
|
func (r *infoProvider) getFromSource(id cid.ID) (Info, error) {
|
||
|
r.kl.Lock(id)
|
||
|
defer r.kl.Unlock(id)
|
||
|
|
||
|
if v, ok := r.tryGetFromCache(id); ok {
|
||
|
return v.info, v.err
|
||
|
}
|
||
|
|
||
|
r.sourceOnce.Do(func() {
|
||
|
r.source, r.sourceErr = r.sourceFactory()
|
||
|
})
|
||
|
if r.sourceErr != nil {
|
||
|
return Info{}, r.sourceErr
|
||
|
}
|
||
|
|
||
|
cnr, err := r.source.Get(id)
|
||
|
var civ infoValue
|
||
|
if err != nil {
|
||
|
if client.IsErrContainerNotFound(err) {
|
||
|
removed, err := WasRemoved(r.source, id)
|
||
|
if err != nil {
|
||
|
civ.err = err
|
||
|
} else {
|
||
|
civ.info.Removed = removed
|
||
|
}
|
||
|
} else {
|
||
|
civ.err = err
|
||
|
}
|
||
|
} else {
|
||
|
civ.info.Indexed = IsIndexedContainer(cnr.Value)
|
||
|
}
|
||
|
r.putToCache(id, civ)
|
||
|
return civ.info, civ.err
|
||
|
}
|
||
|
|
||
|
func (r *infoProvider) putToCache(id cid.ID, ct infoValue) {
|
||
|
r.mtx.Lock()
|
||
|
defer r.mtx.Unlock()
|
||
|
|
||
|
r.cache[id] = ct
|
||
|
}
|