99 lines
3.5 KiB
Go
99 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()
|
||
|
}
|