[#253] Add access box cache

Signed-off-by: Denis Kirillov <denis@nspcc.ru>
This commit is contained in:
Denis Kirillov 2021-09-08 10:08:56 +03:00 committed by Kirillov Denis
parent 7ad81c4d09
commit 951eb6fda8
7 changed files with 144 additions and 68 deletions

View file

@ -81,9 +81,9 @@ func (p prs) Seek(_ int64, _ int) (int64, error) {
var _ io.ReadSeeker = prs(0) var _ io.ReadSeeker = prs(0)
// New creates an instance of AuthCenter. // New creates an instance of AuthCenter.
func New(conns pool.Pool, key *keys.PrivateKey) Center { func New(conns pool.Pool, key *keys.PrivateKey, config *tokens.CacheConfig) Center {
return &center{ return &center{
cli: tokens.New(conns, key), cli: tokens.New(conns, key, config),
reg: &regexpSubmatcher{re: authorizationFieldRegexp}, reg: &regexpSubmatcher{re: authorizationFieldRegexp},
postReg: &regexpSubmatcher{re: postPolicyCredentialRegexp}, postReg: &regexpSubmatcher{re: postPolicyCredentialRegexp},
} }

View file

@ -226,7 +226,7 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
} }
address, err := tokens. address, err := tokens.
New(a.pool, secrets.EphemeralKey). New(a.pool, secrets.EphemeralKey, tokens.DefaultCacheConfig()).
Put(ctx, cid, oid, box, lifetime.Exp, options.GatesPublicKeys...) Put(ctx, cid, oid, box, lifetime.Exp, options.GatesPublicKeys...)
if err != nil { if err != nil {
return fmt.Errorf("failed to put bearer token: %w", err) return fmt.Errorf("failed to put bearer token: %w", err)
@ -268,7 +268,7 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr
// ObtainSecret receives an existing secret access key from NeoFS and // ObtainSecret receives an existing secret access key from NeoFS and
// writes to io.Writer the secret access key. // writes to io.Writer the secret access key.
func (a *Agent) ObtainSecret(ctx context.Context, w io.Writer, options *ObtainSecretOptions) error { func (a *Agent) ObtainSecret(ctx context.Context, w io.Writer, options *ObtainSecretOptions) error {
bearerCreds := tokens.New(a.pool, options.GatePrivateKey) bearerCreds := tokens.New(a.pool, options.GatePrivateKey, tokens.DefaultCacheConfig())
address := object.NewAddress() address := object.NewAddress()
if err := address.Parse(options.SecretAddress); err != nil { if err := address.Parse(options.SecretAddress); err != nil {
return fmt.Errorf("failed to parse secret address: %w", err) return fmt.Errorf("failed to parse secret address: %w", err)

View file

@ -6,6 +6,7 @@ import (
"math" "math"
"net" "net"
"net/http" "net/http"
"time"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neofs-s3-gw/api" "github.com/nspcc-dev/neofs-s3-gw/api"
@ -13,6 +14,7 @@ import (
"github.com/nspcc-dev/neofs-s3-gw/api/cache" "github.com/nspcc-dev/neofs-s3-gw/api/cache"
"github.com/nspcc-dev/neofs-s3-gw/api/handler" "github.com/nspcc-dev/neofs-s3-gw/api/handler"
"github.com/nspcc-dev/neofs-s3-gw/api/layer" "github.com/nspcc-dev/neofs-s3-gw/api/layer"
"github.com/nspcc-dev/neofs-s3-gw/creds/tokens"
"github.com/nspcc-dev/neofs-s3-gw/internal/wallet" "github.com/nspcc-dev/neofs-s3-gw/internal/wallet"
"github.com/nspcc-dev/neofs-sdk-go/policy" "github.com/nspcc-dev/neofs-sdk-go/policy"
"github.com/nspcc-dev/neofs-sdk-go/pool" "github.com/nspcc-dev/neofs-sdk-go/pool"
@ -116,7 +118,7 @@ func newApp(ctx context.Context, l *zap.Logger, v *viper.Viper) *App {
obj = layer.NewLayer(l, conns, cacheCfg) obj = layer.NewLayer(l, conns, cacheCfg)
// prepare auth center // prepare auth center
ctr = auth.New(conns, key) ctr = auth.New(conns, key, getAccessBoxCacheConfig(v, l))
handlerOptions := getHandlerOptions(v, l) handlerOptions := getHandlerOptions(v, l)
@ -222,53 +224,54 @@ func getCacheOptions(v *viper.Viper, l *zap.Logger) *layer.CacheConfig {
Lifetime: cache.DefaultObjectsCacheLifetime, Lifetime: cache.DefaultObjectsCacheLifetime,
} }
if v.IsSet(cfgObjectsCacheLifetime) { cacheCfg.Lifetime = getLifetime(v, l, cfgObjectsCacheLifetime, cacheCfg.Lifetime)
lifetime := v.GetDuration(cfgObjectsCacheLifetime) cacheCfg.Size = getSize(v, l, cfgObjectsCacheSize, cacheCfg.Size)
if lifetime <= 0 {
l.Error("invalid cache lifetime, using default value (in seconds)",
zap.Duration("value in config", lifetime),
zap.Duration("default", cacheCfg.Lifetime))
} else {
cacheCfg.Lifetime = lifetime
}
}
if v.IsSet(cfgCacheSize) { cacheCfg.ListObjectsLifetime = getLifetime(v, l, cfgListObjectsCacheLifetime, cacheCfg.ListObjectsLifetime)
size := v.GetInt(cfgCacheSize) cacheCfg.ListObjectsSize = getSize(v, l, cfgListObjectsCacheSize, cacheCfg.ListObjectsSize)
if size <= 0 {
l.Error("invalid cache size, using default value",
zap.Int("value in config", size),
zap.Int("default", cacheCfg.Size))
} else {
cacheCfg.Size = size
}
}
if v.IsSet(cfgListObjectsCacheLifetime) {
lifetime := v.GetDuration(cfgListObjectsCacheLifetime)
if lifetime <= 0 {
l.Error("invalid list objects cache lifetime, using default value (in seconds)",
zap.Duration("value in config", lifetime),
zap.Duration("default", cacheCfg.ListObjectsLifetime))
} else {
cacheCfg.ListObjectsLifetime = lifetime
}
}
if v.IsSet(cfgListObjectsCacheSize) {
size := v.GetInt(cfgListObjectsCacheSize)
if size <= 0 {
l.Error("invalid cache size, using default value",
zap.Int("value in config", size),
zap.Int("default", cacheCfg.ListObjectsSize))
} else {
cacheCfg.Size = size
}
}
return &cacheCfg return &cacheCfg
} }
func getLifetime(v *viper.Viper, l *zap.Logger, cfgEntry string, defaultValue time.Duration) time.Duration {
if v.IsSet(cfgEntry) {
lifetime := v.GetDuration(cfgEntry)
if lifetime <= 0 {
l.Error("invalid lifetime, using default value (in seconds)",
zap.String("parameter", cfgEntry),
zap.Duration("value in config", lifetime),
zap.Duration("default", defaultValue))
} else {
return lifetime
}
}
return defaultValue
}
func getSize(v *viper.Viper, l *zap.Logger, cfgEntry string, defaultValue int) int {
if v.IsSet(cfgEntry) {
size := v.GetInt(cfgEntry)
if size <= 0 {
l.Error("invalid cache size, using default value",
zap.String("parameter", cfgEntry),
zap.Int("value in config", size),
zap.Int("default", defaultValue))
} else {
return size
}
}
return defaultValue
}
func getAccessBoxCacheConfig(v *viper.Viper, l *zap.Logger) *tokens.CacheConfig {
cacheCfg := tokens.DefaultCacheConfig()
cacheCfg.Lifetime = getLifetime(v, l, cfgAccessBoxCacheLifetime, cacheCfg.Lifetime)
cacheCfg.Size = getSize(v, l, cfgAccessBoxCacheSize, cacheCfg.Size)
return cacheCfg
}
func getHandlerOptions(v *viper.Viper, l *zap.Logger) *handler.Config { func getHandlerOptions(v *viper.Viper, l *zap.Logger) *handler.Config {
var ( var (
cfg handler.Config cfg handler.Config

View file

@ -55,10 +55,12 @@ const ( // Settings.
cfgRebalanceTimer = "rebalance_timer" cfgRebalanceTimer = "rebalance_timer"
// Caching. // Caching.
cfgObjectsCacheLifetime = "cache.lifetime" cfgObjectsCacheLifetime = "cache.objects.lifetime"
cfgCacheSize = "cache.size" cfgObjectsCacheSize = "cache.objects.size"
cfgListObjectsCacheLifetime = "cache.list_objects_lifetime" cfgListObjectsCacheLifetime = "cache.list.lifetime"
cfgListObjectsCacheSize = "cache.list_objects_size" cfgListObjectsCacheSize = "cache.list.size"
cfgAccessBoxCacheLifetime = "cache.accessbox.lifetime"
cfgAccessBoxCacheSize = "cache.accessbox.size"
// Policy. // Policy.
cfgDefaultPolicy = "default_policy" cfgDefaultPolicy = "default_policy"

View file

@ -0,0 +1,59 @@
package tokens
import (
"time"
"github.com/bluele/gcache"
"github.com/nspcc-dev/neofs-api-go/pkg/object"
"github.com/nspcc-dev/neofs-s3-gw/creds/accessbox"
)
const (
// DefaultCacheSize is a default maximum number of entries in cache.
DefaultCacheSize = 100
// DefaultCacheLifetime is a default lifetime of entries in cache.
DefaultCacheLifetime = 10 * time.Minute
)
// CacheConfig stores expiration params for cache.
type CacheConfig struct {
Size int
Lifetime time.Duration
}
// DefaultCacheConfig return new default cache expiration values.
func DefaultCacheConfig() *CacheConfig {
return &CacheConfig{Size: DefaultCacheSize, Lifetime: DefaultCacheLifetime}
}
// AccessBoxCache stores access box by its address.
type AccessBoxCache struct {
cache gcache.Cache
}
// NewAccessBoxCache creates an object of BucketCache.
func NewAccessBoxCache(config *CacheConfig) *AccessBoxCache {
gc := gcache.New(config.Size).LRU().Expiration(config.Lifetime).Build()
return &AccessBoxCache{cache: gc}
}
// Get returns cached object.
func (o *AccessBoxCache) Get(address *object.Address) *accessbox.Box {
entry, err := o.cache.Get(address.String())
if err != nil {
return nil
}
result, ok := entry.(*accessbox.Box)
if !ok {
return nil
}
return result
}
// Put stores an object to cache.
func (o *AccessBoxCache) Put(address *object.Address, box *accessbox.Box) error {
return o.cache.Set(address.String(), box)
}

View file

@ -27,6 +27,7 @@ type (
cred struct { cred struct {
key *keys.PrivateKey key *keys.PrivateKey
pool pool.Pool pool pool.Pool
cache *AccessBoxCache
} }
) )
@ -46,8 +47,8 @@ var bufferPool = sync.Pool{
var _ = New var _ = New
// New creates new Credentials instance using given cli and key. // New creates new Credentials instance using given cli and key.
func New(conns pool.Pool, key *keys.PrivateKey) Credentials { func New(conns pool.Pool, key *keys.PrivateKey, config *CacheConfig) Credentials {
return &cred{pool: conns, key: key} return &cred{pool: conns, key: key, cache: NewAccessBoxCache(config)}
} }
func (c *cred) acquireBuffer() *bytes.Buffer { func (c *cred) acquireBuffer() *bytes.Buffer {
@ -59,22 +60,27 @@ func (c *cred) releaseBuffer(buf *bytes.Buffer) {
bufferPool.Put(buf) bufferPool.Put(buf)
} }
func (c *cred) GetTokens(ctx context.Context, address *object.Address) (*accessbox.GateData, error) {
box, err := c.getAccessBox(ctx, address)
if err != nil {
return nil, err
}
return box.GetTokens(c.key)
}
func (c *cred) GetBox(ctx context.Context, address *object.Address) (*accessbox.Box, error) { func (c *cred) GetBox(ctx context.Context, address *object.Address) (*accessbox.Box, error) {
cachedBox := c.cache.Get(address)
if cachedBox != nil {
return cachedBox, nil
}
box, err := c.getAccessBox(ctx, address) box, err := c.getAccessBox(ctx, address)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return box.GetBox(c.key) cachedBox, err = box.GetBox(c.key)
if err != nil {
return nil, err
}
if err = c.cache.Put(address, cachedBox); err != nil {
return nil, err
}
return cachedBox, nil
} }
func (c *cred) getAccessBox(ctx context.Context, address *object.Address) (*accessbox.AccessBox, error) { func (c *cred) getAccessBox(ctx context.Context, address *object.Address) (*accessbox.AccessBox, error) {

View file

@ -108,8 +108,14 @@ If the value is not set at all it will be set as `REP 3`.
Parameters for caches in s3-gw can be specified in a .yaml config file. E.g.: Parameters for caches in s3-gw can be specified in a .yaml config file. E.g.:
``` ```
cache: cache:
objects:
lifetime: 300s lifetime: 300s
size: 150 size: 150
list_objects_lifetime: 1m list:
lifetime: 1m
size: 100
accessbox:
lifetime: 5m
size: 10
``` ```
If invalid values are set, the gateway will use default values instead. If invalid values are set, the gateway will use default values instead.