Files
kopia/internal/cache/content_cache_data.go
Jarek Kowalski c4ab086d5f chore(repository): streamlined internal content cache API (#1828)
moved from ./repo/content to ./internal/cache
2022-03-15 23:57:04 -07:00

95 lines
2.5 KiB
Go

package cache
import (
"context"
"github.com/pkg/errors"
"go.opencensus.io/stats"
"github.com/kopia/kopia/internal/gather"
"github.com/kopia/kopia/repo/blob"
)
type contentCacheForData struct {
pc *PersistentCache
st blob.Storage
}
// ContentIDCacheKey computes the cache key for the provided content ID.
func ContentIDCacheKey(contentID string) string {
// 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(contentID)%2 == 1 {
return contentID[1:] + contentID[0:1]
}
return contentID
}
// BlobIDCacheKey computes the cache key for the provided blob ID.
func BlobIDCacheKey(id blob.ID) string {
return string(id[1:] + id[0:1])
}
func (c *contentCacheForData) GetContent(ctx context.Context, contentID string, blobID blob.ID, offset, length int64, output *gather.WriteBuffer) error {
if c.pc.GetPartial(ctx, BlobIDCacheKey(blobID), offset, length, output) {
return nil
}
output.Reset()
// nolint:wrapcheck
return c.pc.GetOrLoad(ctx, ContentIDCacheKey(contentID), func(output *gather.WriteBuffer) error {
// nolint:wrapcheck
return c.st.GetBlob(ctx, blobID, offset, length, output)
}, output)
}
func (c *contentCacheForData) Close(ctx context.Context) {
c.pc.Close(ctx)
}
func (c contentCacheForData) PrefetchBlob(ctx context.Context, blobID blob.ID) error {
var blobData gather.WriteBuffer
defer blobData.Close()
if c.pc.GetPartial(ctx, BlobIDCacheKey(blobID), 0, 1, &blobData) {
return nil
}
// read the entire blob
if err := c.st.GetBlob(ctx, blobID, 0, -1, &blobData); err != nil {
stats.Record(ctx, MetricMissErrors.M(1))
// nolint:wrapcheck
return err
}
stats.Record(ctx, MetricMissBytes.M(int64(blobData.Length())))
// store the whole blob in the
c.pc.Put(ctx, BlobIDCacheKey(blobID), blobData.Bytes())
return nil
}
func (c contentCacheForData) CacheStorage() Storage {
return c.pc.cacheStorage
}
// NewContentCacheForData creates new content cache for data contents.
func NewContentCacheForData(ctx context.Context, st blob.Storage, cacheStorage Storage, sweep SweepSettings, hmacSecret []byte) (ContentCache, error) {
if cacheStorage == nil {
return passthroughContentCache{st}, nil
}
pc, err := NewPersistentCache(ctx, "content cache", cacheStorage, ChecksumProtection(hmacSecret), sweep)
if err != nil {
return nil, errors.Wrap(err, "unable to create base cache")
}
return &contentCacheForData{
st: st,
pc: pc,
}, nil
}