github.com/lusis/distribution@v2.0.1+incompatible/registry/storage/cache/redis.go (about) 1 package cache 2 3 import ( 4 ctxu "github.com/docker/distribution/context" 5 "github.com/docker/distribution/digest" 6 "github.com/garyburd/redigo/redis" 7 "golang.org/x/net/context" 8 ) 9 10 // redisLayerInfoCache provides an implementation of storage.LayerInfoCache 11 // based on redis. Layer info is stored in two parts. The first provide fast 12 // access to repository membership through a redis set for each repo. The 13 // second is a redis hash keyed by the digest of the layer, providing path and 14 // length information. Note that there is no implied relationship between 15 // these two caches. The layer may exist in one, both or none and the code 16 // must be written this way. 17 type redisLayerInfoCache struct { 18 pool *redis.Pool 19 20 // TODO(stevvooe): We use a pool because we don't have great control over 21 // the cache lifecycle to manage connections. A new connection if fetched 22 // for each operation. Once we have better lifecycle management of the 23 // request objects, we can change this to a connection. 24 } 25 26 // NewRedisLayerInfoCache returns a new redis-based LayerInfoCache using the 27 // provided redis connection pool. 28 func NewRedisLayerInfoCache(pool *redis.Pool) LayerInfoCache { 29 return &base{&redisLayerInfoCache{ 30 pool: pool, 31 }} 32 } 33 34 // Contains does a membership check on the repository blob set in redis. This 35 // is used as an access check before looking up global path information. If 36 // false is returned, the caller should still check the backend to if it 37 // exists elsewhere. 38 func (rlic *redisLayerInfoCache) Contains(ctx context.Context, repo string, dgst digest.Digest) (bool, error) { 39 conn := rlic.pool.Get() 40 defer conn.Close() 41 42 ctxu.GetLogger(ctx).Debugf("(*redisLayerInfoCache).Contains(%q, %q)", repo, dgst) 43 return redis.Bool(conn.Do("SISMEMBER", rlic.repositoryBlobSetKey(repo), dgst)) 44 } 45 46 // Add adds the layer to the redis repository blob set. 47 func (rlic *redisLayerInfoCache) Add(ctx context.Context, repo string, dgst digest.Digest) error { 48 conn := rlic.pool.Get() 49 defer conn.Close() 50 51 ctxu.GetLogger(ctx).Debugf("(*redisLayerInfoCache).Add(%q, %q)", repo, dgst) 52 _, err := conn.Do("SADD", rlic.repositoryBlobSetKey(repo), dgst) 53 return err 54 } 55 56 // Meta retrieves the layer meta data from the redis hash, returning 57 // ErrUnknownLayer if not found. 58 func (rlic *redisLayerInfoCache) Meta(ctx context.Context, dgst digest.Digest) (LayerMeta, error) { 59 conn := rlic.pool.Get() 60 defer conn.Close() 61 62 reply, err := redis.Values(conn.Do("HMGET", rlic.blobMetaHashKey(dgst), "path", "length")) 63 if err != nil { 64 return LayerMeta{}, err 65 } 66 67 if len(reply) < 2 || reply[0] == nil || reply[1] == nil { 68 return LayerMeta{}, ErrNotFound 69 } 70 71 var meta LayerMeta 72 if _, err := redis.Scan(reply, &meta.Path, &meta.Length); err != nil { 73 return LayerMeta{}, err 74 } 75 76 return meta, nil 77 } 78 79 // SetMeta sets the meta data for the given digest using a redis hash. A hash 80 // is used here since we may store unrelated fields about a layer in the 81 // future. 82 func (rlic *redisLayerInfoCache) SetMeta(ctx context.Context, dgst digest.Digest, meta LayerMeta) error { 83 conn := rlic.pool.Get() 84 defer conn.Close() 85 86 _, err := conn.Do("HMSET", rlic.blobMetaHashKey(dgst), "path", meta.Path, "length", meta.Length) 87 return err 88 } 89 90 // repositoryBlobSetKey returns the key for the blob set in the cache. 91 func (rlic *redisLayerInfoCache) repositoryBlobSetKey(repo string) string { 92 return "repository::" + repo + "::blobs" 93 } 94 95 // blobMetaHashKey returns the cache key for immutable blob meta data. 96 func (rlic *redisLayerInfoCache) blobMetaHashKey(dgst digest.Digest) string { 97 return "blobs::" + dgst.String() 98 }