diff --git a/registry/storage/cache/metrics/prom.go b/registry/storage/cache/metrics/prom.go new file mode 100644 index 000000000..22e847b6d --- /dev/null +++ b/registry/storage/cache/metrics/prom.go @@ -0,0 +1,68 @@ +package metrics + +import ( + "context" + "github.com/docker/distribution" + "github.com/docker/distribution/registry/storage/cache" + "github.com/docker/go-metrics" + "github.com/opencontainers/go-digest" + prometheus "github.com/docker/distribution/metrics" + "time" +) + +type prometheusCacheProvider struct { + cache.BlobDescriptorCacheProvider + latencyTimer metrics.LabeledTimer +} + +func NewPrometheusCacheProvider(wrap cache.BlobDescriptorCacheProvider, name, help string) cache.BlobDescriptorCacheProvider { + return &prometheusCacheProvider{ + wrap, + // TODO: May want to have fine grained buckets since redis calls are generally <1ms and the default minimum bucket is 5ms. + prometheus.StorageNamespace.NewLabeledTimer(name, help, "operation"), + } +} + +func (p *prometheusCacheProvider) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) { + start := time.Now() + d, e := p.BlobDescriptorCacheProvider.Stat(ctx, dgst) + p.latencyTimer.WithValues("Stat").UpdateSince(start) + return d, e +} + +func (p *prometheusCacheProvider) SetDescriptor(ctx context.Context, dgst digest.Digest, desc distribution.Descriptor) error { + start := time.Now() + e := p.BlobDescriptorCacheProvider.SetDescriptor(ctx, dgst, desc) + p.latencyTimer.WithValues("SetDescriptor").UpdateSince(start) + return e +} + +type prometheusRepoCacheProvider struct { + distribution.BlobDescriptorService + latencyTimer metrics.LabeledTimer +} + +func (p *prometheusRepoCacheProvider) Stat(ctx context.Context, dgst digest.Digest) (distribution.Descriptor, error) { + start := time.Now() + d, e := p.BlobDescriptorService.Stat(ctx, dgst) + p.latencyTimer.WithValues("RepoStat").UpdateSince(start) + return d, e +} + +func (p *prometheusRepoCacheProvider) SetDescriptor(ctx context.Context, dgst digest.Digest, desc distribution.Descriptor) error { + start := time.Now() + e := p.BlobDescriptorService.SetDescriptor(ctx, dgst, desc) + p.latencyTimer.WithValues("RepoSetDescriptor").UpdateSince(start) + return e +} + +func (p *prometheusCacheProvider) RepositoryScoped(repo string) (distribution.BlobDescriptorService, error) { + s, err := p.BlobDescriptorCacheProvider.RepositoryScoped(repo) + if err != nil { + return nil, err + } + return &prometheusRepoCacheProvider{ + s, + p.latencyTimer, + }, nil +} \ No newline at end of file diff --git a/registry/storage/cache/redis/redis.go b/registry/storage/cache/redis/redis.go index 550d703b6..531fae2f4 100644 --- a/registry/storage/cache/redis/redis.go +++ b/registry/storage/cache/redis/redis.go @@ -3,6 +3,7 @@ package redis import ( "context" "fmt" + "github.com/docker/distribution/registry/storage/cache/metrics" "github.com/docker/distribution" "github.com/docker/distribution/reference" @@ -34,9 +35,13 @@ type redisBlobDescriptorService struct { // NewRedisBlobDescriptorCacheProvider returns a new redis-based // BlobDescriptorCacheProvider using the provided redis connection pool. func NewRedisBlobDescriptorCacheProvider(pool *redis.Pool) cache.BlobDescriptorCacheProvider { - return &redisBlobDescriptorService{ - pool: pool, - } + return metrics.NewPrometheusCacheProvider( + &redisBlobDescriptorService{ + pool: pool, + }, + "cache_redis", + "Number of seconds taken by redis", + ) } // RepositoryScoped returns the scoped cache.