forked from TrueCloudLab/distribution
a7c2dceea5
This changeset defines the interface for layer info caches. Layer info caches speed up access to layer meta data accessed in storage driver backends. The two main operations are tests for repository membership and resolving path and size information for backend blobs. Two implementations are available. The main implementation leverages redis to store layer info. An alternative implementation simply caches layer info in maps, which should speed up resolution for less sophisticated implementations. Signed-off-by: Stephen J Day <stephen.day@docker.com>
98 lines
3.5 KiB
Go
98 lines
3.5 KiB
Go
package cache
|
|
|
|
import (
|
|
ctxu "github.com/docker/distribution/context"
|
|
"github.com/docker/distribution/digest"
|
|
"github.com/garyburd/redigo/redis"
|
|
"golang.org/x/net/context"
|
|
)
|
|
|
|
// redisLayerInfoCache provides an implementation of storage.LayerInfoCache
|
|
// based on redis. Layer info is stored in two parts. The first provide fast
|
|
// access to repository membership through a redis set for each repo. The
|
|
// second is a redis hash keyed by the digest of the layer, providing path and
|
|
// length information. Note that there is no implied relationship between
|
|
// these two caches. The layer may exist in one, both or none and the code
|
|
// must be written this way.
|
|
type redisLayerInfoCache struct {
|
|
pool *redis.Pool
|
|
|
|
// TODO(stevvooe): We use a pool because we don't have great control over
|
|
// the cache lifecycle to manage connections. A new connection if fetched
|
|
// for each operation. Once we have better lifecycle management of the
|
|
// request objects, we can change this to a connection.
|
|
}
|
|
|
|
// NewRedisLayerInfoCache returns a new redis-based LayerInfoCache using the
|
|
// provided redis connection pool.
|
|
func NewRedisLayerInfoCache(pool *redis.Pool) LayerInfoCache {
|
|
return &base{&redisLayerInfoCache{
|
|
pool: pool,
|
|
}}
|
|
}
|
|
|
|
// Contains does a membership check on the repository blob set in redis. This
|
|
// is used as an access check before looking up global path information. If
|
|
// false is returned, the caller should still check the backend to if it
|
|
// exists elsewhere.
|
|
func (rlic *redisLayerInfoCache) Contains(ctx context.Context, repo string, dgst digest.Digest) (bool, error) {
|
|
conn := rlic.pool.Get()
|
|
defer conn.Close()
|
|
|
|
ctxu.GetLogger(ctx).Debugf("(*redisLayerInfoCache).Contains(%q, %q)", repo, dgst)
|
|
return redis.Bool(conn.Do("SISMEMBER", rlic.repositoryBlobSetKey(repo), dgst))
|
|
}
|
|
|
|
// Add adds the layer to the redis repository blob set.
|
|
func (rlic *redisLayerInfoCache) Add(ctx context.Context, repo string, dgst digest.Digest) error {
|
|
conn := rlic.pool.Get()
|
|
defer conn.Close()
|
|
|
|
ctxu.GetLogger(ctx).Debugf("(*redisLayerInfoCache).Add(%q, %q)", repo, dgst)
|
|
_, err := conn.Do("SADD", rlic.repositoryBlobSetKey(repo), dgst)
|
|
return err
|
|
}
|
|
|
|
// Meta retrieves the layer meta data from the redis hash, returning
|
|
// ErrUnknownLayer if not found.
|
|
func (rlic *redisLayerInfoCache) Meta(ctx context.Context, dgst digest.Digest) (LayerMeta, error) {
|
|
conn := rlic.pool.Get()
|
|
defer conn.Close()
|
|
|
|
reply, err := redis.Values(conn.Do("HMGET", rlic.blobMetaHashKey(dgst), "path", "length"))
|
|
if err != nil {
|
|
return LayerMeta{}, err
|
|
}
|
|
|
|
if len(reply) < 2 || reply[0] == nil || reply[1] == nil {
|
|
return LayerMeta{}, ErrNotFound
|
|
}
|
|
|
|
var meta LayerMeta
|
|
if _, err := redis.Scan(reply, &meta.Path, &meta.Length); err != nil {
|
|
return LayerMeta{}, err
|
|
}
|
|
|
|
return meta, nil
|
|
}
|
|
|
|
// SetMeta sets the meta data for the given digest using a redis hash. A hash
|
|
// is used here since we may store unrelated fields about a layer in the
|
|
// future.
|
|
func (rlic *redisLayerInfoCache) SetMeta(ctx context.Context, dgst digest.Digest, meta LayerMeta) error {
|
|
conn := rlic.pool.Get()
|
|
defer conn.Close()
|
|
|
|
_, err := conn.Do("HMSET", rlic.blobMetaHashKey(dgst), "path", meta.Path, "length", meta.Length)
|
|
return err
|
|
}
|
|
|
|
// repositoryBlobSetKey returns the key for the blob set in the cache.
|
|
func (rlic *redisLayerInfoCache) repositoryBlobSetKey(repo string) string {
|
|
return "repository::" + repo + "::blobs"
|
|
}
|
|
|
|
// blobMetaHashKey returns the cache key for immutable blob meta data.
|
|
func (rlic *redisLayerInfoCache) blobMetaHashKey(dgst digest.Digest) string {
|
|
return "blobs::" + dgst.String()
|
|
}
|