package redis import ( "context" "crypto/tls" "net" "time" ) // UniversalOptions information is required by UniversalClient to establish // connections. type UniversalOptions struct { // Either a single address or a seed list of host:port addresses // of cluster/sentinel nodes. Addrs []string // Database to be selected after connecting to the server. // Only single-node and failover clients. DB int // Common options. Dialer func(ctx context.Context, network, addr string) (net.Conn, error) OnConnect func(ctx context.Context, cn *Conn) error Username string Password string SentinelPassword string MaxRetries int MinRetryBackoff time.Duration MaxRetryBackoff time.Duration DialTimeout time.Duration ReadTimeout time.Duration WriteTimeout time.Duration PoolSize int MinIdleConns int MaxConnAge time.Duration PoolTimeout time.Duration IdleTimeout time.Duration IdleCheckFrequency time.Duration TLSConfig *tls.Config // Only cluster clients. MaxRedirects int ReadOnly bool RouteByLatency bool RouteRandomly bool // The sentinel master name. // Only failover clients. MasterName string } // Cluster returns cluster options created from the universal options. func (o *UniversalOptions) Cluster() *ClusterOptions { if len(o.Addrs) == 0 { o.Addrs = []string{"127.0.0.1:6379"} } return &ClusterOptions{ Addrs: o.Addrs, Dialer: o.Dialer, OnConnect: o.OnConnect, Username: o.Username, Password: o.Password, MaxRedirects: o.MaxRedirects, ReadOnly: o.ReadOnly, RouteByLatency: o.RouteByLatency, RouteRandomly: o.RouteRandomly, MaxRetries: o.MaxRetries, MinRetryBackoff: o.MinRetryBackoff, MaxRetryBackoff: o.MaxRetryBackoff, DialTimeout: o.DialTimeout, ReadTimeout: o.ReadTimeout, WriteTimeout: o.WriteTimeout, PoolSize: o.PoolSize, MinIdleConns: o.MinIdleConns, MaxConnAge: o.MaxConnAge, PoolTimeout: o.PoolTimeout, IdleTimeout: o.IdleTimeout, IdleCheckFrequency: o.IdleCheckFrequency, TLSConfig: o.TLSConfig, } } // Failover returns failover options created from the universal options. func (o *UniversalOptions) Failover() *FailoverOptions { if len(o.Addrs) == 0 { o.Addrs = []string{"127.0.0.1:26379"} } return &FailoverOptions{ SentinelAddrs: o.Addrs, MasterName: o.MasterName, Dialer: o.Dialer, OnConnect: o.OnConnect, DB: o.DB, Username: o.Username, Password: o.Password, SentinelPassword: o.SentinelPassword, MaxRetries: o.MaxRetries, MinRetryBackoff: o.MinRetryBackoff, MaxRetryBackoff: o.MaxRetryBackoff, DialTimeout: o.DialTimeout, ReadTimeout: o.ReadTimeout, WriteTimeout: o.WriteTimeout, PoolSize: o.PoolSize, MinIdleConns: o.MinIdleConns, MaxConnAge: o.MaxConnAge, PoolTimeout: o.PoolTimeout, IdleTimeout: o.IdleTimeout, IdleCheckFrequency: o.IdleCheckFrequency, TLSConfig: o.TLSConfig, } } // Simple returns basic options created from the universal options. func (o *UniversalOptions) Simple() *Options { addr := "127.0.0.1:6379" if len(o.Addrs) > 0 { addr = o.Addrs[0] } return &Options{ Addr: addr, Dialer: o.Dialer, OnConnect: o.OnConnect, DB: o.DB, Username: o.Username, Password: o.Password, MaxRetries: o.MaxRetries, MinRetryBackoff: o.MinRetryBackoff, MaxRetryBackoff: o.MaxRetryBackoff, DialTimeout: o.DialTimeout, ReadTimeout: o.ReadTimeout, WriteTimeout: o.WriteTimeout, PoolSize: o.PoolSize, MinIdleConns: o.MinIdleConns, MaxConnAge: o.MaxConnAge, PoolTimeout: o.PoolTimeout, IdleTimeout: o.IdleTimeout, IdleCheckFrequency: o.IdleCheckFrequency, TLSConfig: o.TLSConfig, } } // -------------------------------------------------------------------- // UniversalClient is an abstract client which - based on the provided options - // can connect to either clusters, or sentinel-backed failover instances // or simple single-instance servers. This can be useful for testing // cluster-specific applications locally. type UniversalClient interface { Cmdable Context() context.Context AddHook(Hook) Watch(ctx context.Context, fn func(*Tx) error, keys ...string) error Do(ctx context.Context, args ...interface{}) *Cmd Process(ctx context.Context, cmd Cmder) error Subscribe(ctx context.Context, channels ...string) *PubSub PSubscribe(ctx context.Context, channels ...string) *PubSub Close() error } var ( _ UniversalClient = (*Client)(nil) _ UniversalClient = (*ClusterClient)(nil) _ UniversalClient = (*Ring)(nil) ) // NewUniversalClient returns a new multi client. The type of client returned depends // on the following three conditions: // // 1. if a MasterName is passed a sentinel-backed FailoverClient will be returned // 2. if the number of Addrs is two or more, a ClusterClient will be returned // 3. otherwise, a single-node redis Client will be returned. func NewUniversalClient(opts *UniversalOptions) UniversalClient { if opts.MasterName != "" { return NewFailoverClient(opts.Failover()) } else if len(opts.Addrs) > 1 { return NewClusterClient(opts.Cluster()) } return NewClient(opts.Simple()) }