Skip to content

Commit

Permalink
Replace custom Redis config struct with go-redis UniversalOptions
Browse files Browse the repository at this point in the history
Signed-off-by: Anders Ingemann <aim@orbit.online>
  • Loading branch information
andsens committed Mar 19, 2024
1 parent 3cb985c commit f74a981
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 90 deletions.
9 changes: 4 additions & 5 deletions cmd/registry/config-cache.yml
Expand Up @@ -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
Expand Down
69 changes: 42 additions & 27 deletions configuration/configuration.go
@@ -1,6 +1,7 @@
package configuration

import (
"crypto/tls"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -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.
Expand Down
35 changes: 14 additions & 21 deletions configuration/configuration_test.go
Expand Up @@ -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,
},
}

Expand Down Expand Up @@ -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
Expand Down
22 changes: 8 additions & 14 deletions docs/content/about/configuration.md
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
58 changes: 42 additions & 16 deletions registry/handlers/app.go
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand All @@ -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,
})
}

Expand Down
4 changes: 2 additions & 2 deletions registry/storage/cache/redis/redis.go
Expand Up @@ -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
Expand All @@ -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,
Expand Down
9 changes: 4 additions & 5 deletions tests/conf-e2e-cloud-storage.yml
Expand Up @@ -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
Expand Down

0 comments on commit f74a981

Please sign in to comment.