forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/redis/go-redis/v9/options.go

922 lines
15 KiB
Go
Raw Normal View History

2024-02-18 10:42:21 +00:00
package redis
import (
"context"
"crypto/tls"
"errors"
"fmt"
"net"
"net/url"
"runtime"
"sort"
"strconv"
"strings"
"time"
"github.com/redis/go-redis/v9/internal/pool"
)
// Limiter is the interface of a rate limiter or a circuit breaker.
2024-02-18 10:42:21 +00:00
type Limiter interface {
2024-02-18 10:42:21 +00:00
// Allow returns nil if operation is allowed or an error otherwise.
2024-02-18 10:42:21 +00:00
// If operation is allowed client must ReportResult of the operation
2024-02-18 10:42:21 +00:00
// whether it is a success or a failure.
2024-02-18 10:42:21 +00:00
Allow() error
2024-02-18 10:42:21 +00:00
// ReportResult reports the result of the previously allowed operation.
2024-02-18 10:42:21 +00:00
// nil indicates a success, non-nil error usually indicates a failure.
2024-02-18 10:42:21 +00:00
ReportResult(result error)
}
// Options keeps the settings to set up redis connection.
2024-02-18 10:42:21 +00:00
type Options struct {
2024-02-18 10:42:21 +00:00
// The network type, either tcp or unix.
2024-02-18 10:42:21 +00:00
// Default is tcp.
2024-02-18 10:42:21 +00:00
Network string
2024-02-18 10:42:21 +00:00
// host:port address.
2024-02-18 10:42:21 +00:00
Addr string
// ClientName will execute the `CLIENT SETNAME ClientName` command for each conn.
2024-02-18 10:42:21 +00:00
ClientName string
// Dialer creates new network connection and has priority over
2024-02-18 10:42:21 +00:00
// Network and Addr options.
2024-02-18 10:42:21 +00:00
Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
// Hook that is called when new connection is established.
2024-02-18 10:42:21 +00:00
OnConnect func(ctx context.Context, cn *Conn) error
// Protocol 2 or 3. Use the version to negotiate RESP version with redis-server.
2024-02-18 10:42:21 +00:00
// Default is 3.
2024-02-18 10:42:21 +00:00
Protocol int
2024-02-18 10:42:21 +00:00
// Use the specified Username to authenticate the current connection
2024-02-18 10:42:21 +00:00
// with one of the connections defined in the ACL list when connecting
2024-02-18 10:42:21 +00:00
// to a Redis 6.0 instance, or greater, that is using the Redis ACL system.
2024-02-18 10:42:21 +00:00
Username string
2024-02-18 10:42:21 +00:00
// Optional password. Must match the password specified in the
2024-02-18 10:42:21 +00:00
// requirepass server configuration option (if connecting to a Redis 5.0 instance, or lower),
2024-02-18 10:42:21 +00:00
// or the User Password when connecting to a Redis 6.0 instance, or greater,
2024-02-18 10:42:21 +00:00
// that is using the Redis ACL system.
2024-02-18 10:42:21 +00:00
Password string
2024-02-18 10:42:21 +00:00
// CredentialsProvider allows the username and password to be updated
2024-02-18 10:42:21 +00:00
// before reconnecting. It should return the current username and password.
2024-02-18 10:42:21 +00:00
CredentialsProvider func() (username string, password string)
// Database to be selected after connecting to the server.
2024-02-18 10:42:21 +00:00
DB int
// Maximum number of retries before giving up.
2024-02-18 10:42:21 +00:00
// Default is 3 retries; -1 (not 0) disables retries.
2024-02-18 10:42:21 +00:00
MaxRetries int
2024-02-18 10:42:21 +00:00
// Minimum backoff between each retry.
2024-02-18 10:42:21 +00:00
// Default is 8 milliseconds; -1 disables backoff.
2024-02-18 10:42:21 +00:00
MinRetryBackoff time.Duration
2024-02-18 10:42:21 +00:00
// Maximum backoff between each retry.
2024-02-18 10:42:21 +00:00
// Default is 512 milliseconds; -1 disables backoff.
2024-02-18 10:42:21 +00:00
MaxRetryBackoff time.Duration
// Dial timeout for establishing new connections.
2024-02-18 10:42:21 +00:00
// Default is 5 seconds.
2024-02-18 10:42:21 +00:00
DialTimeout time.Duration
2024-02-18 10:42:21 +00:00
// Timeout for socket reads. If reached, commands will fail
2024-02-18 10:42:21 +00:00
// with a timeout instead of blocking. Supported values:
2024-02-18 10:42:21 +00:00
// - `0` - default timeout (3 seconds).
2024-02-18 10:42:21 +00:00
// - `-1` - no timeout (block indefinitely).
2024-02-18 10:42:21 +00:00
// - `-2` - disables SetReadDeadline calls completely.
2024-02-18 10:42:21 +00:00
ReadTimeout time.Duration
2024-02-18 10:42:21 +00:00
// Timeout for socket writes. If reached, commands will fail
2024-02-18 10:42:21 +00:00
// with a timeout instead of blocking. Supported values:
2024-02-18 10:42:21 +00:00
// - `0` - default timeout (3 seconds).
2024-02-18 10:42:21 +00:00
// - `-1` - no timeout (block indefinitely).
2024-02-18 10:42:21 +00:00
// - `-2` - disables SetWriteDeadline calls completely.
2024-02-18 10:42:21 +00:00
WriteTimeout time.Duration
2024-02-18 10:42:21 +00:00
// ContextTimeoutEnabled controls whether the client respects context timeouts and deadlines.
2024-02-18 10:42:21 +00:00
// See https://redis.uptrace.dev/guide/go-redis-debugging.html#timeouts
2024-02-18 10:42:21 +00:00
ContextTimeoutEnabled bool
// Type of connection pool.
2024-02-18 10:42:21 +00:00
// true for FIFO pool, false for LIFO pool.
2024-02-18 10:42:21 +00:00
// Note that FIFO has slightly higher overhead compared to LIFO,
2024-02-18 10:42:21 +00:00
// but it helps closing idle connections faster reducing the pool size.
2024-02-18 10:42:21 +00:00
PoolFIFO bool
2024-02-18 10:42:21 +00:00
// Base number of socket connections.
2024-02-18 10:42:21 +00:00
// Default is 10 connections per every available CPU as reported by runtime.GOMAXPROCS.
2024-02-18 10:42:21 +00:00
// If there is not enough connections in the pool, new connections will be allocated in excess of PoolSize,
2024-02-18 10:42:21 +00:00
// you can limit it through MaxActiveConns
2024-02-18 10:42:21 +00:00
PoolSize int
2024-02-18 10:42:21 +00:00
// Amount of time client waits for connection if all connections
2024-02-18 10:42:21 +00:00
// are busy before returning an error.
2024-02-18 10:42:21 +00:00
// Default is ReadTimeout + 1 second.
2024-02-18 10:42:21 +00:00
PoolTimeout time.Duration
2024-02-18 10:42:21 +00:00
// Minimum number of idle connections which is useful when establishing
2024-02-18 10:42:21 +00:00
// new connection is slow.
2024-02-18 10:42:21 +00:00
// Default is 0. the idle connections are not closed by default.
2024-02-18 10:42:21 +00:00
MinIdleConns int
2024-02-18 10:42:21 +00:00
// Maximum number of idle connections.
2024-02-18 10:42:21 +00:00
// Default is 0. the idle connections are not closed by default.
2024-02-18 10:42:21 +00:00
MaxIdleConns int
2024-02-18 10:42:21 +00:00
// Maximum number of connections allocated by the pool at a given time.
2024-02-18 10:42:21 +00:00
// When zero, there is no limit on the number of connections in the pool.
2024-02-18 10:42:21 +00:00
MaxActiveConns int
2024-02-18 10:42:21 +00:00
// ConnMaxIdleTime is the maximum amount of time a connection may be idle.
2024-02-18 10:42:21 +00:00
// Should be less than server's timeout.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Expired connections may be closed lazily before reuse.
2024-02-18 10:42:21 +00:00
// If d <= 0, connections are not closed due to a connection's idle time.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Default is 30 minutes. -1 disables idle timeout check.
2024-02-18 10:42:21 +00:00
ConnMaxIdleTime time.Duration
2024-02-18 10:42:21 +00:00
// ConnMaxLifetime is the maximum amount of time a connection may be reused.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Expired connections may be closed lazily before reuse.
2024-02-18 10:42:21 +00:00
// If <= 0, connections are not closed due to a connection's age.
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Default is to not close idle connections.
2024-02-18 10:42:21 +00:00
ConnMaxLifetime time.Duration
// TLS Config to use. When set, TLS will be negotiated.
2024-02-18 10:42:21 +00:00
TLSConfig *tls.Config
// Limiter interface used to implement circuit breaker or rate limiter.
2024-02-18 10:42:21 +00:00
Limiter Limiter
// Enables read only queries on slave/follower nodes.
2024-02-18 10:42:21 +00:00
readOnly bool
// Disable set-lib on connect. Default is false.
2024-02-18 10:42:21 +00:00
DisableIndentity bool
// Add suffix to client name. Default is empty.
2024-02-18 10:42:21 +00:00
IdentitySuffix string
}
func (opt *Options) init() {
2024-02-18 10:42:21 +00:00
if opt.Addr == "" {
2024-02-18 10:42:21 +00:00
opt.Addr = "localhost:6379"
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if opt.Network == "" {
2024-02-18 10:42:21 +00:00
if strings.HasPrefix(opt.Addr, "/") {
2024-02-18 10:42:21 +00:00
opt.Network = "unix"
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
opt.Network = "tcp"
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if opt.DialTimeout == 0 {
2024-02-18 10:42:21 +00:00
opt.DialTimeout = 5 * time.Second
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if opt.Dialer == nil {
2024-02-18 10:42:21 +00:00
opt.Dialer = NewDialer(opt)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if opt.PoolSize == 0 {
2024-02-18 10:42:21 +00:00
opt.PoolSize = 10 * runtime.GOMAXPROCS(0)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
switch opt.ReadTimeout {
2024-02-18 10:42:21 +00:00
case -2:
2024-02-18 10:42:21 +00:00
opt.ReadTimeout = -1
2024-02-18 10:42:21 +00:00
case -1:
2024-02-18 10:42:21 +00:00
opt.ReadTimeout = 0
2024-02-18 10:42:21 +00:00
case 0:
2024-02-18 10:42:21 +00:00
opt.ReadTimeout = 3 * time.Second
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
switch opt.WriteTimeout {
2024-02-18 10:42:21 +00:00
case -2:
2024-02-18 10:42:21 +00:00
opt.WriteTimeout = -1
2024-02-18 10:42:21 +00:00
case -1:
2024-02-18 10:42:21 +00:00
opt.WriteTimeout = 0
2024-02-18 10:42:21 +00:00
case 0:
2024-02-18 10:42:21 +00:00
opt.WriteTimeout = opt.ReadTimeout
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if opt.PoolTimeout == 0 {
2024-02-18 10:42:21 +00:00
if opt.ReadTimeout > 0 {
2024-02-18 10:42:21 +00:00
opt.PoolTimeout = opt.ReadTimeout + time.Second
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
opt.PoolTimeout = 30 * time.Second
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if opt.ConnMaxIdleTime == 0 {
2024-02-18 10:42:21 +00:00
opt.ConnMaxIdleTime = 30 * time.Minute
2024-02-18 10:42:21 +00:00
}
if opt.MaxRetries == -1 {
2024-02-18 10:42:21 +00:00
opt.MaxRetries = 0
2024-02-18 10:42:21 +00:00
} else if opt.MaxRetries == 0 {
2024-02-18 10:42:21 +00:00
opt.MaxRetries = 3
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
switch opt.MinRetryBackoff {
2024-02-18 10:42:21 +00:00
case -1:
2024-02-18 10:42:21 +00:00
opt.MinRetryBackoff = 0
2024-02-18 10:42:21 +00:00
case 0:
2024-02-18 10:42:21 +00:00
opt.MinRetryBackoff = 8 * time.Millisecond
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
switch opt.MaxRetryBackoff {
2024-02-18 10:42:21 +00:00
case -1:
2024-02-18 10:42:21 +00:00
opt.MaxRetryBackoff = 0
2024-02-18 10:42:21 +00:00
case 0:
2024-02-18 10:42:21 +00:00
opt.MaxRetryBackoff = 512 * time.Millisecond
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
func (opt *Options) clone() *Options {
2024-02-18 10:42:21 +00:00
clone := *opt
2024-02-18 10:42:21 +00:00
return &clone
2024-02-18 10:42:21 +00:00
}
// NewDialer returns a function that will be used as the default dialer
2024-02-18 10:42:21 +00:00
// when none is specified in Options.Dialer.
2024-02-18 10:42:21 +00:00
func NewDialer(opt *Options) func(context.Context, string, string) (net.Conn, error) {
2024-02-18 10:42:21 +00:00
return func(ctx context.Context, network, addr string) (net.Conn, error) {
2024-02-18 10:42:21 +00:00
netDialer := &net.Dialer{
Timeout: opt.DialTimeout,
2024-02-18 10:42:21 +00:00
KeepAlive: 5 * time.Minute,
}
2024-02-18 10:42:21 +00:00
if opt.TLSConfig == nil {
2024-02-18 10:42:21 +00:00
return netDialer.DialContext(ctx, network, addr)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return tls.DialWithDialer(netDialer, network, addr, opt.TLSConfig)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
// ParseURL parses an URL into Options that can be used to connect to Redis.
2024-02-18 10:42:21 +00:00
// Scheme is required.
2024-02-18 10:42:21 +00:00
// There are two connection types: by tcp socket and by unix socket.
2024-02-18 10:42:21 +00:00
// Tcp connection:
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// redis://<user>:<password>@<host>:<port>/<db_number>
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Unix connection:
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// unix://<user>:<password>@</path/to/redis.sock>?db=<db_number>
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Most Option fields can be set using query parameters, with the following restrictions:
2024-02-18 10:42:21 +00:00
// - field names are mapped using snake-case conversion: to set MaxRetries, use max_retries
2024-02-18 10:42:21 +00:00
// - only scalar type fields are supported (bool, int, time.Duration)
2024-02-18 10:42:21 +00:00
// - for time.Duration fields, values must be a valid input for time.ParseDuration();
2024-02-18 10:42:21 +00:00
// additionally a plain integer as value (i.e. without unit) is intepreted as seconds
2024-02-18 10:42:21 +00:00
// - to disable a duration field, use value less than or equal to 0; to use the default
2024-02-18 10:42:21 +00:00
// value, leave the value blank or remove the parameter
2024-02-18 10:42:21 +00:00
// - only the last value is interpreted if a parameter is given multiple times
2024-02-18 10:42:21 +00:00
// - fields "network", "addr", "username" and "password" can only be set using other
2024-02-18 10:42:21 +00:00
// URL attributes (scheme, host, userinfo, resp.), query paremeters using these
2024-02-18 10:42:21 +00:00
// names will be treated as unknown parameters
2024-02-18 10:42:21 +00:00
// - unknown parameter names will result in an error
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// Examples:
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// redis://user:password@localhost:6789/3?dial_timeout=3&db=1&read_timeout=6s&max_retries=2
2024-02-18 10:42:21 +00:00
// is equivalent to:
2024-02-18 10:42:21 +00:00
// &Options{
2024-02-18 10:42:21 +00:00
// Network: "tcp",
2024-02-18 10:42:21 +00:00
// Addr: "localhost:6789",
2024-02-18 10:42:21 +00:00
// DB: 1, // path "/3" was overridden by "&db=1"
2024-02-18 10:42:21 +00:00
// DialTimeout: 3 * time.Second, // no time unit = seconds
2024-02-18 10:42:21 +00:00
// ReadTimeout: 6 * time.Second,
2024-02-18 10:42:21 +00:00
// MaxRetries: 2,
2024-02-18 10:42:21 +00:00
// }
2024-02-18 10:42:21 +00:00
func ParseURL(redisURL string) (*Options, error) {
2024-02-18 10:42:21 +00:00
u, err := url.Parse(redisURL)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
switch u.Scheme {
2024-02-18 10:42:21 +00:00
case "redis", "rediss":
2024-02-18 10:42:21 +00:00
return setupTCPConn(u)
2024-02-18 10:42:21 +00:00
case "unix":
2024-02-18 10:42:21 +00:00
return setupUnixConn(u)
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
return nil, fmt.Errorf("redis: invalid URL scheme: %s", u.Scheme)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
func setupTCPConn(u *url.URL) (*Options, error) {
2024-02-18 10:42:21 +00:00
o := &Options{Network: "tcp"}
o.Username, o.Password = getUserPassword(u)
h, p := getHostPortWithDefaults(u)
2024-02-18 10:42:21 +00:00
o.Addr = net.JoinHostPort(h, p)
f := strings.FieldsFunc(u.Path, func(r rune) bool {
2024-02-18 10:42:21 +00:00
return r == '/'
2024-02-18 10:42:21 +00:00
})
2024-02-18 10:42:21 +00:00
switch len(f) {
2024-02-18 10:42:21 +00:00
case 0:
2024-02-18 10:42:21 +00:00
o.DB = 0
2024-02-18 10:42:21 +00:00
case 1:
2024-02-18 10:42:21 +00:00
var err error
2024-02-18 10:42:21 +00:00
if o.DB, err = strconv.Atoi(f[0]); err != nil {
2024-02-18 10:42:21 +00:00
return nil, fmt.Errorf("redis: invalid database number: %q", f[0])
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
return nil, fmt.Errorf("redis: invalid URL path: %s", u.Path)
2024-02-18 10:42:21 +00:00
}
if u.Scheme == "rediss" {
2024-02-18 10:42:21 +00:00
o.TLSConfig = &tls.Config{
2024-02-18 10:42:21 +00:00
ServerName: h,
2024-02-18 10:42:21 +00:00
MinVersion: tls.VersionTLS12,
}
2024-02-18 10:42:21 +00:00
}
return setupConnParams(u, o)
2024-02-18 10:42:21 +00:00
}
// getHostPortWithDefaults is a helper function that splits the url into
2024-02-18 10:42:21 +00:00
// a host and a port. If the host is missing, it defaults to localhost
2024-02-18 10:42:21 +00:00
// and if the port is missing, it defaults to 6379.
2024-02-18 10:42:21 +00:00
func getHostPortWithDefaults(u *url.URL) (string, string) {
2024-02-18 10:42:21 +00:00
host, port, err := net.SplitHostPort(u.Host)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
host = u.Host
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if host == "" {
2024-02-18 10:42:21 +00:00
host = "localhost"
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if port == "" {
2024-02-18 10:42:21 +00:00
port = "6379"
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return host, port
2024-02-18 10:42:21 +00:00
}
func setupUnixConn(u *url.URL) (*Options, error) {
2024-02-18 10:42:21 +00:00
o := &Options{
2024-02-18 10:42:21 +00:00
Network: "unix",
}
if strings.TrimSpace(u.Path) == "" { // path is required with unix connection
2024-02-18 10:42:21 +00:00
return nil, errors.New("redis: empty unix socket path")
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
o.Addr = u.Path
2024-02-18 10:42:21 +00:00
o.Username, o.Password = getUserPassword(u)
2024-02-18 10:42:21 +00:00
return setupConnParams(u, o)
2024-02-18 10:42:21 +00:00
}
type queryOptions struct {
q url.Values
2024-02-18 10:42:21 +00:00
err error
}
func (o *queryOptions) has(name string) bool {
2024-02-18 10:42:21 +00:00
return len(o.q[name]) > 0
2024-02-18 10:42:21 +00:00
}
func (o *queryOptions) string(name string) string {
2024-02-18 10:42:21 +00:00
vs := o.q[name]
2024-02-18 10:42:21 +00:00
if len(vs) == 0 {
2024-02-18 10:42:21 +00:00
return ""
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
delete(o.q, name) // enable detection of unknown parameters
2024-02-18 10:42:21 +00:00
return vs[len(vs)-1]
2024-02-18 10:42:21 +00:00
}
func (o *queryOptions) strings(name string) []string {
2024-02-18 10:42:21 +00:00
vs := o.q[name]
2024-02-18 10:42:21 +00:00
delete(o.q, name)
2024-02-18 10:42:21 +00:00
return vs
2024-02-18 10:42:21 +00:00
}
func (o *queryOptions) int(name string) int {
2024-02-18 10:42:21 +00:00
s := o.string(name)
2024-02-18 10:42:21 +00:00
if s == "" {
2024-02-18 10:42:21 +00:00
return 0
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
i, err := strconv.Atoi(s)
2024-02-18 10:42:21 +00:00
if err == nil {
2024-02-18 10:42:21 +00:00
return i
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if o.err == nil {
2024-02-18 10:42:21 +00:00
o.err = fmt.Errorf("redis: invalid %s number: %s", name, err)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return 0
2024-02-18 10:42:21 +00:00
}
func (o *queryOptions) duration(name string) time.Duration {
2024-02-18 10:42:21 +00:00
s := o.string(name)
2024-02-18 10:42:21 +00:00
if s == "" {
2024-02-18 10:42:21 +00:00
return 0
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
// try plain number first
2024-02-18 10:42:21 +00:00
if i, err := strconv.Atoi(s); err == nil {
2024-02-18 10:42:21 +00:00
if i <= 0 {
2024-02-18 10:42:21 +00:00
// disable timeouts
2024-02-18 10:42:21 +00:00
return -1
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return time.Duration(i) * time.Second
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
dur, err := time.ParseDuration(s)
2024-02-18 10:42:21 +00:00
if err == nil {
2024-02-18 10:42:21 +00:00
return dur
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if o.err == nil {
2024-02-18 10:42:21 +00:00
o.err = fmt.Errorf("redis: invalid %s duration: %w", name, err)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return 0
2024-02-18 10:42:21 +00:00
}
func (o *queryOptions) bool(name string) bool {
2024-02-18 10:42:21 +00:00
switch s := o.string(name); s {
2024-02-18 10:42:21 +00:00
case "true", "1":
2024-02-18 10:42:21 +00:00
return true
2024-02-18 10:42:21 +00:00
case "false", "0", "":
2024-02-18 10:42:21 +00:00
return false
2024-02-18 10:42:21 +00:00
default:
2024-02-18 10:42:21 +00:00
if o.err == nil {
2024-02-18 10:42:21 +00:00
o.err = fmt.Errorf("redis: invalid %s boolean: expected true/false/1/0 or an empty string, got %q", name, s)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return false
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
func (o *queryOptions) remaining() []string {
2024-02-18 10:42:21 +00:00
if len(o.q) == 0 {
2024-02-18 10:42:21 +00:00
return nil
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
keys := make([]string, 0, len(o.q))
2024-02-18 10:42:21 +00:00
for k := range o.q {
2024-02-18 10:42:21 +00:00
keys = append(keys, k)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
sort.Strings(keys)
2024-02-18 10:42:21 +00:00
return keys
2024-02-18 10:42:21 +00:00
}
// setupConnParams converts query parameters in u to option value in o.
2024-02-18 10:42:21 +00:00
func setupConnParams(u *url.URL, o *Options) (*Options, error) {
2024-02-18 10:42:21 +00:00
q := queryOptions{q: u.Query()}
// compat: a future major release may use q.int("db")
2024-02-18 10:42:21 +00:00
if tmp := q.string("db"); tmp != "" {
2024-02-18 10:42:21 +00:00
db, err := strconv.Atoi(tmp)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, fmt.Errorf("redis: invalid database number: %w", err)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
o.DB = db
2024-02-18 10:42:21 +00:00
}
o.Protocol = q.int("protocol")
2024-02-18 10:42:21 +00:00
o.ClientName = q.string("client_name")
2024-02-18 10:42:21 +00:00
o.MaxRetries = q.int("max_retries")
2024-02-18 10:42:21 +00:00
o.MinRetryBackoff = q.duration("min_retry_backoff")
2024-02-18 10:42:21 +00:00
o.MaxRetryBackoff = q.duration("max_retry_backoff")
2024-02-18 10:42:21 +00:00
o.DialTimeout = q.duration("dial_timeout")
2024-02-18 10:42:21 +00:00
o.ReadTimeout = q.duration("read_timeout")
2024-02-18 10:42:21 +00:00
o.WriteTimeout = q.duration("write_timeout")
2024-02-18 10:42:21 +00:00
o.PoolFIFO = q.bool("pool_fifo")
2024-02-18 10:42:21 +00:00
o.PoolSize = q.int("pool_size")
2024-02-18 10:42:21 +00:00
o.PoolTimeout = q.duration("pool_timeout")
2024-02-18 10:42:21 +00:00
o.MinIdleConns = q.int("min_idle_conns")
2024-02-18 10:42:21 +00:00
o.MaxIdleConns = q.int("max_idle_conns")
2024-02-18 10:42:21 +00:00
o.MaxActiveConns = q.int("max_active_conns")
2024-02-18 10:42:21 +00:00
if q.has("conn_max_idle_time") {
2024-02-18 10:42:21 +00:00
o.ConnMaxIdleTime = q.duration("conn_max_idle_time")
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
o.ConnMaxIdleTime = q.duration("idle_timeout")
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if q.has("conn_max_lifetime") {
2024-02-18 10:42:21 +00:00
o.ConnMaxLifetime = q.duration("conn_max_lifetime")
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
o.ConnMaxLifetime = q.duration("max_conn_age")
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
if q.err != nil {
2024-02-18 10:42:21 +00:00
return nil, q.err
2024-02-18 10:42:21 +00:00
}
// any parameters left?
2024-02-18 10:42:21 +00:00
if r := q.remaining(); len(r) > 0 {
2024-02-18 10:42:21 +00:00
return nil, fmt.Errorf("redis: unexpected option: %s", strings.Join(r, ", "))
2024-02-18 10:42:21 +00:00
}
return o, nil
2024-02-18 10:42:21 +00:00
}
func getUserPassword(u *url.URL) (string, string) {
2024-02-18 10:42:21 +00:00
var user, password string
2024-02-18 10:42:21 +00:00
if u.User != nil {
2024-02-18 10:42:21 +00:00
user = u.User.Username()
2024-02-18 10:42:21 +00:00
if p, ok := u.User.Password(); ok {
2024-02-18 10:42:21 +00:00
password = p
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return user, password
2024-02-18 10:42:21 +00:00
}
func newConnPool(
2024-02-18 10:42:21 +00:00
opt *Options,
2024-02-18 10:42:21 +00:00
dialer func(ctx context.Context, network, addr string) (net.Conn, error),
2024-02-18 10:42:21 +00:00
) *pool.ConnPool {
2024-02-18 10:42:21 +00:00
return pool.NewConnPool(&pool.Options{
2024-02-18 10:42:21 +00:00
Dialer: func(ctx context.Context) (net.Conn, error) {
2024-02-18 10:42:21 +00:00
return dialer(ctx, opt.Network, opt.Addr)
2024-02-18 10:42:21 +00:00
},
PoolFIFO: opt.PoolFIFO,
PoolSize: opt.PoolSize,
PoolTimeout: opt.PoolTimeout,
MinIdleConns: opt.MinIdleConns,
MaxIdleConns: opt.MaxIdleConns,
MaxActiveConns: opt.MaxActiveConns,
2024-02-18 10:42:21 +00:00
ConnMaxIdleTime: opt.ConnMaxIdleTime,
2024-02-18 10:42:21 +00:00
ConnMaxLifetime: opt.ConnMaxLifetime,
})
2024-02-18 10:42:21 +00:00
}