Files
kopia/repo/content/content_cache_data.go
Jarek Kowalski e03971fc59 Upgraded linter to v1.33.0 (#734)
* linter: upgraded to 1.33, disabled some linters

* lint: fixed 'errorlint' errors

This ensures that all error comparisons use errors.Is() or errors.As().
We will be wrapping more errors going forward so it's important that
error checks are not strict everywhere.

Verified that there are no exceptions for errorlint linter which
guarantees that.

* lint: fixed or suppressed wrapcheck errors

* lint: nolintlint and misc cleanups

Co-authored-by: Julio López <julio+gh@kasten.io>
2020-12-21 22:39:22 -08:00

116 lines
2.9 KiB
Go

package content
import (
"context"
"github.com/pkg/errors"
"go.opencensus.io/stats"
"github.com/kopia/kopia/internal/gather"
"github.com/kopia/kopia/internal/hmac"
"github.com/kopia/kopia/repo/blob"
)
type contentCacheForData struct {
*cacheBase
st blob.Storage
hmacSecret []byte
}
func adjustCacheKey(cacheKey cacheKey) cacheKey {
// content IDs with odd length have a single-byte prefix.
// move the prefix to the end of cache key to make sure the top level shard is spread 256 ways.
if len(cacheKey)%2 == 1 {
return cacheKey[1:] + cacheKey[0:1]
}
return cacheKey
}
func (c *contentCacheForData) getContent(ctx context.Context, cacheKey cacheKey, blobID blob.ID, offset, length int64) ([]byte, error) {
cacheKey = adjustCacheKey(cacheKey)
useCache := shouldUseContentCache(ctx)
if useCache {
if b := c.readAndVerifyCacheContent(ctx, cacheKey); b != nil {
stats.Record(ctx,
metricContentCacheHitCount.M(1),
metricContentCacheHitBytes.M(int64(len(b))),
)
return b, nil
}
}
stats.Record(ctx, metricContentCacheMissCount.M(1))
b, err := c.st.GetBlob(ctx, blobID, offset, length)
if err != nil {
stats.Record(ctx, metricContentCacheMissErrors.M(1))
} else {
stats.Record(ctx, metricContentCacheMissBytes.M(int64(len(b))))
}
if errors.Is(err, blob.ErrBlobNotFound) {
// not found in underlying storage
// nolint:wrapcheck
return nil, err
}
if err == nil && useCache {
// do not report cache writes as uploads.
if puterr := c.cacheStorage.PutBlob(
blob.WithUploadProgressCallback(ctx, nil),
blob.ID(cacheKey),
gather.FromSlice(hmac.Append(b, c.hmacSecret)),
); puterr != nil {
stats.Record(ctx, metricContentCacheStoreErrors.M(1))
log(ctx).Warningf("unable to write cache item %v: %v", cacheKey, puterr)
}
}
return b, errors.Wrap(err, "error getting content from cache")
}
func (c *contentCacheForData) readAndVerifyCacheContent(ctx context.Context, cacheKey cacheKey) []byte {
b, err := c.cacheStorage.GetBlob(ctx, blob.ID(cacheKey), 0, -1)
if err == nil {
b, err = hmac.VerifyAndStrip(b, c.hmacSecret)
if err == nil {
c.touch(ctx, blob.ID(cacheKey))
// retrieved from cache and HMAC valid
return b
}
// ignore malformed contents
log(ctx).Warningf("malformed content %v: %v", cacheKey, err)
return nil
}
if !errors.Is(err, blob.ErrBlobNotFound) {
log(ctx).Warningf("unable to read cache %v: %v", cacheKey, err)
}
return nil
}
func newContentCacheForData(ctx context.Context, st, cacheStorage blob.Storage, maxSizeBytes int64, hmacSecret []byte) (contentCache, error) {
if cacheStorage == nil {
return passthroughContentCache{st}, nil
}
cb, err := newContentCacheBase(ctx, cacheStorage, maxSizeBytes, defaultTouchThreshold, defaultSweepFrequency)
if err != nil {
return nil, errors.Wrap(err, "unable to create base cache")
}
return &contentCacheForData{
st: st,
hmacSecret: append([]byte(nil), hmacSecret...),
cacheBase: cb,
}, nil
}