diff --git a/cmd/registry/config-cache.yml b/cmd/registry/config-cache.yml index d648303d9c1..b553ad5bcde 100644 --- a/cmd/registry/config-cache.yml +++ b/cmd/registry/config-cache.yml @@ -20,11 +20,10 @@ http: headers: X-Content-Type-Options: [nosniff] redis: - addr: localhost:6379 - pool: - maxidle: 16 - maxactive: 64 - idletimeout: 300s + addrs: [localhost:6379] + maxidleconns: 16 + poolsize: 64 + connmaxidletime: 300s dialtimeout: 10ms readtimeout: 10ms writetimeout: 10ms diff --git a/configuration/configuration.go b/configuration/configuration.go index 530a278d8eb..6d927e85568 100644 --- a/configuration/configuration.go +++ b/configuration/configuration.go @@ -1,6 +1,7 @@ package configuration import ( + "crypto/tls" "errors" "fmt" "io" @@ -277,42 +278,56 @@ type FileChecker struct { Threshold int `yaml:"threshold,omitempty"` } -// Redis configures the redis pool available to the registry webapp. type Redis struct { - // Addr specifies the the redis instance available to the application. - Addr string `yaml:"addr,omitempty"` + // Either a single address or a seed list of host:port addresses + // of cluster/sentinel nodes. + Addrs []string - // Usernames can be used as a finer-grained permission control since the introduction of the redis 6.0. - Username string `yaml:"username,omitempty"` + // ClientName will execute the `CLIENT SETNAME ClientName` command for each conn. + ClientName string - // Password string to use when making a connection. - Password string `yaml:"password,omitempty"` + // Database to be selected after connecting to the server. + // Only single-node and failover clients. + DB int - // DB specifies the database to connect to on the redis instance. - DB int `yaml:"db,omitempty"` + Protocol int + Username string + Password string + SentinelUsername string + SentinelPassword string - // TLS configures settings for redis in-transit encryption - TLS struct { - Enabled bool `yaml:"enabled,omitempty"` - } `yaml:"tls,omitempty"` + MaxRetries int + MinRetryBackoff time.Duration + MaxRetryBackoff time.Duration + + DialTimeout time.Duration + ReadTimeout time.Duration + WriteTimeout time.Duration + ContextTimeoutEnabled bool + + // PoolFIFO uses FIFO mode for each node connection pool GET/PUT (default LIFO). + PoolFIFO bool + + PoolSize int + PoolTimeout time.Duration + MinIdleConns int + MaxIdleConns int + ConnMaxIdleTime time.Duration + ConnMaxLifetime time.Duration + + TLSConfig *tls.Config - DialTimeout time.Duration `yaml:"dialtimeout,omitempty"` // timeout for connect - ReadTimeout time.Duration `yaml:"readtimeout,omitempty"` // timeout for reads of data - WriteTimeout time.Duration `yaml:"writetimeout,omitempty"` // timeout for writes of data + // Only cluster clients. - // Pool configures the behavior of the redis connection pool. - Pool struct { - // MaxIdle sets the maximum number of idle connections. - MaxIdle int `yaml:"maxidle,omitempty"` + MaxRedirects int + ReadOnly bool + RouteByLatency bool + RouteRandomly bool - // MaxActive sets the maximum number of connections that should be - // opened before blocking a connection request. - MaxActive int `yaml:"maxactive,omitempty"` + // The sentinel master name. + // Only failover clients. - // IdleTimeout sets the amount time to wait before closing - // inactive connections. - IdleTimeout time.Duration `yaml:"idletimeout,omitempty"` - } `yaml:"pool,omitempty"` + MasterName string } // HTTPChecker is a type of entry in the health section for checking HTTP URIs. diff --git a/configuration/configuration_test.go b/configuration/configuration_test.go index 2139f8f1a9c..30f80e5e38e 100644 --- a/configuration/configuration_test.go +++ b/configuration/configuration_test.go @@ -131,22 +131,16 @@ var configStruct = Configuration{ }, }, Redis: Redis{ - Addr: "localhost:6379", - Username: "alice", - Password: "123456", - DB: 1, - Pool: struct { - MaxIdle int `yaml:"maxidle,omitempty"` - MaxActive int `yaml:"maxactive,omitempty"` - IdleTimeout time.Duration `yaml:"idletimeout,omitempty"` - }{ - MaxIdle: 16, - MaxActive: 64, - IdleTimeout: time.Second * 300, - }, - DialTimeout: time.Millisecond * 10, - ReadTimeout: time.Millisecond * 10, - WriteTimeout: time.Millisecond * 10, + Addrs: []string{"localhost:6379"}, + Username: "alice", + Password: "123456", + DB: 1, + MaxIdleConns: 16, + PoolSize: 64, + ConnMaxIdleTime: time.Second * 300, + DialTimeout: time.Millisecond * 10, + ReadTimeout: time.Millisecond * 10, + WriteTimeout: time.Millisecond * 10, }, } @@ -190,14 +184,13 @@ http: headers: X-Content-Type-Options: [nosniff] redis: - addr: localhost:6379 + addrs: [localhost:6379] username: alice password: 123456 db: 1 - pool: - maxidle: 16 - maxactive: 64 - idletimeout: 300s + maxidleconns: 16 + poolsize: 64 + connmaxidletime: 300s dialtimeout: 10ms readtimeout: 10ms writetimeout: 10ms diff --git a/docs/content/about/configuration.md b/docs/content/about/configuration.md index 482a40ca742..c508dddf9e1 100644 --- a/docs/content/about/configuration.md +++ b/docs/content/about/configuration.md @@ -241,18 +241,15 @@ notifications: actions: - pull redis: - addr: localhost:6379 + addrs: [localhost:6379] password: asecret db: 0 dialtimeout: 10ms readtimeout: 10ms writetimeout: 10ms - pool: - maxidle: 16 - maxactive: 64 - idletimeout: 300s - tls: - enabled: false + maxidleconns: 16 + poolsize: 64 + connmaxidletime: 300s health: storagedriver: enabled: true @@ -954,18 +951,15 @@ The `events` structure configures the information provided in event notification ```yaml redis: - addr: localhost:6379 + addrs: [localhost:6379] password: asecret db: 0 dialtimeout: 10ms readtimeout: 10ms writetimeout: 10ms - pool: - maxidle: 16 - maxactive: 64 - idletimeout: 300s - tls: - enabled: false + maxidleconns: 16 + poolsize: 64 + connmaxidletime: 300s ``` Declare parameters for constructing the `redis` connections. Registry instances diff --git a/registry/handlers/app.go b/registry/handlers/app.go index 2983176b858..437e283f3f2 100644 --- a/registry/handlers/app.go +++ b/registry/handlers/app.go @@ -77,7 +77,7 @@ type App struct { source notifications.SourceRecord } - redis *redis.Client + redis redis.UniversalClient // isCache is true if this registry is configured as a pull through cache isCache bool @@ -487,7 +487,7 @@ func (app *App) configureEvents(configuration *configuration.Configuration) { } func (app *App) configureRedis(cfg *configuration.Configuration) { - if cfg.Redis.Addr == "" { + if len(cfg.Redis.Addrs) == 0 { dcontext.GetLogger(app).Infof("redis not configured") return } @@ -514,24 +514,50 @@ func (app *App) configureRedis(cfg *configuration.Configuration) { })) } -func (app *App) createPool(cfg configuration.Redis) *redis.Client { - return redis.NewClient(&redis.Options{ - Addr: cfg.Addr, +func (app *App) createPool(cfg configuration.Redis) redis.UniversalClient { + + return redis.NewUniversalClient(&redis.UniversalOptions{ + Addrs: cfg.Addrs, OnConnect: func(ctx context.Context, cn *redis.Conn) error { res := cn.Ping(ctx) return res.Err() }, - Username: cfg.Username, - Password: cfg.Password, - DB: cfg.DB, - MaxRetries: 3, - DialTimeout: cfg.DialTimeout, - ReadTimeout: cfg.ReadTimeout, - WriteTimeout: cfg.WriteTimeout, - PoolFIFO: false, - MaxIdleConns: cfg.Pool.MaxIdle, - PoolSize: cfg.Pool.MaxActive, - ConnMaxIdleTime: cfg.Pool.IdleTimeout, + ClientName: cfg.ClientName, + + DB: cfg.DB, + + Protocol: cfg.Protocol, + Username: cfg.Username, + Password: cfg.Password, + SentinelUsername: cfg.SentinelUsername, + SentinelPassword: cfg.SentinelPassword, + + MaxRetries: cfg.MaxRetries, + MinRetryBackoff: cfg.MinRetryBackoff, + MaxRetryBackoff: cfg.MaxRetryBackoff, + + DialTimeout: cfg.DialTimeout, + ReadTimeout: cfg.ReadTimeout, + WriteTimeout: cfg.WriteTimeout, + ContextTimeoutEnabled: cfg.ContextTimeoutEnabled, + + PoolFIFO: cfg.PoolFIFO, + + PoolSize: cfg.PoolSize, + PoolTimeout: cfg.PoolTimeout, + MinIdleConns: cfg.MinIdleConns, + MaxIdleConns: cfg.MaxIdleConns, + ConnMaxIdleTime: cfg.ConnMaxIdleTime, + ConnMaxLifetime: cfg.ConnMaxLifetime, + + TLSConfig: cfg.TLSConfig, + + MaxRedirects: cfg.MaxRedirects, + ReadOnly: cfg.ReadOnly, + RouteByLatency: cfg.RouteByLatency, + RouteRandomly: cfg.RouteRandomly, + + MasterName: cfg.MasterName, }) } diff --git a/registry/storage/cache/redis/redis.go b/registry/storage/cache/redis/redis.go index d2596b7bc43..baae8e1be6b 100644 --- a/registry/storage/cache/redis/redis.go +++ b/registry/storage/cache/redis/redis.go @@ -25,7 +25,7 @@ import ( // 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 redisBlobDescriptorService struct { - pool *redis.Client + pool redis.UniversalClient // 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 @@ -37,7 +37,7 @@ var _ distribution.BlobDescriptorService = &redisBlobDescriptorService{} // NewRedisBlobDescriptorCacheProvider returns a new redis-based // BlobDescriptorCacheProvider using the provided redis connection pool. -func NewRedisBlobDescriptorCacheProvider(pool *redis.Client) cache.BlobDescriptorCacheProvider { +func NewRedisBlobDescriptorCacheProvider(pool redis.UniversalClient) cache.BlobDescriptorCacheProvider { return metrics.NewPrometheusCacheProvider( &redisBlobDescriptorService{ pool: pool, diff --git a/tests/conf-e2e-cloud-storage.yml b/tests/conf-e2e-cloud-storage.yml index 63a8778c707..89e496c4717 100644 --- a/tests/conf-e2e-cloud-storage.yml +++ b/tests/conf-e2e-cloud-storage.yml @@ -17,15 +17,14 @@ log: formatter: text level: debug redis: - addr: redis:6379 + addrs: [redis:6379] db: 0 dialtimeout: 5s readtimeout: 10ms writetimeout: 10ms - pool: - idletimeout: 60s - maxactive: 64 - maxidle: 16 + maxidleconns: 16 + poolsize: 64 + connmaxidletime: 300s storage: redirect: disable: true