diff --git a/backends/redis.go b/backends/redis.go index d7128555..938361f2 100644 --- a/backends/redis.go +++ b/backends/redis.go @@ -3,10 +3,11 @@ package backends import ( "context" "crypto/tls" + "reflect" "strconv" "time" - "github.com/go-redis/redis/v8" + "github.com/redis/go-redis/v9" "github.com/prebid/prebid-cache/config" "github.com/prebid/prebid-cache/utils" log "github.com/sirupsen/logrus" @@ -21,21 +22,35 @@ type RedisDB interface { } // RedisDBClient is a wrapper for the Redis client that implements -// the RedisDB interface +// the RedisDB interface. It can work with both standalone and cluster clients. type RedisDBClient struct { - client *redis.Client + client interface{} // Can be either *redis.Client or *redis.ClusterClient } // Get returns the value associated with the provided `key` parameter func (db RedisDBClient) Get(ctx context.Context, key string) (string, error) { - return db.client.Get(ctx, key).Result() + switch c := db.client.(type) { + case *redis.Client: + return c.Get(ctx, key).Result() + case *redis.ClusterClient: + return c.Get(ctx, key).Result() + default: + return "", utils.NewPBCError(utils.KEY_NOT_FOUND) + } } // Put will set 'key' to hold string 'value' if 'key' does not exist in the redis storage. // When key already holds a value, no operation is performed. That's the reason this adapter // uses the 'github.com/go-redis/redis's library SetNX. SetNX is short for "SET if Not eXists". func (db RedisDBClient) Put(ctx context.Context, key, value string, ttlSeconds int) (bool, error) { - return db.client.SetNX(ctx, key, value, time.Duration(ttlSeconds)*time.Second).Result() + switch c := db.client.(type) { + case *redis.Client: + return c.SetNX(ctx, key, value, time.Duration(ttlSeconds)*time.Second).Result() + case *redis.ClusterClient: + return c.SetNX(ctx, key, value, time.Duration(ttlSeconds)*time.Second).Result() + default: + return false, utils.NewPBCError(utils.KEY_NOT_FOUND) + } } // RedisBackend when initialized will instantiate and configure the Redis client. It implements @@ -47,42 +62,162 @@ type RedisBackend struct { // NewRedisBackend initializes the redis client and pings to make sure connection was successful func NewRedisBackend(cfg config.Redis, ctx context.Context) *RedisBackend { - constr := cfg.Host + ":" + strconv.Itoa(cfg.Port) + var redisClient RedisDBClient - options := &redis.Options{ - Addr: constr, - Password: cfg.Password, - DB: cfg.Db, - } + if cfg.Cluster.Enabled { + // Cluster mode + clusterOptions := &redis.ClusterOptions{ + Addrs: cfg.Cluster.Hosts, + Password: cfg.Password, + // Note: DB selection is not supported in cluster mode + } + + // Apply performance configuration for cluster + applyClusterPerformanceOptions(clusterOptions, cfg) - if cfg.TLS.Enabled { - options = &redis.Options{ + if cfg.TLS.Enabled { + clusterOptions.TLSConfig = &tls.Config{ + InsecureSkipVerify: cfg.TLS.InsecureSkipVerify, + } + } + + clusterClient := redis.NewClusterClient(clusterOptions) + + // Test cluster connection + _, err := clusterClient.Ping(ctx).Result() + if err != nil { + log.Fatalf("Error creating Redis cluster backend: %v", err) + panic("RedisBackend failure. This shouldn't happen.") + } + + log.Infof("Connected to Redis cluster with hosts: %v", cfg.Cluster.Hosts) + redisClient = RedisDBClient{client: clusterClient} + } else { + // Single-node mode + constr := cfg.Host + ":" + strconv.Itoa(cfg.Port) + + options := &redis.Options{ Addr: constr, Password: cfg.Password, DB: cfg.Db, - TLSConfig: &tls.Config{ + } + + // Apply performance configuration for single-node + applySingleNodePerformanceOptions(options, cfg) + + if cfg.TLS.Enabled { + options.TLSConfig = &tls.Config{ InsecureSkipVerify: cfg.TLS.InsecureSkipVerify, - }, + } } - } - redisClient := RedisDBClient{client: redis.NewClient(options)} + singleClient := redis.NewClient(options) - _, err := redisClient.client.Ping(ctx).Result() + // Test single-node connection + _, err := singleClient.Ping(ctx).Result() + if err != nil { + log.Fatalf("Error creating Redis backend: %v", err) + panic("RedisBackend failure. This shouldn't happen.") + } - if err != nil { - log.Fatalf("Error creating Redis backend: %v", err) - panic("RedisBackend failure. This shouldn't happen.") + log.Infof("Connected to Redis at %s:%d", cfg.Host, cfg.Port) + redisClient = RedisDBClient{client: singleClient} } - log.Infof("Connected to Redis at %s:%d", cfg.Host, cfg.Port) - return &RedisBackend{ cfg: cfg, client: redisClient, } } +// applyPerformanceOptions applies performance tuning options to both single-node and cluster Redis clients +// This function uses reflection to set common fields on both redis.Options and redis.ClusterOptions +func applyPerformanceOptions(options interface{}, cfg config.Redis) { + optionsValue := reflect.ValueOf(options).Elem() + + // Apply pool configuration + if cfg.Pool != nil { + if cfg.Pool.Size > 0 { + if field := optionsValue.FieldByName("PoolSize"); field.IsValid() && field.CanSet() { + field.SetInt(int64(cfg.Pool.Size)) + } + } + if cfg.Pool.Timeout > 0 { + if field := optionsValue.FieldByName("PoolTimeout"); field.IsValid() && field.CanSet() { + field.Set(reflect.ValueOf(cfg.Pool.Timeout)) + } + } + if cfg.Pool.MinIdleConns > 0 { + if field := optionsValue.FieldByName("MinIdleConns"); field.IsValid() && field.CanSet() { + field.SetInt(int64(cfg.Pool.MinIdleConns)) + } + } + if cfg.Pool.MaxIdleConns > 0 { + if field := optionsValue.FieldByName("MaxIdleConns"); field.IsValid() && field.CanSet() { + field.SetInt(int64(cfg.Pool.MaxIdleConns)) + } + } + if cfg.Pool.ConnMaxIdleTime > 0 { + if field := optionsValue.FieldByName("ConnMaxIdleTime"); field.IsValid() && field.CanSet() { + field.Set(reflect.ValueOf(cfg.Pool.ConnMaxIdleTime)) + } + } + if cfg.Pool.ConnMaxLifetime > 0 { + if field := optionsValue.FieldByName("ConnMaxLifetime"); field.IsValid() && field.CanSet() { + field.Set(reflect.ValueOf(cfg.Pool.ConnMaxLifetime)) + } + } + } + + // Apply timeout configuration + if cfg.Timeouts != nil { + if cfg.Timeouts.DialTimeout > 0 { + if field := optionsValue.FieldByName("DialTimeout"); field.IsValid() && field.CanSet() { + field.Set(reflect.ValueOf(cfg.Timeouts.DialTimeout)) + } + } + if cfg.Timeouts.ReadTimeout > 0 { + if field := optionsValue.FieldByName("ReadTimeout"); field.IsValid() && field.CanSet() { + field.Set(reflect.ValueOf(cfg.Timeouts.ReadTimeout)) + } + } + if cfg.Timeouts.WriteTimeout > 0 { + if field := optionsValue.FieldByName("WriteTimeout"); field.IsValid() && field.CanSet() { + field.Set(reflect.ValueOf(cfg.Timeouts.WriteTimeout)) + } + } + } + + // Apply retry configuration + if cfg.Retry != nil { + if cfg.Retry.MaxRetries >= 0 { + if field := optionsValue.FieldByName("MaxRetries"); field.IsValid() && field.CanSet() { + field.SetInt(int64(cfg.Retry.MaxRetries)) + } + } + if cfg.Retry.MinRetryBackoff > 0 { + if field := optionsValue.FieldByName("MinRetryBackoff"); field.IsValid() && field.CanSet() { + field.Set(reflect.ValueOf(cfg.Retry.MinRetryBackoff)) + } + } + if cfg.Retry.MaxRetryBackoff > 0 { + if field := optionsValue.FieldByName("MaxRetryBackoff"); field.IsValid() && field.CanSet() { + field.Set(reflect.ValueOf(cfg.Retry.MaxRetryBackoff)) + } + } + } +} + +// applySingleNodePerformanceOptions applies performance tuning options to single-node Redis client +func applySingleNodePerformanceOptions(options *redis.Options, cfg config.Redis) { + applyPerformanceOptions(options, cfg) +} + +// applyClusterPerformanceOptions applies performance tuning options to cluster Redis client +func applyClusterPerformanceOptions(options *redis.ClusterOptions, cfg config.Redis) { + applyPerformanceOptions(options, cfg) +} + // Get calls the Redis client to return the value associated with the provided `key` // parameter and interprets its response. A `Nil` error reply of the Redis client means // the `key` does not exist. diff --git a/backends/redis_test.go b/backends/redis_test.go index 7238ae5c..61b721a5 100644 --- a/backends/redis_test.go +++ b/backends/redis_test.go @@ -5,7 +5,7 @@ import ( "errors" "testing" - "github.com/go-redis/redis/v8" + "github.com/redis/go-redis/v9" "github.com/prebid/prebid-cache/utils" "github.com/stretchr/testify/assert" ) diff --git a/config.yaml b/config.yaml index 277cee95..6b0c2e6d 100644 --- a/config.yaml +++ b/config.yaml @@ -13,7 +13,41 @@ request_limits: request_logging: referer_sampling_rate: 0.0 backend: - type: "memory" # Switch to be "aerospike", "cassandra", "memcache", "ignite" or "redis" for production. + type: memory + + # Redis backend configuration (when type: redis) + redis: + host: localhost + port: 6379 + password: "" + db: 0 + + # Optional: Redis performance tuning (uncomment to use) + # pool: + # size: 100 # Connection pool size (0 = default: 10 * GOMAXPROCS) + # timeout: 5s # Max time to wait for connection from pool + # min_idle_conns: 10 # Minimum idle connections to maintain + # max_idle_conns: 20 # Maximum idle connections + # conn_max_idle_time: 30m # Close connections after remaining idle + # conn_max_lifetime: 60m # Close connections after this lifetime + + # timeouts: + # dial_timeout: 5s # Timeout for establishing connections + # read_timeout: 3s # Timeout for socket reads + # write_timeout: 3s # Timeout for socket writes + + # retry: + # max_retries: 3 # Maximum number of retries + # min_retry_backoff: 8ms # Minimum backoff between retries + # max_retry_backoff: 512ms # Maximum backoff between retries + + tls: + enabled: false + insecure_skip_verify: false + + cluster: + enabled: false + hosts: [] # aerospike: # hosts: [ "aerospike.prebid.com" ] # port: 3000 @@ -24,7 +58,7 @@ backend: # memcache: # config_host: "" # Configuration endpoint for auto discovery. Replaced at docker build. # poll_interval_seconds: 30 # Node change polling interval when auto discovery is used -# hosts: "10.0.0.1:11211" # List of nodes when not using auto discovery. Can also use an array for multiple hosts. +# hosts: "10.0.0.1:11211" # List of nodes when not using auto discovery. Can also use an array for multiple hosts. # redis: # host: "127.0.0.1" # port: 6379 @@ -34,6 +68,9 @@ backend: # tls: # enabled: false # insecure_skip_verify: false +# cluster: +# enabled: false +# hosts: ["redis-node1:6379", "redis-node2:6379", "redis-node3:6379"] # ignite: # scheme: "http" # host: "127.0.0.1" @@ -47,7 +84,7 @@ backend: compression: type: "snappy" # Can also be "none" metrics: - type: "none" # Can also be "influx" + type: "none" # Can also be "influx" influx: host: "http://influx.prebid.com" database: "some-database" diff --git a/config/backends.go b/config/backends.go index 2f5c8694..bac69b65 100644 --- a/config/backends.go +++ b/config/backends.go @@ -3,6 +3,7 @@ package config import ( "errors" "fmt" + "time" log "github.com/sirupsen/logrus" ) @@ -151,12 +152,42 @@ func (cfg *Memcache) validateAndLog() error { } type Redis struct { - Host string `mapstructure:"host"` - Port int `mapstructure:"port"` - Password string `mapstructure:"password"` - Db int `mapstructure:"db"` - ExpirationMinutes int `mapstructure:"expiration"` - TLS RedisTLS `mapstructure:"tls"` + Host string `mapstructure:"host"` + Port int `mapstructure:"port"` + Password string `mapstructure:"password"` + Db int `mapstructure:"db"` + ExpirationMinutes int `mapstructure:"expiration"` + TLS RedisTLS `mapstructure:"tls"` + Cluster RedisCluster `mapstructure:"cluster"` + Pool *RedisPool `mapstructure:"pool"` + Timeouts *RedisTimeouts `mapstructure:"timeouts"` + Retry *RedisRetry `mapstructure:"retry"` +} + +type RedisPool struct { + Size int `mapstructure:"size"` + Timeout time.Duration `mapstructure:"timeout"` + MinIdleConns int `mapstructure:"min_idle_conns"` + MaxIdleConns int `mapstructure:"max_idle_conns"` + ConnMaxIdleTime time.Duration `mapstructure:"conn_max_idle_time"` + ConnMaxLifetime time.Duration `mapstructure:"conn_max_lifetime"` +} + +type RedisTimeouts struct { + DialTimeout time.Duration `mapstructure:"dial_timeout"` + ReadTimeout time.Duration `mapstructure:"read_timeout"` + WriteTimeout time.Duration `mapstructure:"write_timeout"` +} + +type RedisRetry struct { + MaxRetries int `mapstructure:"max_retries"` + MinRetryBackoff time.Duration `mapstructure:"min_retry_backoff"` + MaxRetryBackoff time.Duration `mapstructure:"max_retry_backoff"` +} + +type RedisCluster struct { + Enabled bool `mapstructure:"enabled"` + Hosts []string `mapstructure:"hosts"` } type RedisTLS struct { @@ -165,14 +196,99 @@ type RedisTLS struct { } func (cfg *Redis) validateAndLog() error { - log.Infof("config.backend.redis.host: %s", cfg.Host) - log.Infof("config.backend.redis.port: %d", cfg.Port) - log.Infof("config.backend.redis.db: %d", cfg.Db) + if cfg.Cluster.Enabled { + // Cluster mode validation and logging + if len(cfg.Cluster.Hosts) == 0 { + return errors.New("Redis cluster is enabled but no hosts are provided") + } + log.Infof("config.backend.redis.cluster.enabled: %t", cfg.Cluster.Enabled) + log.Infof("config.backend.redis.cluster.hosts: %v", cfg.Cluster.Hosts) + if cfg.Db != 0 { + log.Warnf("config.backend.redis.db: %d. Note that database selection is not supported in Redis cluster mode and will be ignored", cfg.Db) + } + } else { + // Single-node mode validation and logging + log.Infof("config.backend.redis.host: %s", cfg.Host) + log.Infof("config.backend.redis.port: %d", cfg.Port) + log.Infof("config.backend.redis.db: %d", cfg.Db) + } + + // Common configuration logging if cfg.ExpirationMinutes > 0 { log.Infof("config.backend.redis.expiration: %d. Note that this configuration option is being deprecated in favor of config.request_limits.max_ttl_seconds", cfg.ExpirationMinutes) } log.Infof("config.backend.redis.tls.enabled: %t", cfg.TLS.Enabled) log.Infof("config.backend.redis.tls.insecure_skip_verify: %t", cfg.TLS.InsecureSkipVerify) + + // Validate and log pool configuration + if cfg.Pool != nil { + if cfg.Pool.Size < 0 { + return errors.New("Redis pool size cannot be negative") + } + if cfg.Pool.MinIdleConns < 0 { + return errors.New("Redis pool min_idle_conns cannot be negative") + } + if cfg.Pool.MaxIdleConns < 0 { + return errors.New("Redis pool max_idle_conns cannot be negative") + } + if cfg.Pool.MinIdleConns > 0 && cfg.Pool.MaxIdleConns > 0 && cfg.Pool.MinIdleConns > cfg.Pool.MaxIdleConns { + return errors.New("Redis pool min_idle_conns cannot be greater than max_idle_conns") + } + if cfg.Pool.Timeout < 0 { + return errors.New("Redis pool timeout cannot be negative") + } + if cfg.Pool.ConnMaxIdleTime < 0 { + return errors.New("Redis pool conn_max_idle_time cannot be negative") + } + if cfg.Pool.ConnMaxLifetime < 0 { + return errors.New("Redis pool conn_max_lifetime cannot be negative") + } + + log.Infof("config.backend.redis.pool.size: %d", cfg.Pool.Size) + log.Infof("config.backend.redis.pool.timeout: %v", cfg.Pool.Timeout) + log.Infof("config.backend.redis.pool.min_idle_conns: %d", cfg.Pool.MinIdleConns) + log.Infof("config.backend.redis.pool.max_idle_conns: %d", cfg.Pool.MaxIdleConns) + log.Infof("config.backend.redis.pool.conn_max_idle_time: %v", cfg.Pool.ConnMaxIdleTime) + log.Infof("config.backend.redis.pool.conn_max_lifetime: %v", cfg.Pool.ConnMaxLifetime) + } + + // Validate and log timeout configuration + if cfg.Timeouts != nil { + if cfg.Timeouts.DialTimeout < 0 { + return errors.New("Redis dial_timeout cannot be negative") + } + if cfg.Timeouts.ReadTimeout < 0 { + return errors.New("Redis read_timeout cannot be negative") + } + if cfg.Timeouts.WriteTimeout < 0 { + return errors.New("Redis write_timeout cannot be negative") + } + + log.Infof("config.backend.redis.timeouts.dial_timeout: %v", cfg.Timeouts.DialTimeout) + log.Infof("config.backend.redis.timeouts.read_timeout: %v", cfg.Timeouts.ReadTimeout) + log.Infof("config.backend.redis.timeouts.write_timeout: %v", cfg.Timeouts.WriteTimeout) + } + + // Validate and log retry configuration + if cfg.Retry != nil { + if cfg.Retry.MaxRetries < 0 { + return errors.New("Redis max_retries cannot be negative") + } + if cfg.Retry.MinRetryBackoff < 0 { + return errors.New("Redis min_retry_backoff cannot be negative") + } + if cfg.Retry.MaxRetryBackoff < 0 { + return errors.New("Redis max_retry_backoff cannot be negative") + } + if cfg.Retry.MinRetryBackoff > 0 && cfg.Retry.MaxRetryBackoff > 0 && cfg.Retry.MinRetryBackoff > cfg.Retry.MaxRetryBackoff { + return errors.New("Redis min_retry_backoff cannot be greater than max_retry_backoff") + } + + log.Infof("config.backend.redis.retry.max_retries: %d", cfg.Retry.MaxRetries) + log.Infof("config.backend.redis.retry.min_retry_backoff: %v", cfg.Retry.MinRetryBackoff) + log.Infof("config.backend.redis.retry.max_retry_backoff: %v", cfg.Retry.MaxRetryBackoff) + } + return nil } diff --git a/config/config.go b/config/config.go index 6e714a7e..0f917641 100644 --- a/config/config.go +++ b/config/config.go @@ -71,6 +71,20 @@ func setConfigDefaults(v *viper.Viper) { v.SetDefault("backend.redis.expiration", utils.REDIS_DEFAULT_EXPIRATION_MINUTES) v.SetDefault("backend.redis.tls.enabled", false) v.SetDefault("backend.redis.tls.insecure_skip_verify", false) + v.SetDefault("backend.redis.cluster.enabled", false) + v.SetDefault("backend.redis.cluster.hosts", []string{}) + v.SetDefault("backend.redis.pool.size", 0) + v.SetDefault("backend.redis.pool.timeout", time.Duration(0)) + v.SetDefault("backend.redis.pool.min_idle_conns", 0) + v.SetDefault("backend.redis.pool.max_idle_conns", 0) + v.SetDefault("backend.redis.pool.conn_max_idle_time", time.Duration(0)) + v.SetDefault("backend.redis.pool.conn_max_lifetime", time.Duration(0)) + v.SetDefault("backend.redis.timeouts.dial_timeout", time.Duration(0)) + v.SetDefault("backend.redis.timeouts.read_timeout", time.Duration(0)) + v.SetDefault("backend.redis.timeouts.write_timeout", time.Duration(0)) + v.SetDefault("backend.redis.retry.max_retries", 0) + v.SetDefault("backend.redis.retry.min_retry_backoff", time.Duration(0)) + v.SetDefault("backend.redis.retry.max_retry_backoff", time.Duration(0)) v.SetDefault("backend.ignite.scheme", "") v.SetDefault("backend.ignite.host", "") v.SetDefault("backend.ignite.port", 0) diff --git a/config/config_test.go b/config/config_test.go index 7e1a5477..967daaaa 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -1300,6 +1300,28 @@ func getExpectedDefaultConfig() Configuration { }, Redis: Redis{ ExpirationMinutes: utils.REDIS_DEFAULT_EXPIRATION_MINUTES, + Cluster: RedisCluster{ + Enabled: false, + Hosts: []string{}, + }, + Pool: &RedisPool{ + Size: 0, + Timeout: time.Duration(0), + MinIdleConns: 0, + MaxIdleConns: 0, + ConnMaxIdleTime: time.Duration(0), + ConnMaxLifetime: time.Duration(0), + }, + Timeouts: &RedisTimeouts{ + DialTimeout: time.Duration(0), + ReadTimeout: time.Duration(0), + WriteTimeout: time.Duration(0), + }, + Retry: &RedisRetry{ + MaxRetries: 0, + MinRetryBackoff: time.Duration(0), + MaxRetryBackoff: time.Duration(0), + }, }, Ignite: Ignite{ Headers: map[string]string{}, @@ -1378,6 +1400,28 @@ func getExpectedFullConfigForTestFile() Configuration { Enabled: false, InsecureSkipVerify: false, }, + Cluster: RedisCluster{ + Enabled: false, + Hosts: []string{"redis-node1:6379", "redis-node2:6379", "redis-node3:6379"}, + }, + Pool: &RedisPool{ + Size: 0, + Timeout: time.Duration(0), + MinIdleConns: 0, + MaxIdleConns: 0, + ConnMaxIdleTime: time.Duration(0), + ConnMaxLifetime: time.Duration(0), + }, + Timeouts: &RedisTimeouts{ + DialTimeout: time.Duration(0), + ReadTimeout: time.Duration(0), + WriteTimeout: time.Duration(0), + }, + Retry: &RedisRetry{ + MaxRetries: 0, + MinRetryBackoff: time.Duration(0), + MaxRetryBackoff: time.Duration(0), + }, }, Ignite: Ignite{ Scheme: "http", diff --git a/config/configtest/sample_full_config.yaml b/config/configtest/sample_full_config.yaml index 23854c9f..1e870963 100644 --- a/config/configtest/sample_full_config.yaml +++ b/config/configtest/sample_full_config.yaml @@ -28,7 +28,7 @@ backend: keyspace: "prebid" default_ttl_seconds: 60 memcache: - hosts: ["10.0.0.1:11211","127.0.0.1"] + hosts: ["10.0.0.1:11211", "127.0.0.1"] redis: host: "127.0.0.1" port: 6379 @@ -38,6 +38,9 @@ backend: tls: enabled: false insecure_skip_verify: false + cluster: + enabled: false + hosts: ["redis-node1:6379", "redis-node2:6379", "redis-node3:6379"] ignite: scheme: "http" host: "127.0.0.1" diff --git a/endpoints/put_test.go b/endpoints/put_test.go index 428c052c..d394f894 100644 --- a/endpoints/put_test.go +++ b/endpoints/put_test.go @@ -15,7 +15,7 @@ import ( "testing" "time" - "github.com/go-redis/redis/v8" + "github.com/redis/go-redis/v9" "github.com/gofrs/uuid" "github.com/julienschmidt/httprouter" "github.com/prebid/prebid-cache/backends" diff --git a/go.mod b/go.mod index cd8fdb74..5d2c3710 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,6 @@ go 1.19 require ( github.com/aerospike/aerospike-client-go/v6 v6.7.0 github.com/didip/tollbooth/v6 v6.1.2 - github.com/go-redis/redis/v8 v8.11.5 github.com/gocql/gocql v1.0.0 github.com/gofrs/uuid v4.2.0+incompatible github.com/golang/snappy v0.0.4 @@ -14,6 +13,7 @@ require ( github.com/prometheus/client_golang v1.12.2 github.com/prometheus/client_model v0.2.0 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 + github.com/redis/go-redis/v9 v9.12.0 github.com/rs/cors v1.11.0 github.com/sirupsen/logrus v1.6.0 github.com/spf13/viper v1.11.0 @@ -24,7 +24,7 @@ require ( require ( github.com/beorn7/perks v1.0.1 // indirect github.com/bitly/go-hostpool v0.1.0 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/fsnotify/fsnotify v1.5.1 // indirect diff --git a/go.sum b/go.sum index b241657e..1ad3552c 100644 --- a/go.sum +++ b/go.sum @@ -54,10 +54,13 @@ github.com/bitly/go-hostpool v0.1.0 h1:XKmsF6k5el6xHG3WPJ8U0Ku/ye7njX7W81Ng7O2io github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -93,8 +96,6 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-pkgz/expirable-cache v0.0.3 h1:rTh6qNPp78z0bQE6HDhXBHUwqnV9i09Vm6dksJLXQDc= github.com/go-pkgz/expirable-cache v0.0.3/go.mod h1:+IauqN00R2FqNRLCLA+X5YljQJrwB179PfiAoMPlTlQ= -github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= -github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gocql/gocql v1.0.0 h1:UnbTERpP72VZ/viKE1Q1gPtmLvyTZTvuAstvSRydw/c= @@ -221,12 +222,12 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.15.0 h1:WjP/FQ/sk43MRmnEcT+MlDw2TFvkrXlprrPST/IudjU= github.com/onsi/gomega v1.15.0/go.mod h1:cIuvLEne0aoVhAgh/O6ac0Op8WWw9H6eYCriF+tEHG0= -github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/pelletier/go-toml/v2 v2.0.0-beta.8 h1:dy81yyLYJDwMTifq24Oi/IslOslRrDSb3jwDggjz3Z0= @@ -263,9 +264,9 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/rcrowley/go-metrics v0.0.0-20190826022208-cac0b30c2563/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM= github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/redis/go-redis/v9 v9.12.0 h1:XlVPGlflh4nxfhsNXPA8Qp6EmEfTo0rp8oaBzPipXnU= +github.com/redis/go-redis/v9 v9.12.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= -github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -631,8 +632,6 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=