mirror of
				https://github.com/redis/go-redis.git
				synced 2025-11-04 02:33:24 +03:00 
			
		
		
		
	* e2e wip * cleanup * remove unused fault injector mock * errChan in test * remove log messages tests * cleanup log messages * s/hitless/maintnotifications/ * fix moving when none * better logs * test with second client after action has started * Fixes Signed-off-by: Elena Kolevska <elena@kolevska.com> * Test fix Signed-off-by: Elena Kolevska <elena@kolevska.com> * feat(e2e-test): Extended e2e tests * imroved e2e test resiliency --------- Signed-off-by: Elena Kolevska <elena@kolevska.com> Co-authored-by: Elena Kolevska <elena@kolevska.com> Co-authored-by: Elena Kolevska <elena-kolevska@users.noreply.github.com> Co-authored-by: Hristo Temelski <hristo.temelski@redis.com>
		
			
				
	
	
		
			352 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			352 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package redis
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"crypto/tls"
 | 
						|
	"net"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/redis/go-redis/v9/auth"
 | 
						|
	"github.com/redis/go-redis/v9/maintnotifications"
 | 
						|
)
 | 
						|
 | 
						|
// 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
 | 
						|
 | 
						|
	// ClientName will execute the `CLIENT SETNAME ClientName` command for each conn.
 | 
						|
	ClientName 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
 | 
						|
 | 
						|
	Protocol int
 | 
						|
	Username string
 | 
						|
	Password string
 | 
						|
	// CredentialsProvider allows the username and password to be updated
 | 
						|
	// before reconnecting. It should return the current username and password.
 | 
						|
	CredentialsProvider func() (username string, password string)
 | 
						|
 | 
						|
	// CredentialsProviderContext is an enhanced parameter of CredentialsProvider,
 | 
						|
	// done to maintain API compatibility. In the future,
 | 
						|
	// there might be a merge between CredentialsProviderContext and CredentialsProvider.
 | 
						|
	// There will be a conflict between them; if CredentialsProviderContext exists, we will ignore CredentialsProvider.
 | 
						|
	CredentialsProviderContext func(ctx context.Context) (username string, password string, err error)
 | 
						|
 | 
						|
	// StreamingCredentialsProvider is used to retrieve the credentials
 | 
						|
	// for the connection from an external source. Those credentials may change
 | 
						|
	// during the connection lifetime. This is useful for managed identity
 | 
						|
	// scenarios where the credentials are retrieved from an external source.
 | 
						|
	//
 | 
						|
	// Currently, this is a placeholder for the future implementation.
 | 
						|
	StreamingCredentialsProvider auth.StreamingCredentialsProvider
 | 
						|
 | 
						|
	SentinelUsername string
 | 
						|
	SentinelPassword string
 | 
						|
 | 
						|
	MaxRetries      int
 | 
						|
	MinRetryBackoff time.Duration
 | 
						|
	MaxRetryBackoff time.Duration
 | 
						|
 | 
						|
	DialTimeout           time.Duration
 | 
						|
	ReadTimeout           time.Duration
 | 
						|
	WriteTimeout          time.Duration
 | 
						|
	ContextTimeoutEnabled bool
 | 
						|
 | 
						|
	// ReadBufferSize is the size of the bufio.Reader buffer for each connection.
 | 
						|
	// Larger buffers can improve performance for commands that return large responses.
 | 
						|
	// Smaller buffers can improve memory usage for larger pools.
 | 
						|
	//
 | 
						|
	// default: 32KiB (32768 bytes)
 | 
						|
	ReadBufferSize int
 | 
						|
 | 
						|
	// WriteBufferSize is the size of the bufio.Writer buffer for each connection.
 | 
						|
	// Larger buffers can improve performance for large pipelines and commands with many arguments.
 | 
						|
	// Smaller buffers can improve memory usage for larger pools.
 | 
						|
	//
 | 
						|
	// default: 32KiB (32768 bytes)
 | 
						|
	WriteBufferSize int
 | 
						|
 | 
						|
	// PoolFIFO uses FIFO mode for each node connection pool GET/PUT (default LIFO).
 | 
						|
	PoolFIFO bool
 | 
						|
 | 
						|
	PoolSize        int
 | 
						|
	PoolTimeout     time.Duration
 | 
						|
	MinIdleConns    int
 | 
						|
	MaxIdleConns    int
 | 
						|
	MaxActiveConns  int
 | 
						|
	ConnMaxIdleTime time.Duration
 | 
						|
	ConnMaxLifetime time.Duration
 | 
						|
 | 
						|
	TLSConfig *tls.Config
 | 
						|
 | 
						|
	// Only cluster clients.
 | 
						|
 | 
						|
	MaxRedirects   int
 | 
						|
	ReadOnly       bool
 | 
						|
	RouteByLatency bool
 | 
						|
	RouteRandomly  bool
 | 
						|
 | 
						|
	// MasterName is the sentinel master name.
 | 
						|
	// Only for failover clients.
 | 
						|
	MasterName string
 | 
						|
 | 
						|
	// DisableIndentity - Disable set-lib on connect.
 | 
						|
	//
 | 
						|
	// default: false
 | 
						|
	//
 | 
						|
	// Deprecated: Use DisableIdentity instead.
 | 
						|
	DisableIndentity bool
 | 
						|
 | 
						|
	// DisableIdentity is used to disable CLIENT SETINFO command on connect.
 | 
						|
	//
 | 
						|
	// default: false
 | 
						|
	DisableIdentity bool
 | 
						|
 | 
						|
	IdentitySuffix string
 | 
						|
 | 
						|
	// FailingTimeoutSeconds is the timeout in seconds for marking a cluster node as failing.
 | 
						|
	// When a node is marked as failing, it will be avoided for this duration.
 | 
						|
	// Only applies to cluster clients. Default is 15 seconds.
 | 
						|
	FailingTimeoutSeconds int
 | 
						|
 | 
						|
	UnstableResp3 bool
 | 
						|
 | 
						|
	// IsClusterMode can be used when only one Addrs is provided (e.g. Elasticache supports setting up cluster mode with configuration endpoint).
 | 
						|
	IsClusterMode bool
 | 
						|
 | 
						|
	// MaintNotificationsConfig provides configuration for maintnotifications upgrades.
 | 
						|
	MaintNotificationsConfig *maintnotifications.Config
 | 
						|
}
 | 
						|
 | 
						|
// 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,
 | 
						|
		ClientName: o.ClientName,
 | 
						|
		Dialer:     o.Dialer,
 | 
						|
		OnConnect:  o.OnConnect,
 | 
						|
 | 
						|
		Protocol:                     o.Protocol,
 | 
						|
		Username:                     o.Username,
 | 
						|
		Password:                     o.Password,
 | 
						|
		CredentialsProvider:          o.CredentialsProvider,
 | 
						|
		CredentialsProviderContext:   o.CredentialsProviderContext,
 | 
						|
		StreamingCredentialsProvider: o.StreamingCredentialsProvider,
 | 
						|
 | 
						|
		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,
 | 
						|
		ContextTimeoutEnabled: o.ContextTimeoutEnabled,
 | 
						|
 | 
						|
		ReadBufferSize:  o.ReadBufferSize,
 | 
						|
		WriteBufferSize: o.WriteBufferSize,
 | 
						|
 | 
						|
		PoolFIFO: o.PoolFIFO,
 | 
						|
 | 
						|
		PoolSize:        o.PoolSize,
 | 
						|
		PoolTimeout:     o.PoolTimeout,
 | 
						|
		MinIdleConns:    o.MinIdleConns,
 | 
						|
		MaxIdleConns:    o.MaxIdleConns,
 | 
						|
		MaxActiveConns:  o.MaxActiveConns,
 | 
						|
		ConnMaxIdleTime: o.ConnMaxIdleTime,
 | 
						|
		ConnMaxLifetime: o.ConnMaxLifetime,
 | 
						|
 | 
						|
		TLSConfig: o.TLSConfig,
 | 
						|
 | 
						|
		DisableIdentity:          o.DisableIdentity,
 | 
						|
		DisableIndentity:         o.DisableIndentity,
 | 
						|
		IdentitySuffix:           o.IdentitySuffix,
 | 
						|
		FailingTimeoutSeconds:    o.FailingTimeoutSeconds,
 | 
						|
		UnstableResp3:            o.UnstableResp3,
 | 
						|
		MaintNotificationsConfig: o.MaintNotificationsConfig,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// 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,
 | 
						|
		ClientName:    o.ClientName,
 | 
						|
 | 
						|
		Dialer:    o.Dialer,
 | 
						|
		OnConnect: o.OnConnect,
 | 
						|
 | 
						|
		DB:                           o.DB,
 | 
						|
		Protocol:                     o.Protocol,
 | 
						|
		Username:                     o.Username,
 | 
						|
		Password:                     o.Password,
 | 
						|
		CredentialsProvider:          o.CredentialsProvider,
 | 
						|
		CredentialsProviderContext:   o.CredentialsProviderContext,
 | 
						|
		StreamingCredentialsProvider: o.StreamingCredentialsProvider,
 | 
						|
 | 
						|
		SentinelUsername: o.SentinelUsername,
 | 
						|
		SentinelPassword: o.SentinelPassword,
 | 
						|
 | 
						|
		RouteByLatency: o.RouteByLatency,
 | 
						|
		RouteRandomly:  o.RouteRandomly,
 | 
						|
 | 
						|
		MaxRetries:      o.MaxRetries,
 | 
						|
		MinRetryBackoff: o.MinRetryBackoff,
 | 
						|
		MaxRetryBackoff: o.MaxRetryBackoff,
 | 
						|
 | 
						|
		DialTimeout:           o.DialTimeout,
 | 
						|
		ReadTimeout:           o.ReadTimeout,
 | 
						|
		WriteTimeout:          o.WriteTimeout,
 | 
						|
		ContextTimeoutEnabled: o.ContextTimeoutEnabled,
 | 
						|
 | 
						|
		ReadBufferSize:  o.ReadBufferSize,
 | 
						|
		WriteBufferSize: o.WriteBufferSize,
 | 
						|
 | 
						|
		PoolFIFO:        o.PoolFIFO,
 | 
						|
		PoolSize:        o.PoolSize,
 | 
						|
		PoolTimeout:     o.PoolTimeout,
 | 
						|
		MinIdleConns:    o.MinIdleConns,
 | 
						|
		MaxIdleConns:    o.MaxIdleConns,
 | 
						|
		MaxActiveConns:  o.MaxActiveConns,
 | 
						|
		ConnMaxIdleTime: o.ConnMaxIdleTime,
 | 
						|
		ConnMaxLifetime: o.ConnMaxLifetime,
 | 
						|
 | 
						|
		TLSConfig: o.TLSConfig,
 | 
						|
 | 
						|
		ReplicaOnly: o.ReadOnly,
 | 
						|
 | 
						|
		DisableIdentity:  o.DisableIdentity,
 | 
						|
		DisableIndentity: o.DisableIndentity,
 | 
						|
		IdentitySuffix:   o.IdentitySuffix,
 | 
						|
		UnstableResp3:    o.UnstableResp3,
 | 
						|
		// Note: MaintNotificationsConfig not supported for FailoverOptions
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// 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,
 | 
						|
		ClientName: o.ClientName,
 | 
						|
		Dialer:     o.Dialer,
 | 
						|
		OnConnect:  o.OnConnect,
 | 
						|
 | 
						|
		DB:                           o.DB,
 | 
						|
		Protocol:                     o.Protocol,
 | 
						|
		Username:                     o.Username,
 | 
						|
		Password:                     o.Password,
 | 
						|
		CredentialsProvider:          o.CredentialsProvider,
 | 
						|
		CredentialsProviderContext:   o.CredentialsProviderContext,
 | 
						|
		StreamingCredentialsProvider: o.StreamingCredentialsProvider,
 | 
						|
 | 
						|
		MaxRetries:      o.MaxRetries,
 | 
						|
		MinRetryBackoff: o.MinRetryBackoff,
 | 
						|
		MaxRetryBackoff: o.MaxRetryBackoff,
 | 
						|
 | 
						|
		DialTimeout:           o.DialTimeout,
 | 
						|
		ReadTimeout:           o.ReadTimeout,
 | 
						|
		WriteTimeout:          o.WriteTimeout,
 | 
						|
		ContextTimeoutEnabled: o.ContextTimeoutEnabled,
 | 
						|
 | 
						|
		ReadBufferSize:  o.ReadBufferSize,
 | 
						|
		WriteBufferSize: o.WriteBufferSize,
 | 
						|
 | 
						|
		PoolFIFO:        o.PoolFIFO,
 | 
						|
		PoolSize:        o.PoolSize,
 | 
						|
		PoolTimeout:     o.PoolTimeout,
 | 
						|
		MinIdleConns:    o.MinIdleConns,
 | 
						|
		MaxIdleConns:    o.MaxIdleConns,
 | 
						|
		MaxActiveConns:  o.MaxActiveConns,
 | 
						|
		ConnMaxIdleTime: o.ConnMaxIdleTime,
 | 
						|
		ConnMaxLifetime: o.ConnMaxLifetime,
 | 
						|
 | 
						|
		TLSConfig: o.TLSConfig,
 | 
						|
 | 
						|
		DisableIdentity:          o.DisableIdentity,
 | 
						|
		DisableIndentity:         o.DisableIndentity,
 | 
						|
		IdentitySuffix:           o.IdentitySuffix,
 | 
						|
		UnstableResp3:            o.UnstableResp3,
 | 
						|
		MaintNotificationsConfig: o.MaintNotificationsConfig,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// --------------------------------------------------------------------
 | 
						|
 | 
						|
// UniversalClient is an abstract client which - based on the provided options -
 | 
						|
// represents either a ClusterClient, a FailoverClient, or a single-node Client.
 | 
						|
// This can be useful for testing cluster-specific applications locally or having different
 | 
						|
// clients in different environments.
 | 
						|
type UniversalClient interface {
 | 
						|
	Cmdable
 | 
						|
	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
 | 
						|
	SSubscribe(ctx context.Context, channels ...string) *PubSub
 | 
						|
	Close() error
 | 
						|
	PoolStats() *PoolStats
 | 
						|
}
 | 
						|
 | 
						|
var (
 | 
						|
	_ UniversalClient = (*Client)(nil)
 | 
						|
	_ UniversalClient = (*ClusterClient)(nil)
 | 
						|
	_ UniversalClient = (*Ring)(nil)
 | 
						|
)
 | 
						|
 | 
						|
// NewUniversalClient returns a new multi client. The type of the returned client depends
 | 
						|
// on the following conditions:
 | 
						|
//
 | 
						|
//  1. If the MasterName option is specified with RouteByLatency, RouteRandomly or IsClusterMode,
 | 
						|
//     a FailoverClusterClient is returned.
 | 
						|
//  2. If the MasterName option is specified without RouteByLatency, RouteRandomly or IsClusterMode,
 | 
						|
//     a sentinel-backed FailoverClient is returned.
 | 
						|
//  3. If the number of Addrs is two or more, or IsClusterMode option is specified,
 | 
						|
//     a ClusterClient is returned.
 | 
						|
//  4. Otherwise, a single-node Client is returned.
 | 
						|
func NewUniversalClient(opts *UniversalOptions) UniversalClient {
 | 
						|
	if opts == nil {
 | 
						|
		panic("redis: NewUniversalClient nil options")
 | 
						|
	}
 | 
						|
 | 
						|
	switch {
 | 
						|
	case opts.MasterName != "" && (opts.RouteByLatency || opts.RouteRandomly || opts.IsClusterMode):
 | 
						|
		return NewFailoverClusterClient(opts.Failover())
 | 
						|
	case opts.MasterName != "":
 | 
						|
		return NewFailoverClient(opts.Failover())
 | 
						|
	case len(opts.Addrs) > 1 || opts.IsClusterMode:
 | 
						|
		return NewClusterClient(opts.Cluster())
 | 
						|
	default:
 | 
						|
		return NewClient(opts.Simple())
 | 
						|
	}
 | 
						|
}
 |