From 951eb6fda8a4942d179658ac3dfa5e710b1a2c61 Mon Sep 17 00:00:00 2001 From: Denis Kirillov Date: Wed, 8 Sep 2021 10:08:56 +0300 Subject: [PATCH] [#253] Add access box cache Signed-off-by: Denis Kirillov --- api/auth/center.go | 4 +- authmate/authmate.go | 4 +- cmd/s3-gw/app.go | 89 +++++++++++++++++---------------- cmd/s3-gw/app_settings.go | 10 ++-- creds/tokens/accessbox_cache.go | 59 ++++++++++++++++++++++ creds/tokens/credentials.go | 34 +++++++------ docs/configuration.md | 12 +++-- 7 files changed, 144 insertions(+), 68 deletions(-) create mode 100644 creds/tokens/accessbox_cache.go diff --git a/api/auth/center.go b/api/auth/center.go index 78fc3d8a8..613c803ca 100644 --- a/api/auth/center.go +++ b/api/auth/center.go @@ -81,9 +81,9 @@ func (p prs) Seek(_ int64, _ int) (int64, error) { var _ io.ReadSeeker = prs(0) // 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 ¢er{ - cli: tokens.New(conns, key), + cli: tokens.New(conns, key, config), reg: ®expSubmatcher{re: authorizationFieldRegexp}, postReg: ®expSubmatcher{re: postPolicyCredentialRegexp}, } diff --git a/authmate/authmate.go b/authmate/authmate.go index 2881ae0c8..e90fa599d 100644 --- a/authmate/authmate.go +++ b/authmate/authmate.go @@ -226,7 +226,7 @@ func (a *Agent) IssueSecret(ctx context.Context, w io.Writer, options *IssueSecr } address, err := tokens. - New(a.pool, secrets.EphemeralKey). + New(a.pool, secrets.EphemeralKey, tokens.DefaultCacheConfig()). Put(ctx, cid, oid, box, lifetime.Exp, options.GatesPublicKeys...) if err != nil { 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 // writes to io.Writer the secret access key. 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() if err := address.Parse(options.SecretAddress); err != nil { return fmt.Errorf("failed to parse secret address: %w", err) diff --git a/cmd/s3-gw/app.go b/cmd/s3-gw/app.go index 2a432f5cd..71ba0aaeb 100644 --- a/cmd/s3-gw/app.go +++ b/cmd/s3-gw/app.go @@ -6,6 +6,7 @@ import ( "math" "net" "net/http" + "time" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "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/handler" "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-sdk-go/policy" "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) // prepare auth center - ctr = auth.New(conns, key) + ctr = auth.New(conns, key, getAccessBoxCacheConfig(v, l)) handlerOptions := getHandlerOptions(v, l) @@ -222,53 +224,54 @@ func getCacheOptions(v *viper.Viper, l *zap.Logger) *layer.CacheConfig { Lifetime: cache.DefaultObjectsCacheLifetime, } - if v.IsSet(cfgObjectsCacheLifetime) { - lifetime := v.GetDuration(cfgObjectsCacheLifetime) - 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 - } - } + cacheCfg.Lifetime = getLifetime(v, l, cfgObjectsCacheLifetime, cacheCfg.Lifetime) + cacheCfg.Size = getSize(v, l, cfgObjectsCacheSize, cacheCfg.Size) - if v.IsSet(cfgCacheSize) { - size := v.GetInt(cfgCacheSize) - 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 - } - } + cacheCfg.ListObjectsLifetime = getLifetime(v, l, cfgListObjectsCacheLifetime, cacheCfg.ListObjectsLifetime) + cacheCfg.ListObjectsSize = getSize(v, l, cfgListObjectsCacheSize, cacheCfg.ListObjectsSize) 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 { var ( cfg handler.Config diff --git a/cmd/s3-gw/app_settings.go b/cmd/s3-gw/app_settings.go index c6e5ea9ac..308b851c7 100644 --- a/cmd/s3-gw/app_settings.go +++ b/cmd/s3-gw/app_settings.go @@ -55,10 +55,12 @@ const ( // Settings. cfgRebalanceTimer = "rebalance_timer" // Caching. - cfgObjectsCacheLifetime = "cache.lifetime" - cfgCacheSize = "cache.size" - cfgListObjectsCacheLifetime = "cache.list_objects_lifetime" - cfgListObjectsCacheSize = "cache.list_objects_size" + cfgObjectsCacheLifetime = "cache.objects.lifetime" + cfgObjectsCacheSize = "cache.objects.size" + cfgListObjectsCacheLifetime = "cache.list.lifetime" + cfgListObjectsCacheSize = "cache.list.size" + cfgAccessBoxCacheLifetime = "cache.accessbox.lifetime" + cfgAccessBoxCacheSize = "cache.accessbox.size" // Policy. cfgDefaultPolicy = "default_policy" diff --git a/creds/tokens/accessbox_cache.go b/creds/tokens/accessbox_cache.go new file mode 100644 index 000000000..c7b2b2793 --- /dev/null +++ b/creds/tokens/accessbox_cache.go @@ -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) +} diff --git a/creds/tokens/credentials.go b/creds/tokens/credentials.go index 4a945d1f6..ce3ae364f 100644 --- a/creds/tokens/credentials.go +++ b/creds/tokens/credentials.go @@ -25,8 +25,9 @@ type ( } cred struct { - key *keys.PrivateKey - pool pool.Pool + key *keys.PrivateKey + pool pool.Pool + cache *AccessBoxCache } ) @@ -46,8 +47,8 @@ var bufferPool = sync.Pool{ var _ = New // New creates new Credentials instance using given cli and key. -func New(conns pool.Pool, key *keys.PrivateKey) Credentials { - return &cred{pool: conns, key: key} +func New(conns pool.Pool, key *keys.PrivateKey, config *CacheConfig) Credentials { + return &cred{pool: conns, key: key, cache: NewAccessBoxCache(config)} } func (c *cred) acquireBuffer() *bytes.Buffer { @@ -59,22 +60,27 @@ func (c *cred) releaseBuffer(buf *bytes.Buffer) { 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) { + cachedBox := c.cache.Get(address) + if cachedBox != nil { + return cachedBox, nil + } + box, err := c.getAccessBox(ctx, address) if err != nil { 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) { diff --git a/docs/configuration.md b/docs/configuration.md index 66f8f5a8c..fbd997d03 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -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.: ``` cache: - lifetime: 300s - size: 150 - list_objects_lifetime: 1m + objects: + lifetime: 300s + size: 150 + list: + lifetime: 1m + size: 100 + accessbox: + lifetime: 5m + size: 10 ``` If invalid values are set, the gateway will use default values instead.