Skip to content

Commit 7a4d587

Browse files
authored
FFM-12528 - Add additional config options for redis (#408)
1 parent 1829809 commit 7a4d587

File tree

3 files changed

+141
-17
lines changed

3 files changed

+141
-17
lines changed

cmd/ff-proxy/main.go

Lines changed: 135 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,25 @@ var (
6666
redisPassword string
6767
redisUsername string
6868
redisDB int
69-
redisPoolSize int
69+
70+
// Redis Config Options
71+
redisMaxRetries int
72+
redisMinRetryBackoffMilliseconds int
73+
redisMaxRetryBackoffMilliseconds int
74+
75+
redisDialTimeoutSeconds int
76+
redisReadTimeoutSeconds int
77+
redisWriteTimeoutSeconds int
78+
79+
redisPoolSize int
80+
redisPoolSizeLiteral int
81+
redisPoolTimeoutSeconds int
82+
83+
redisMinIdleConns int
84+
redisMaxIdleConns int
85+
redisMaxActiveConns int
86+
redisConnMaxIdleTimeMinutes int
87+
redisConnMaxLifetimeMinutes int
7088

7189
// Server Config
7290
port int
@@ -126,7 +144,25 @@ const (
126144
redisPasswordEnv = "REDIS_PASSWORD"
127145
redisUsernameEnv = "REDIS_USERNAME"
128146
redisDBEnv = "REDIS_DB"
129-
redisPoolSizeEnv = "REDIS_POOL_SIZE"
147+
148+
// Redis Config
149+
redisMaxRetriesEnv = "REDIS_MAX_RETRIES"
150+
redisMinRetryBackoffMillisecondsEnv = "REDIS_MIN_RETRY_BACKOFF_MILLIS"
151+
redisMaxRetryBackoffMillisecondsEnv = "REDIS_MAX_RETRY_BACKOFF_MILLIS"
152+
153+
redisDialTimeoutSecondsEnv = "REDIS_DIAL_TIMEOUT_SECONDS"
154+
redisReadTimeoutSecondsEnv = "REDIS_READ_TIMEOUT_SECONDS"
155+
redisWriteTimeoutSecondsEnv = "REDIS_WRITE_TIMEOUT_SECONDS"
156+
157+
redisPoolSizeEnv = "REDIS_POOL_SIZE"
158+
redisPoolSizeLiteralEnv = "REDIS_POOL_SIZE_LITERAL"
159+
redisPoolTimeoutSecondsEnv = "REDIS_POOL_TIMEOUT_SECONDS"
160+
161+
redisMinIdleConnsEnv = "REDIS_MIN_IDLE_CONNS"
162+
redisMaxIdleConnsEnv = "REDIS_MAX_IDLE_CONNS"
163+
redisMaxActiveConnsEnv = "REDIS_MAX_ACTIVE_CONNS"
164+
redisConnMaxIdleTimeMinutesEnv = "REDIS_CON_MAX_IDLE_TIME_MINUTES"
165+
redisConnMaxLifetimeMinutesEnv = "REDIS_CON_MAX_LIFETIME_MINUTES"
130166

131167
// Server Config
132168
portEnv = "PORT"
@@ -170,7 +206,25 @@ const (
170206
redisPasswordFlag = "redis-password"
171207
redisUsernameFlag = "redis-username"
172208
redisDBFlag = "redis-db"
173-
redisPoolSizeFlag = "redis-pool-size"
209+
210+
// Redis Configuration Options
211+
redisMaxRetriesFlag = "redis-max-retries"
212+
redisMinRetryBackoffMillisecondsFlag = "redis-min-retry-backoff-milliseconds"
213+
redisMaxRetryBackoffMillisecondsFlag = "redis-max-retry-backoff-milliseconds"
214+
215+
redisDialTimeoutSecondsFlag = "redis-dial-timeout-seconds"
216+
redisReadTimeoutSecondsFlag = "redis-read-timeout-seconds"
217+
redisWriteTimeoutSecondsFlag = "redis-write-timeout-seconds"
218+
219+
redisPoolSizeFlag = "redis-pool-size"
220+
redisPoolSizeLiteralFlag = "redis-pool-size-literal"
221+
redisPoolTimeoutSecondsFlag = "redis-pool-timeout"
222+
223+
redisMinIdleConnsFlag = "redis-min-idle-conns"
224+
redisMaxIdleConnsFlag = "redis-max-idle-conns"
225+
redisMaxActiveConnsFlag = "redis-max-active-conns"
226+
redisConnMaxIdleTimeMinutesFlag = "redis-conn-max-idle-time-minutes"
227+
redisConnMaxLifetimeMinutesFlag = "redis-conn-max-lifetime-minutes"
174228

175229
// Server Config
176230
portFlag = "port"
@@ -214,7 +268,25 @@ func init() {
214268
flag.StringVar(&redisPassword, redisPasswordFlag, "", "Optional. Redis password")
215269
flag.StringVar(&redisUsername, redisUsernameFlag, "", "Optional. Redis username")
216270
flag.IntVar(&redisDB, redisDBFlag, 0, "Database to be selected after connecting to the server.")
217-
flag.IntVar(&redisPoolSize, redisPoolSizeFlag, 10, "sets the redi connection pool size, to this value multipled by the number of CPU available. E.g if this value is 10 and you've 2 CPU the connection pool size will be 20")
271+
272+
// Redis Configuration Options
273+
flag.IntVar(&redisMaxRetries, redisMaxRetriesFlag, 3, "Maximum number of retries before giving up. Default is 3 retries.")
274+
flag.IntVar(&redisMinRetryBackoffMilliseconds, redisMinRetryBackoffMillisecondsFlag, 8, "Minimum backoff between each retry.Default is 8 milliseconds;")
275+
flag.IntVar(&redisMaxRetryBackoffMilliseconds, redisMaxRetryBackoffMillisecondsFlag, 512, "Maximum backoff between each retry. Default is 512 milliseconds")
276+
277+
flag.IntVar(&redisDialTimeoutSeconds, redisDialTimeoutSecondsFlag, 5, "Dial timeout for establishing new connections. Default is 5 seconds")
278+
flag.IntVar(&redisReadTimeoutSeconds, redisReadTimeoutSecondsFlag, 3, " Timeout for socket reads. If reached, commands will fail with a timeout instead of blocking. Default is 3 seconds.")
279+
flag.IntVar(&redisWriteTimeoutSeconds, redisWriteTimeoutSecondsFlag, 3, "Timeout for socket writes. If reached, commands will fail with a timeout instead of blocking. Default is 3 seconds.")
280+
281+
flag.IntVar(&redisPoolSize, redisPoolSizeFlag, 10, "Legacy setting that has been kept for backwards compatibility, you may want to use REDIS_POOL_SIZE_LITERAL going forward. This sets the redis connection pool size, to this value multiplied by the number of CPU available. E.g if this value is 10 and you've 2 CPU the connection pool size will be 20")
282+
flag.IntVar(&redisPoolSizeLiteral, redisPoolSizeLiteralFlag, 0, "sets the maximum number of socket connections to the literal value passed e.g. setting this to 10 means the connection pool size will be 10. If not specified, this will be set to the default value of 10 per CPU")
283+
flag.IntVar(&redisPoolTimeoutSeconds, redisPoolTimeoutSecondsFlag, 4, "Amount of time client waits for connection if all connections are busy before returning an error.Default is 4 seconds (default ReadTimeout + 1 second)")
284+
285+
flag.IntVar(&redisMinIdleConns, redisMinIdleConnsFlag, 0, "Minimum number of idle connections which is useful when establishing new connection is slow.")
286+
flag.IntVar(&redisMaxIdleConns, redisMaxIdleConnsFlag, 0, "MaxIdleConns is the maximum number of idle connections. The idle connections are not closed by default. Default: 0")
287+
flag.IntVar(&redisMaxActiveConns, redisMaxActiveConnsFlag, 0, "MaxActiveConns is the maximum number of connections allocated by the pool at a given time. When zero, there is no limit on the number of connections in the pool. If the pool is full, the next call to Get() will block until a connection is released.")
288+
flag.IntVar(&redisConnMaxIdleTimeMinutes, redisConnMaxIdleTimeMinutesFlag, 30, "The maximum amount of time a connection may be idle. Should be less than server's timeout. Expired connections may be closed lazily before reuse. If d <= 0, connections are not closed due to a connection's idle time. -1 disables idle timeout check. Default: 30 minutes")
289+
flag.IntVar(&redisConnMaxLifetimeMinutes, redisConnMaxLifetimeMinutesFlag, 0, "The maximum amount of time a connection may be reused. Expired connections may be closed lazily before reuse. If <= 0, connections are not closed due to a connection's age. Default: 0")
218290

219291
// Server Config
220292
flag.IntVar(&port, portFlag, 8000, "port the relay proxy service is exposed on, default's to 8000")
@@ -266,6 +338,23 @@ func init() {
266338
metricsStreamMaxLenEnv: metricsStreamMaxLenFlag,
267339
metricsStreamReadConcurrencyEnv: metricStreamReadConcurrencyFlag,
268340
forwardTargetsEnv: forwardTargetsFlag,
341+
342+
redisMaxRetriesEnv: redisMaxRetriesFlag,
343+
redisMinRetryBackoffMillisecondsEnv: redisMinRetryBackoffMillisecondsFlag,
344+
redisMaxRetryBackoffMillisecondsEnv: redisMaxRetryBackoffMillisecondsFlag,
345+
346+
redisDialTimeoutSecondsEnv: redisDialTimeoutSecondsFlag,
347+
redisReadTimeoutSecondsEnv: redisReadTimeoutSecondsFlag,
348+
redisWriteTimeoutSecondsEnv: redisWriteTimeoutSecondsFlag,
349+
350+
redisPoolSizeLiteralEnv: redisPoolSizeLiteralFlag,
351+
redisPoolTimeoutSecondsEnv: redisPoolTimeoutSecondsFlag,
352+
353+
redisMinIdleConnsEnv: redisMinIdleConnsFlag,
354+
redisMaxIdleConnsEnv: redisMaxIdleConnsFlag,
355+
redisMaxActiveConnsEnv: redisMaxActiveConnsFlag,
356+
redisConnMaxIdleTimeMinutesEnv: redisConnMaxIdleTimeMinutesFlag,
357+
redisConnMaxLifetimeMinutesEnv: redisConnMaxLifetimeMinutesFlag,
269358
})
270359

271360
flag.Parse()
@@ -760,20 +849,55 @@ func newRedisClient(addr string, username string, password string, db int, logge
760849
splitAddr[i] = removeRedisScheme(split)
761850
}
762851

852+
minRetryBackoff := time.Duration(redisMinRetryBackoffMilliseconds) * time.Millisecond
853+
maxRetryBackoff := time.Duration(redisMaxRetryBackoffMilliseconds) * time.Millisecond
854+
dialTimeout := time.Duration(redisDialTimeoutSeconds) * time.Second
855+
readTimeout := time.Duration(redisReadTimeoutSeconds) * time.Second
856+
writeTimeout := time.Duration(redisWriteTimeoutSeconds) * time.Second
857+
poolTimeout := time.Duration(redisPoolTimeoutSeconds) * time.Second
858+
maxIdleTime := time.Duration(redisConnMaxIdleTimeMinutes) * time.Minute
859+
connMaxLifetime := time.Duration(redisConnMaxLifetimeMinutes) * time.Minute
860+
861+
if poolTimeout < readTimeout {
862+
poolTimeout = readTimeout + time.Second
863+
logger.Warn("redis pool timeout is less than readTimeout, setting redis pool timeout to read timeout +1 second", "readTimeout", readTimeout, "poolTimeout", poolTimeout)
864+
}
865+
866+
// For backwards compatibility by default we use the old method of figuring out the pool size
867+
// which is the REDIS_POOL_SIZE value multiplied by the number of CPU.
868+
//
869+
// However, if REDIS_POOL_SIZE_LITERAL is set then we will use it instead.
870+
poolSize := redisPoolSize * runtime.NumCPU()
871+
if redisPoolSizeLiteral >= 0 {
872+
poolSize = redisPoolSizeLiteral
873+
}
874+
763875
opts := redis.UniversalOptions{
764-
Addrs: splitAddr,
765-
DB: db,
766-
Username: username,
767-
Password: password,
768-
PoolSize: redisPoolSize * runtime.NumCPU(),
769-
TLSConfig: parsed.TLSConfig,
876+
Addrs: splitAddr,
877+
DB: db,
878+
Username: username,
879+
Password: password,
880+
PoolSize: poolSize,
881+
TLSConfig: parsed.TLSConfig,
882+
MaxRetries: redisMaxRetries,
883+
MinRetryBackoff: minRetryBackoff,
884+
MaxRetryBackoff: maxRetryBackoff,
885+
DialTimeout: dialTimeout,
886+
ReadTimeout: readTimeout,
887+
WriteTimeout: writeTimeout,
888+
PoolTimeout: poolTimeout,
889+
MinIdleConns: redisMinIdleConns,
890+
MaxIdleConns: redisMaxIdleConns,
891+
MaxActiveConns: redisMaxActiveConns,
892+
ConnMaxIdleTime: maxIdleTime,
893+
ConnMaxLifetime: connMaxLifetime,
770894
}
771895

772896
if redisPassword != "" {
773897
opts.Password = redisPassword
774898
}
775899

776-
logger.Info("connecting to redis", "address", redisAddress, "poolSize", opts.PoolSize)
900+
logger.Info("connecting to redis", "redis_config", fmt.Sprintf("%+v", opts))
777901
return redis.NewUniversalClient(&opts)
778902
}
779903

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ require (
2222
github.com/labstack/echo/v4 v4.13.4
2323
github.com/patrickmn/go-cache v2.1.0+incompatible
2424
github.com/prometheus/client_golang v1.11.1
25-
github.com/redis/go-redis/v9 v9.5.5
25+
github.com/redis/go-redis/v9 v9.11.0
2626
github.com/sirupsen/logrus v1.8.1
2727
github.com/stretchr/testify v1.10.0
2828
go.uber.org/zap v1.19.1
@@ -39,7 +39,7 @@ require (
3939
github.com/apapsch/go-jsonmerge/v2 v2.0.0 // indirect
4040
github.com/beorn7/perks v1.0.1 // indirect
4141
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
42-
github.com/cespare/xxhash/v2 v2.2.0 // indirect
42+
github.com/cespare/xxhash/v2 v2.3.0 // indirect
4343
github.com/davecgh/go-spew v1.1.1 // indirect
4444
github.com/deepmap/oapi-codegen/v2 v2.1.0 // indirect
4545
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyY
7373
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
7474
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
7575
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
76-
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
77-
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
76+
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
77+
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
7878
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
7979
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
8080
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -384,8 +384,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
384384
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
385385
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
386386
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
387-
github.com/redis/go-redis/v9 v9.5.5 h1:51VEyMF8eOO+NUHFm8fpg+IOc1xFuFOhxs3R+kPu1FM=
388-
github.com/redis/go-redis/v9 v9.5.5/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
387+
github.com/redis/go-redis/v9 v9.11.0 h1:E3S08Gl/nJNn5vkxd2i78wZxWAPNZgUNTp8WIJUAiIs=
388+
github.com/redis/go-redis/v9 v9.11.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw=
389389
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
390390
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
391391
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=

0 commit comments

Comments
 (0)