From 547aaa8543da958ba9b4aa6cc3afa74eb06f26dd Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Mon, 8 Jan 2024 15:53:48 -0300 Subject: [PATCH 01/25] migrating from go-redis to storage library --- go.mod | 3 +- go.sum | 8 ++- storage/redis.go | 114 +++++++++++++++++++++++------------------- storage/redis_test.go | 66 +++++++++++++++--------- 4 files changed, 111 insertions(+), 80 deletions(-) diff --git a/go.mod b/go.mod index 9f4c59a0d..83b3e81a7 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/DataDog/datadog-go v4.7.0+incompatible github.com/TykTechnologies/gorpc v0.0.0-20210624160652-fe65bda0ccb9 github.com/TykTechnologies/murmur3 v0.0.0-20230310161213-aad17efd5632 - github.com/TykTechnologies/storage v1.0.8 + github.com/TykTechnologies/storage v1.0.11-0.20231229145029-6fde14b1aa20 github.com/aws/aws-sdk-go-v2 v1.16.14 github.com/aws/aws-sdk-go-v2/config v1.9.0 github.com/aws/aws-sdk-go-v2/service/timestreamwrite v1.9.0 @@ -105,6 +105,7 @@ require ( github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect + github.com/redis/go-redis/v9 v9.3.1 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect github.com/segmentio/backo-go v0.0.0-20160424052352-204274ad699c // indirect github.com/shirou/gopsutil v3.20.11+incompatible // indirect diff --git a/go.sum b/go.sum index 959530c88..adfcf50e5 100644 --- a/go.sum +++ b/go.sum @@ -41,8 +41,8 @@ github.com/TykTechnologies/gorpc v0.0.0-20210624160652-fe65bda0ccb9 h1:fbxHiuw/2 github.com/TykTechnologies/gorpc v0.0.0-20210624160652-fe65bda0ccb9/go.mod h1:v6v7Mlj08+EmEcXOfpuTxGt2qYU9yhqqtv4QF9Wf50E= github.com/TykTechnologies/murmur3 v0.0.0-20230310161213-aad17efd5632 h1:T5NWziFusj8au5nxAqMMh/bZyX9CAyYnBkaMSsfH6BA= github.com/TykTechnologies/murmur3 v0.0.0-20230310161213-aad17efd5632/go.mod h1:UsPYgOFBpNzDXLEti7MKOwHLpVSqdzuNGkVFPspQmnQ= -github.com/TykTechnologies/storage v1.0.8 h1:MBs6hk5oLOmr2qK5/rl+dYO6iDMez6u3QkwOCL6K8n8= -github.com/TykTechnologies/storage v1.0.8/go.mod h1:+0S3KuNlLGBTMTSFREuZFm315zzXjuuCO4QSAPy+d3M= +github.com/TykTechnologies/storage v1.0.11-0.20231229145029-6fde14b1aa20 h1:puEgTIpj2A6c5z7TqIQB3bly2ielrhE4ubxOZTUND2o= +github.com/TykTechnologies/storage v1.0.11-0.20231229145029-6fde14b1aa20/go.mod h1:zcANqpIsDL/l/1zsMMERmjBeJYpER9XMi/dw2Gqa7m4= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= @@ -96,6 +96,8 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r 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/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/cenkalti/backoff/v4 v4.0.2 h1:JIufpQLbh4DkbQoii76ItQIUFzevQSqOLZca4eamEDs= github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= @@ -451,6 +453,8 @@ github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+Pymzi github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/quipo/statsd v0.0.0-20160923160612-75b7afedf0d2 h1:IvjiJDGCF8L8TjKHQKmLAjWztpKDCAaRifiRMdGzWk0= github.com/quipo/statsd v0.0.0-20160923160612-75b7afedf0d2/go.mod h1:1COUodqytMiv/GkAVUGhc0CA6e8xak5U4551TY7iEe0= +github.com/redis/go-redis/v9 v9.3.1 h1:KqdY8U+3X6z+iACvumCNxnoluToB+9Me+TvyFa21Mds= +github.com/redis/go-redis/v9 v9.3.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/resurfaceio/logger-go/v3 v3.2.1 h1:tTPvGp+FpH35aaT/nnhP4n/Rh/f1vHe64WoXTDgv0fY= github.com/resurfaceio/logger-go/v3 v3.2.1/go.mod h1:YPcxFUcloW37F1WQA9MUcGWu2JzlvBxlCfFF5+T3GO8= github.com/retailnext/hllpp v1.0.1-0.20180308014038-101a6d2f8b52/go.mod h1:RDpi1RftBQPUCDRw6SmxeaREsAaRKnOclghuzp/WRzc= diff --git a/storage/redis.go b/storage/redis.go index 70accc0b9..1a39b9bea 100644 --- a/storage/redis.go +++ b/storage/redis.go @@ -2,12 +2,16 @@ package storage import ( "context" - "crypto/tls" + "fmt" "strconv" "strings" "time" - "github.com/go-redis/redis/v8" + "github.com/TykTechnologies/storage/temporal/connector" + keyvalue "github.com/TykTechnologies/storage/temporal/keyvalue" + "github.com/TykTechnologies/storage/temporal/list" + "github.com/TykTechnologies/storage/temporal/model" + "github.com/sirupsen/logrus" "github.com/kelseyhightower/envconfig" @@ -16,11 +20,16 @@ import ( // ------------------- REDIS CLUSTER STORAGE MANAGER ------------------------------- -var redisClusterSingleton redis.UniversalClient +var redisClusterSingleton *redisManager var redisLogPrefix = "redis" var ENV_REDIS_PREFIX = "TYK_PMP_REDIS" var ctx = context.Background() +type redisManager struct { + list list.List + kv keyvalue.KeyValue +} + type EnvMapString map[string]string func (e *EnvMapString) Decode(value string) error { @@ -78,13 +87,13 @@ type RedisStorageConfig struct { // RedisClusterStorageManager is a storage manager that uses the redis database. type RedisClusterStorageManager struct { - db redis.UniversalClient + db *redisManager KeyPrefix string HashKeys bool Config RedisStorageConfig } -func NewRedisClusterPool(forceReconnect bool, config RedisStorageConfig) redis.UniversalClient { +func NewRedisClusterPool(forceReconnect bool, config RedisStorageConfig) *redisManager { if !forceReconnect { if redisClusterSingleton != nil { log.WithFields(logrus.Fields{ @@ -94,7 +103,7 @@ func NewRedisClusterPool(forceReconnect bool, config RedisStorageConfig) redis.U } } else { if redisClusterSingleton != nil { - redisClusterSingleton.Close() + // redisClusterSingleton.Close() } } @@ -107,49 +116,52 @@ func NewRedisClusterPool(forceReconnect bool, config RedisStorageConfig) redis.U maxActive = config.MaxActive } - timeout := 5 * time.Second + timeout := 5 if config.Timeout > 0 { - timeout = time.Duration(config.Timeout) * time.Second - } - - var tlsConfig *tls.Config - if config.RedisUseSSL { - tlsConfig = &tls.Config{ - InsecureSkipVerify: config.RedisSSLInsecureSkipVerify, - } + timeout = config.Timeout } - var client redis.UniversalClient - opts := &redis.UniversalOptions{ + opts := &model.RedisOptions{ MasterName: config.MasterName, SentinelPassword: config.SentinelPassword, Addrs: getRedisAddrs(config), - DB: config.Database, + Database: config.Database, Username: config.Username, Password: config.Password, - PoolSize: maxActive, - IdleTimeout: 240 * time.Second, - ReadTimeout: timeout, - WriteTimeout: timeout, - DialTimeout: timeout, - TLSConfig: tlsConfig, + MaxActive: maxActive, + Timeout: timeout, + EnableCluster: config.EnableCluster, } - if opts.MasterName != "" { - log.Info("--> [REDIS] Creating sentinel-backed failover client") - client = redis.NewFailoverClient(opts.Failover()) - } else if config.EnableCluster { - log.Info("--> [REDIS] Creating cluster client") - client = redis.NewClusterClient(opts.Cluster()) - } else { - log.Info("--> [REDIS] Creating single-node client") - client = redis.NewClient(opts.Simple()) + tlsOptions := &model.TLS{ + Enable: config.RedisUseSSL, + InsecureSkipVerify: config.RedisSSLInsecureSkipVerify, + } + + conn, err := connector.NewConnector(model.RedisV9Type, model.WithRedisConfig(opts), model.WithTLS(tlsOptions)) + if err != nil { + log.WithFields(logrus.Fields{"prefix": redisLogPrefix}).Error(err) + return nil } - redisClusterSingleton = client + kv, err := keyvalue.NewKeyValue(conn) + if err != nil { + log.WithFields(logrus.Fields{"prefix": redisLogPrefix}).Error(err) + return nil + } - return client + l, err := list.NewList(conn) + if err != nil { + log.WithFields(logrus.Fields{"prefix": redisLogPrefix}).Error(err) + return nil + } + + redisClusterSingleton = &redisManager{} + redisClusterSingleton.kv = kv + redisClusterSingleton.list = l + + return redisClusterSingleton } func getRedisAddrs(config RedisStorageConfig) (addrs []string) { @@ -252,30 +264,28 @@ func (r *RedisClusterStorageManager) GetAndDeleteSet(keyName string, chunkSize i "prefix": redisLogPrefix, }).Debug("Fixed keyname is: ", fixedKey) - var lrange *redis.StringSliceCmd - _, err := r.db.TxPipelined(ctx, func(pipe redis.Pipeliner) error { - lrange = pipe.LRange(ctx, fixedKey, 0, chunkSize-1) - - if chunkSize == 0 { - pipe.Del(ctx, fixedKey) - } else { - pipe.LTrim(ctx, fixedKey, chunkSize, -1) - - // extend expiry after successful LTRIM - pipe.Expire(ctx, fixedKey, expire) - } + vals, err := r.db.list.Pop(ctx, fixedKey, chunkSize-1) + if err != nil { + fmt.Println("FAILED 1") + log.WithFields(logrus.Fields{ + "prefix": redisLogPrefix, + }).Error("Multi command failed: ", err) + r.Connect() return nil - }) + } + fmt.Println("vals:", vals) + + err = r.db.kv.Expire(ctx, fixedKey, expire) if err != nil { + fmt.Println("FAILED 2") log.WithFields(logrus.Fields{ "prefix": redisLogPrefix, }).Error("Multi command failed: ", err) r.Connect() + return nil } - vals := lrange.Val() - result := make([]interface{}, len(vals)) for i, v := range vals { result[i] = v @@ -294,7 +304,7 @@ func (r *RedisClusterStorageManager) SetKey(keyName, session string, timeout int log.Debug("[STORE] Setting key: ", r.fixKey(keyName)) r.ensureConnection() - err := r.db.Set(ctx, r.fixKey(keyName), session, 0).Err() + err := r.db.kv.Set(ctx, r.fixKey(keyName), session, 0) if timeout > 0 { if err := r.SetExp(keyName, timeout); err != nil { return err @@ -308,7 +318,7 @@ func (r *RedisClusterStorageManager) SetKey(keyName, session string, timeout int } func (r *RedisClusterStorageManager) SetExp(keyName string, timeout int64) error { - err := r.db.Expire(ctx, r.fixKey(keyName), time.Duration(timeout)*time.Second).Err() + err := r.db.kv.Expire(ctx, r.fixKey(keyName), time.Duration(timeout)*time.Second) if err != nil { log.Error("Could not EXPIRE key: ", err) } diff --git a/storage/redis_test.go b/storage/redis_test.go index 13bffa07b..dfa36d392 100644 --- a/storage/redis_test.go +++ b/storage/redis_test.go @@ -71,30 +71,30 @@ var testData = []struct { in []string chunk int64 }{ - {in: nil, chunk: int64(0)}, - {in: []string{"one"}, chunk: int64(0)}, - {in: []string{"one", "two"}, chunk: int64(0)}, - {in: []string{"one", "two", "three"}, chunk: int64(0)}, - {in: []string{"one", "two", "three", "four"}, chunk: int64(0)}, - {in: []string{"one", "two", "three", "four", "five"}, chunk: int64(0)}, - {in: nil, chunk: int64(1)}, - {in: []string{"one"}, chunk: int64(1)}, - {in: []string{"one", "two"}, chunk: int64(1)}, - {in: []string{"one", "two", "three"}, chunk: int64(1)}, - {in: []string{"one", "two", "three", "four"}, chunk: int64(1)}, - {in: []string{"one", "two", "three", "four", "five"}, chunk: int64(1)}, - {in: nil, chunk: int64(2)}, - {in: []string{"one"}, chunk: int64(2)}, + // {in: nil, chunk: int64(0)}, + // {in: []string{"one"}, chunk: int64(0)}, + // {in: []string{"one", "two"}, chunk: int64(0)}, + // {in: []string{"one", "two", "three"}, chunk: int64(0)}, + // {in: []string{"one", "two", "three", "four"}, chunk: int64(0)}, + // {in: []string{"one", "two", "three", "four", "five"}, chunk: int64(0)}, + // {in: nil, chunk: int64(1)}, + // {in: []string{"one"}, chunk: int64(1)}, + // {in: []string{"one", "two"}, chunk: int64(1)}, + // {in: []string{"one", "two", "three"}, chunk: int64(1)}, + // {in: []string{"one", "two", "three", "four"}, chunk: int64(1)}, + // {in: []string{"one", "two", "three", "four", "five"}, chunk: int64(1)}, + // {in: nil, chunk: int64(2)}, + // {in: []string{"one"}, chunk: int64(2)}, {in: []string{"one", "two"}, chunk: int64(2)}, - {in: []string{"one", "two", "three"}, chunk: int64(2)}, - {in: []string{"one", "two", "three", "four"}, chunk: int64(2)}, - {in: []string{"one", "two", "three", "four", "five"}, chunk: int64(2)}, - {in: nil, chunk: int64(3)}, - {in: []string{"one"}, chunk: int64(3)}, - {in: []string{"one", "two"}, chunk: int64(3)}, - {in: []string{"one", "two", "three"}, chunk: int64(3)}, - {in: []string{"one", "two", "three", "four"}, chunk: int64(3)}, - {in: []string{"one", "two", "three", "four", "five"}, chunk: int64(3)}, + // {in: []string{"one", "two", "three"}, chunk: int64(2)}, + // {in: []string{"one", "two", "three", "four"}, chunk: int64(2)}, + // {in: []string{"one", "two", "three", "four", "five"}, chunk: int64(2)}, + // {in: nil, chunk: int64(3)}, + // {in: []string{"one"}, chunk: int64(3)}, + // {in: []string{"one", "two"}, chunk: int64(3)}, + // {in: []string{"one", "two", "three"}, chunk: int64(3)}, + // {in: []string{"one", "two", "three", "four"}, chunk: int64(3)}, + // {in: []string{"one", "two", "three", "four", "five"}, chunk: int64(3)}, } func TestRedisClusterStorageManager_GetAndDeleteSet(t *testing.T) { @@ -107,13 +107,29 @@ func TestRedisClusterStorageManager_GetAndDeleteSet(t *testing.T) { t.Fatal("unable to connect", err.Error()) } + connected := r.Connect() + if !connected { + t.Fatal("failed to connect") + } + + if r.db == nil { + t.Fatal("db is empty") + } + mockKeyName := "testanalytics" for _, tt := range testData { t.Run(fmt.Sprintf("in: %v", tt), func(t *testing.T) { ctx := context.Background() if tt.in != nil { - r.db.RPush(ctx, r.fixKey(mockKeyName), tt.in) + in := [][]byte{} + for _, v := range tt.in { + in = append(in, []byte(v)) + } + err := r.db.list.Append(ctx, false, r.fixKey(mockKeyName), in...) + if err != nil { + t.Fatal(err) + } } iterations := 1 @@ -129,12 +145,12 @@ func TestRedisClusterStorageManager_GetAndDeleteSet(t *testing.T) { count := 0 for i := 0; i < iterations; i++ { res := r.GetAndDeleteSet(mockKeyName, tt.chunk, 60*time.Second) - count += len(res) t.Logf("---> %d: %v", i, res) } if count != len(tt.in) { + fmt.Println("count:", count, "(tt.in):", tt.in) t.Fatal() } }) From 192adf2f2e690fe3e0a44623c9b3eceb69208700 Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Tue, 9 Jan 2024 13:36:58 -0300 Subject: [PATCH 02/25] fixing issues --- go.mod | 5 +-- go.sum | 34 +++--------------- main.go | 16 +++++++-- storage/redis.go | 34 +++++------------- storage/redis_test.go | 83 +++++++++++++++++-------------------------- storage/store.go | 2 +- 6 files changed, 63 insertions(+), 111 deletions(-) diff --git a/go.mod b/go.mod index 83b3e81a7..4133b2ea0 100644 --- a/go.mod +++ b/go.mod @@ -6,13 +6,12 @@ require ( github.com/DataDog/datadog-go v4.7.0+incompatible github.com/TykTechnologies/gorpc v0.0.0-20210624160652-fe65bda0ccb9 github.com/TykTechnologies/murmur3 v0.0.0-20230310161213-aad17efd5632 - github.com/TykTechnologies/storage v1.0.11-0.20231229145029-6fde14b1aa20 + github.com/TykTechnologies/storage v1.0.11-0.20240109162024-69c223c0cc99 github.com/aws/aws-sdk-go-v2 v1.16.14 github.com/aws/aws-sdk-go-v2/config v1.9.0 github.com/aws/aws-sdk-go-v2/service/timestreamwrite v1.9.0 github.com/cenkalti/backoff/v4 v4.0.2 github.com/fatih/structs v1.1.0 - github.com/go-redis/redis/v8 v8.3.1 github.com/gocraft/health v0.0.0-20170925182251-8675af27fef0 github.com/gofrs/uuid v4.0.0+incompatible github.com/golang/protobuf v1.5.3 @@ -98,7 +97,6 @@ require ( github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect github.com/olivere/elastic v6.2.31+incompatible // indirect - github.com/onsi/ginkgo v1.16.4 // indirect github.com/onsi/gomega v1.20.0 // indirect github.com/pierrec/lz4 v2.6.0+incompatible // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -119,7 +117,6 @@ require ( github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect go.mongodb.org/mongo-driver v1.11.2 // indirect - go.opentelemetry.io/otel v0.13.0 // indirect go.uber.org/atomic v1.9.0 // indirect golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect golang.org/x/sync v0.2.0 // indirect diff --git a/go.sum b/go.sum index adfcf50e5..6846d1a13 100644 --- a/go.sum +++ b/go.sum @@ -41,8 +41,8 @@ github.com/TykTechnologies/gorpc v0.0.0-20210624160652-fe65bda0ccb9 h1:fbxHiuw/2 github.com/TykTechnologies/gorpc v0.0.0-20210624160652-fe65bda0ccb9/go.mod h1:v6v7Mlj08+EmEcXOfpuTxGt2qYU9yhqqtv4QF9Wf50E= github.com/TykTechnologies/murmur3 v0.0.0-20230310161213-aad17efd5632 h1:T5NWziFusj8au5nxAqMMh/bZyX9CAyYnBkaMSsfH6BA= github.com/TykTechnologies/murmur3 v0.0.0-20230310161213-aad17efd5632/go.mod h1:UsPYgOFBpNzDXLEti7MKOwHLpVSqdzuNGkVFPspQmnQ= -github.com/TykTechnologies/storage v1.0.11-0.20231229145029-6fde14b1aa20 h1:puEgTIpj2A6c5z7TqIQB3bly2ielrhE4ubxOZTUND2o= -github.com/TykTechnologies/storage v1.0.11-0.20231229145029-6fde14b1aa20/go.mod h1:zcANqpIsDL/l/1zsMMERmjBeJYpER9XMi/dw2Gqa7m4= +github.com/TykTechnologies/storage v1.0.11-0.20240109162024-69c223c0cc99 h1:EKYVk+Wxvvaj4EdFpV0t5gx2ClLFzrELdJD55sonrsQ= +github.com/TykTechnologies/storage v1.0.11-0.20240109162024-69c223c0cc99/go.mod h1:zcANqpIsDL/l/1zsMMERmjBeJYpER9XMi/dw2Gqa7m4= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= @@ -103,7 +103,6 @@ github.com/cenkalti/backoff/v4 v4.0.2 h1:JIufpQLbh4DkbQoii76ItQIUFzevQSqOLZca4ea github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -143,8 +142,6 @@ github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/ github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/getkin/kin-openapi v0.61.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/glycerine/go-unsnap-stream v0.0.0-20180323001048-9f0cb55181dd/go.mod h1:/20jfyN9Y5QPEAprSgKAUr+glWDY39ZiUEAYOEv5dsE= @@ -161,13 +158,10 @@ github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI= github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-redis/redis/v8 v8.3.1 h1:jEPCgHQopfNaABun3NVN9pv2K7RjstY/7UJD6UEKFEY= -github.com/go-redis/redis/v8 v8.3.1/go.mod h1:a2xkpBM7NJUN5V5kiF46X5Ltx4WeXJ9757X/ScKUBdE= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.5.0 h1:ozyZYNQW3x3HtqT1jira07DN2PArx2v7/mN66gGcHOs= github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= 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/gocraft/health v0.0.0-20170925182251-8675af27fef0 h1:pKjeDsx7HGGbjr7VGI1HksxDJqSjaGED3cSw9GeSI98= github.com/gocraft/health v0.0.0-20170925182251-8675af27fef0/go.mod h1:rWibcVfwbUxi/QXW84U7vNTcIcZFd6miwbt8ritxh/Y= github.com/gofrs/uuid v3.3.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -195,7 +189,6 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= @@ -237,6 +230,7 @@ github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/helloeave/json v1.15.3 h1:roUxUEGhsSvhuhi80c4qmLiW633d5uf0mkzUGzBMfX8= github.com/helloeave/json v1.15.3/go.mod h1:uTHhuUsgnrpm9cc7Gi3tfIUwgf1dq/7+uLfpUFLBFEQ= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -395,24 +389,15 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6f github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/olivere/elastic v6.2.31+incompatible h1:zwJIIsgfiDBuDS3sb6MCbm/e03BPEJoGZvqevZXM254= github.com/olivere/elastic v6.2.31+incompatible/go.mod h1:J+q1zQJTgAz9woqsbVRqGeB5G1iqDKVBWLNSYW8yfJ8= github.com/olivere/elastic/v7 v7.0.12/go.mod h1:14rWX28Pnh3qCKYRVnSGXWLf9MbLonYS/4FDCY3LAPo= github.com/olivere/elastic/v7 v7.0.28 h1:KAP4EuaEcvPJknRNkAAso1xeu0C1+/CeDQsxj9Cw9Fg= github.com/olivere/elastic/v7 v7.0.28/go.mod h1:DzHQoqd6YqSuvF1lk/fR4cW4FNUNzSD5/F5MBm3GRMo= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -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/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -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.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q= github.com/onsi/gomega v1.20.0/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= @@ -553,8 +538,6 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/otel v0.13.0 h1:2isEnyzjjJZq6r2EKMsFj4TxiQiexsM04AVhwbR/oBA= -go.opentelemetry.io/otel v0.13.0/go.mod h1:dlSNewoRYikTkotEnxdmuBHgzT+k/idJSfDv/FxEnOY= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= @@ -634,7 +617,6 @@ golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -676,21 +658,16 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200107162124-548cf772de50/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200826173525-f9321e4c35a6/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -744,7 +721,6 @@ golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200108203644-89082a384178/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -814,6 +790,7 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/mgo.v2 v2.0.0-20190816093944-a6b53ec6cb22 h1:VpOs+IwYnYBaFnrNAeB8UUWtL3vEUnzSCL1nVjPhqrw= @@ -830,7 +807,6 @@ gopkg.in/vmihailenco/msgpack.v2 v2.9.1 h1:kb0VV7NuIojvRfzwslQeP3yArBqJHW9tOl4t38 gopkg.in/vmihailenco/msgpack.v2 v2.9.1/go.mod h1:/3Dn1Npt9+MYyLpYYXjInO/5jvMLamn+AEGwNEOatn8= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= diff --git a/main.go b/main.go index 31bdb2818..a1e357ccf 100644 --- a/main.go +++ b/main.go @@ -218,7 +218,12 @@ func StartPurgeLoop(wg *sync.WaitGroup, ctx context.Context, secInterval int, ch for _, serializerMethod := range AnalyticsSerializers { analyticsKeyName += serializerMethod.GetSuffix() - AnalyticsValues := AnalyticsStore.GetAndDeleteSet(analyticsKeyName, chunkSize, expire) + AnalyticsValues, err := AnalyticsStore.GetAndDeleteSet(analyticsKeyName, chunkSize, expire) + if err != nil { + log.WithFields(logrus.Fields{ + "prefix": mainPrefix, + }).Error("Error trying to execute GetAndDeleteSet: " + err.Error()) + } if len(AnalyticsValues) > 0 { PreprocessAnalyticsValues(AnalyticsValues, serializerMethod, analyticsKeyName, omitDetails, job, startTime, secInterval) } @@ -229,7 +234,14 @@ func StartPurgeLoop(wg *sync.WaitGroup, ctx context.Context, secInterval int, ch job.Timing("purge_time_all", time.Since(startTime).Nanoseconds()) if !SystemConfig.DontPurgeUptimeData { - UptimeValues := UptimeStorage.GetAndDeleteSet(storage.UptimeAnalytics_KEYNAME, chunkSize, expire) + UptimeValues, err := UptimeStorage.GetAndDeleteSet(storage.UptimeAnalytics_KEYNAME, chunkSize, expire) + if err != nil { + if err != nil { + log.WithFields(logrus.Fields{ + "prefix": mainPrefix, + }).Error("Error trying to execute GetAndDeleteSet: " + err.Error()) + } + } UptimePump.WriteUptimeData(UptimeValues) } diff --git a/storage/redis.go b/storage/redis.go index 1a39b9bea..9443cd8e6 100644 --- a/storage/redis.go +++ b/storage/redis.go @@ -2,7 +2,6 @@ package storage import ( "context" - "fmt" "strconv" "strings" "time" @@ -241,7 +240,7 @@ func (r *RedisClusterStorageManager) fixKey(keyName string) string { return setKeyName } -func (r *RedisClusterStorageManager) GetAndDeleteSet(keyName string, chunkSize int64, expire time.Duration) []interface{} { +func (r *RedisClusterStorageManager) GetAndDeleteSet(keyName string, chunkSize int64, expire time.Duration) ([]interface{}, error) { log.WithFields(logrus.Fields{ "prefix": redisLogPrefix, }).Debug("Getting raw key set: ", keyName) @@ -264,38 +263,23 @@ func (r *RedisClusterStorageManager) GetAndDeleteSet(keyName string, chunkSize i "prefix": redisLogPrefix, }).Debug("Fixed keyname is: ", fixedKey) - vals, err := r.db.list.Pop(ctx, fixedKey, chunkSize-1) + result, err := r.db.list.Pop(ctx, fixedKey, chunkSize) if err != nil { - fmt.Println("FAILED 1") - log.WithFields(logrus.Fields{ - "prefix": redisLogPrefix, - }).Error("Multi command failed: ", err) - r.Connect() - return nil + return nil, err } - fmt.Println("vals:", vals) - err = r.db.kv.Expire(ctx, fixedKey, expire) if err != nil { - fmt.Println("FAILED 2") - log.WithFields(logrus.Fields{ - "prefix": redisLogPrefix, - }).Error("Multi command failed: ", err) - r.Connect() - return nil + return nil, err } - result := make([]interface{}, len(vals)) - for i, v := range vals { - result[i] = v - } + intResult := []interface{}{} + for _, v := range result { + intResult = append(intResult, v) - log.WithFields(logrus.Fields{ - "prefix": redisLogPrefix, - }).Debug("Unpacked vals: ", len(result)) + } - return result + return intResult, nil } // SetKey will create (or update) a key value in the store diff --git a/storage/redis_test.go b/storage/redis_test.go index dfa36d392..7628c7e5b 100644 --- a/storage/redis_test.go +++ b/storage/redis_test.go @@ -5,8 +5,6 @@ import ( "fmt" "testing" "time" - - "github.com/go-redis/redis/v8" ) func TestRedisAddressConfiguration(t *testing.T) { @@ -43,58 +41,36 @@ func TestRedisAddressConfiguration(t *testing.T) { } }) - t.Run("Default addresses", func(t *testing.T) { - opts := &redis.UniversalOptions{} - simpleOpts := opts.Simple() - - if simpleOpts.Addr != "127.0.0.1:6379" { - t.Fatal("Wrong default single node address") - } - - opts.Addrs = []string{} - clusterOpts := opts.Cluster() - - if clusterOpts.Addrs[0] != "127.0.0.1:6379" || len(clusterOpts.Addrs) != 1 { - t.Fatal("Wrong default cluster mode address") - } - - opts.Addrs = []string{} - failoverOpts := opts.Failover() - - if failoverOpts.SentinelAddrs[0] != "127.0.0.1:26379" || len(failoverOpts.SentinelAddrs) != 1 { - t.Fatal("Wrong default sentinel mode address") - } - }) } var testData = []struct { in []string chunk int64 }{ - // {in: nil, chunk: int64(0)}, - // {in: []string{"one"}, chunk: int64(0)}, - // {in: []string{"one", "two"}, chunk: int64(0)}, - // {in: []string{"one", "two", "three"}, chunk: int64(0)}, - // {in: []string{"one", "two", "three", "four"}, chunk: int64(0)}, - // {in: []string{"one", "two", "three", "four", "five"}, chunk: int64(0)}, - // {in: nil, chunk: int64(1)}, - // {in: []string{"one"}, chunk: int64(1)}, - // {in: []string{"one", "two"}, chunk: int64(1)}, - // {in: []string{"one", "two", "three"}, chunk: int64(1)}, - // {in: []string{"one", "two", "three", "four"}, chunk: int64(1)}, - // {in: []string{"one", "two", "three", "four", "five"}, chunk: int64(1)}, - // {in: nil, chunk: int64(2)}, - // {in: []string{"one"}, chunk: int64(2)}, + {in: nil, chunk: int64(0)}, + {in: []string{"one"}, chunk: int64(0)}, + {in: []string{"one", "two"}, chunk: int64(0)}, + {in: []string{"one", "two", "three"}, chunk: int64(0)}, + {in: []string{"one", "two", "three", "four"}, chunk: int64(0)}, + {in: []string{"one", "two", "three", "four", "five"}, chunk: int64(0)}, + {in: nil, chunk: int64(1)}, + {in: []string{"one"}, chunk: int64(1)}, + {in: []string{"one", "two"}, chunk: int64(1)}, + {in: []string{"one", "two", "three"}, chunk: int64(1)}, + {in: []string{"one", "two", "three", "four"}, chunk: int64(1)}, + {in: []string{"one", "two", "three", "four", "five"}, chunk: int64(1)}, + {in: nil, chunk: int64(2)}, + {in: []string{"one"}, chunk: int64(2)}, {in: []string{"one", "two"}, chunk: int64(2)}, - // {in: []string{"one", "two", "three"}, chunk: int64(2)}, - // {in: []string{"one", "two", "three", "four"}, chunk: int64(2)}, - // {in: []string{"one", "two", "three", "four", "five"}, chunk: int64(2)}, - // {in: nil, chunk: int64(3)}, - // {in: []string{"one"}, chunk: int64(3)}, - // {in: []string{"one", "two"}, chunk: int64(3)}, - // {in: []string{"one", "two", "three"}, chunk: int64(3)}, - // {in: []string{"one", "two", "three", "four"}, chunk: int64(3)}, - // {in: []string{"one", "two", "three", "four", "five"}, chunk: int64(3)}, + {in: []string{"one", "two", "three"}, chunk: int64(2)}, + {in: []string{"one", "two", "three", "four"}, chunk: int64(2)}, + {in: []string{"one", "two", "three", "four", "five"}, chunk: int64(2)}, + {in: nil, chunk: int64(3)}, + {in: []string{"one"}, chunk: int64(3)}, + {in: []string{"one", "two"}, chunk: int64(3)}, + {in: []string{"one", "two", "three"}, chunk: int64(3)}, + {in: []string{"one", "two", "three", "four"}, chunk: int64(3)}, + {in: []string{"one", "two", "three", "four", "five"}, chunk: int64(3)}, } func TestRedisClusterStorageManager_GetAndDeleteSet(t *testing.T) { @@ -102,7 +78,12 @@ func TestRedisClusterStorageManager_GetAndDeleteSet(t *testing.T) { conf["host"] = "localhost" conf["port"] = 6379 - r := RedisClusterStorageManager{} + r := RedisClusterStorageManager{ + Config: RedisStorageConfig{ + Host: "localhost", + Port: 6379, + }, + } if err := r.Init(conf); err != nil { t.Fatal("unable to connect", err.Error()) } @@ -144,13 +125,15 @@ func TestRedisClusterStorageManager_GetAndDeleteSet(t *testing.T) { count := 0 for i := 0; i < iterations; i++ { - res := r.GetAndDeleteSet(mockKeyName, tt.chunk, 60*time.Second) + res, err := r.GetAndDeleteSet(mockKeyName, tt.chunk, 60*time.Second) + if err != nil { + t.Fatal(err) + } count += len(res) t.Logf("---> %d: %v", i, res) } if count != len(tt.in) { - fmt.Println("count:", count, "(tt.in):", tt.in) t.Fatal() } }) diff --git a/storage/store.go b/storage/store.go index edd0d3cc4..55d4b9857 100644 --- a/storage/store.go +++ b/storage/store.go @@ -6,7 +6,7 @@ type AnalyticsStorage interface { Init(config interface{}) error GetName() string Connect() bool - GetAndDeleteSet(setName string, chunkSize int64, expire time.Duration) []interface{} + GetAndDeleteSet(setName string, chunkSize int64, expire time.Duration) ([]interface{}, error) } const ( From bc3c969a7cc6620be295ca7fec68941f689bf205 Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Tue, 9 Jan 2024 13:50:13 -0300 Subject: [PATCH 03/25] linting --- storage/redis.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/storage/redis.go b/storage/redis.go index 9443cd8e6..c1f0e161f 100644 --- a/storage/redis.go +++ b/storage/redis.go @@ -27,6 +27,7 @@ var ctx = context.Background() type redisManager struct { list list.List kv keyvalue.KeyValue + conn model.Connector } type EnvMapString map[string]string @@ -92,7 +93,7 @@ type RedisClusterStorageManager struct { Config RedisStorageConfig } -func NewRedisClusterPool(forceReconnect bool, config RedisStorageConfig) *redisManager { +func NewRedisClusterPool(forceReconnect bool, config *RedisStorageConfig) *redisManager { if !forceReconnect { if redisClusterSingleton != nil { log.WithFields(logrus.Fields{ @@ -102,7 +103,14 @@ func NewRedisClusterPool(forceReconnect bool, config RedisStorageConfig) *redisM } } else { if redisClusterSingleton != nil { - // redisClusterSingleton.Close() + err := redisClusterSingleton.conn.Disconnect(ctx) + if err != nil { + log.WithFields(logrus.Fields{ + "prefix": redisLogPrefix, + }).Error("Error disconnecting Redis: " + err.Error()) + } + + return redisClusterSingleton } } @@ -124,7 +132,7 @@ func NewRedisClusterPool(forceReconnect bool, config RedisStorageConfig) *redisM opts := &model.RedisOptions{ MasterName: config.MasterName, SentinelPassword: config.SentinelPassword, - Addrs: getRedisAddrs(config), + Addrs: getRedisAddrs(*config), Database: config.Database, Username: config.Username, Password: config.Password, From 97601c9af94ce436be20928c39b2604b10e1edc2 Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Tue, 9 Jan 2024 13:51:41 -0300 Subject: [PATCH 04/25] fixing compilation error --- storage/redis.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/redis.go b/storage/redis.go index c1f0e161f..5b3a56e4a 100644 --- a/storage/redis.go +++ b/storage/redis.go @@ -221,7 +221,7 @@ func (r *RedisClusterStorageManager) Connect() bool { log.WithFields(logrus.Fields{ "prefix": redisLogPrefix, }).Debug("Connecting to redis cluster") - r.db = NewRedisClusterPool(false, r.Config) + r.db = NewRedisClusterPool(false, &r.Config) return true } From 524fb01f904efc67f853a079e882f2145e927a2d Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Tue, 9 Jan 2024 13:59:29 -0300 Subject: [PATCH 05/25] linting --- main.go | 10 +++++----- storage/redis.go | 19 ++++++++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/main.go b/main.go index a1e357ccf..04804dbb5 100644 --- a/main.go +++ b/main.go @@ -223,6 +223,7 @@ func StartPurgeLoop(wg *sync.WaitGroup, ctx context.Context, secInterval int, ch log.WithFields(logrus.Fields{ "prefix": mainPrefix, }).Error("Error trying to execute GetAndDeleteSet: " + err.Error()) + return } if len(AnalyticsValues) > 0 { PreprocessAnalyticsValues(AnalyticsValues, serializerMethod, analyticsKeyName, omitDetails, job, startTime, secInterval) @@ -236,11 +237,10 @@ func StartPurgeLoop(wg *sync.WaitGroup, ctx context.Context, secInterval int, ch if !SystemConfig.DontPurgeUptimeData { UptimeValues, err := UptimeStorage.GetAndDeleteSet(storage.UptimeAnalytics_KEYNAME, chunkSize, expire) if err != nil { - if err != nil { - log.WithFields(logrus.Fields{ - "prefix": mainPrefix, - }).Error("Error trying to execute GetAndDeleteSet: " + err.Error()) - } + log.WithFields(logrus.Fields{ + "prefix": mainPrefix, + }).Error("Error trying to execute GetAndDeleteSet: " + err.Error()) + return } UptimePump.WriteUptimeData(UptimeValues) } diff --git a/storage/redis.go b/storage/redis.go index 5b3a56e4a..511fb8cd7 100644 --- a/storage/redis.go +++ b/storage/redis.go @@ -19,12 +19,14 @@ import ( // ------------------- REDIS CLUSTER STORAGE MANAGER ------------------------------- -var redisClusterSingleton *redisManager -var redisLogPrefix = "redis" -var ENV_REDIS_PREFIX = "TYK_PMP_REDIS" -var ctx = context.Background() +var ( + redisClusterSingleton *RedisManager + redisLogPrefix = "redis" + ENV_REDIS_PREFIX = "TYK_PMP_REDIS" + ctx = context.Background() +) -type redisManager struct { +type RedisManager struct { list list.List kv keyvalue.KeyValue conn model.Connector @@ -87,13 +89,13 @@ type RedisStorageConfig struct { // RedisClusterStorageManager is a storage manager that uses the redis database. type RedisClusterStorageManager struct { - db *redisManager + db *RedisManager KeyPrefix string HashKeys bool Config RedisStorageConfig } -func NewRedisClusterPool(forceReconnect bool, config *RedisStorageConfig) *redisManager { +func NewRedisClusterPool(forceReconnect bool, config *RedisStorageConfig) *RedisManager { if !forceReconnect { if redisClusterSingleton != nil { log.WithFields(logrus.Fields{ @@ -164,7 +166,7 @@ func NewRedisClusterPool(forceReconnect bool, config *RedisStorageConfig) *redis return nil } - redisClusterSingleton = &redisManager{} + redisClusterSingleton = &RedisManager{} redisClusterSingleton.kv = kv redisClusterSingleton.list = l @@ -284,7 +286,6 @@ func (r *RedisClusterStorageManager) GetAndDeleteSet(keyName string, chunkSize i intResult := []interface{}{} for _, v := range result { intResult = append(intResult, v) - } return intResult, nil From c39344127e0b767d814fb8c49f22b928522bc266 Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Mon, 22 Jan 2024 10:02:02 -0300 Subject: [PATCH 06/25] linting part 2 --- storage/redis.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/redis.go b/storage/redis.go index 511fb8cd7..b49cbc2fc 100644 --- a/storage/redis.go +++ b/storage/redis.go @@ -22,7 +22,7 @@ import ( var ( redisClusterSingleton *RedisManager redisLogPrefix = "redis" - ENV_REDIS_PREFIX = "TYK_PMP_REDIS" + envRedisPrefix = "TYK_PMP_REDIS" ctx = context.Background() ) @@ -204,7 +204,7 @@ func (r *RedisClusterStorageManager) Init(config interface{}) error { }).Fatal("Failed to decode configuration: ", err) } - overrideErr := envconfig.Process(ENV_REDIS_PREFIX, &r.Config) + overrideErr := envconfig.Process(envRedisPrefix, &r.Config) if overrideErr != nil { log.Error("Failed to process environment variables for redis: ", overrideErr) } From 4c4963d2f868627a039dda258566ce50c75c61d6 Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Mon, 22 Jan 2024 12:58:47 -0300 Subject: [PATCH 07/25] adding constructor unit test --- storage/redis.go | 1 + storage/redis_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/storage/redis.go b/storage/redis.go index b49cbc2fc..483c58e6b 100644 --- a/storage/redis.go +++ b/storage/redis.go @@ -169,6 +169,7 @@ func NewRedisClusterPool(forceReconnect bool, config *RedisStorageConfig) *Redis redisClusterSingleton = &RedisManager{} redisClusterSingleton.kv = kv redisClusterSingleton.list = l + redisClusterSingleton.conn = conn return redisClusterSingleton } diff --git a/storage/redis_test.go b/storage/redis_test.go index 7628c7e5b..04ed1fc74 100644 --- a/storage/redis_test.go +++ b/storage/redis_test.go @@ -5,6 +5,9 @@ import ( "fmt" "testing" "time" + + "github.com/TykTechnologies/storage/temporal/model" + "github.com/stretchr/testify/assert" ) func TestRedisAddressConfiguration(t *testing.T) { @@ -139,3 +142,33 @@ func TestRedisClusterStorageManager_GetAndDeleteSet(t *testing.T) { }) } } + +func TestNewRedisClusterPool(t *testing.T) { + tcs := []struct { + name string + cfg *RedisStorageConfig + }{ + { + name: "connect to localhost:6379", + cfg: &RedisStorageConfig{ + Host: "localhost", + Port: 6379, + }, + }, + } + + for _, tc := range tcs { + t.Run(tc.name, func(t *testing.T) { + pool := NewRedisClusterPool(false, tc.cfg) + if pool == nil { + t.Fatal("pool is nil") + } + + assert.NotNil(t, pool.conn) + assert.NotNil(t, pool.kv) + assert.NotNil(t, pool.list) + assert.Equal(t, pool.conn.Type(), model.RedisV9Type) + assert.NoError(t, pool.conn.Ping(context.Background())) + }) + } +} From 3714ad9fb2d34aaa234ba74e5160fa1bb2384f93 Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Wed, 24 Jan 2024 10:46:28 -0300 Subject: [PATCH 08/25] Updating storage to v1.1.0 --- go.mod | 2 +- go.sum | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4db0c1708..39139dd26 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/DataDog/datadog-go v4.7.0+incompatible github.com/TykTechnologies/gorpc v0.0.0-20210624160652-fe65bda0ccb9 github.com/TykTechnologies/murmur3 v0.0.0-20230310161213-aad17efd5632 - github.com/TykTechnologies/storage v1.0.11-0.20240109162024-69c223c0cc99 + github.com/TykTechnologies/storage v1.1.0 github.com/aws/aws-sdk-go-v2 v1.16.14 github.com/aws/aws-sdk-go-v2/config v1.9.0 github.com/aws/aws-sdk-go-v2/service/timestreamwrite v1.9.0 diff --git a/go.sum b/go.sum index ce1e783f5..9095b1b12 100644 --- a/go.sum +++ b/go.sum @@ -41,8 +41,8 @@ github.com/TykTechnologies/gorpc v0.0.0-20210624160652-fe65bda0ccb9 h1:fbxHiuw/2 github.com/TykTechnologies/gorpc v0.0.0-20210624160652-fe65bda0ccb9/go.mod h1:v6v7Mlj08+EmEcXOfpuTxGt2qYU9yhqqtv4QF9Wf50E= github.com/TykTechnologies/murmur3 v0.0.0-20230310161213-aad17efd5632 h1:T5NWziFusj8au5nxAqMMh/bZyX9CAyYnBkaMSsfH6BA= github.com/TykTechnologies/murmur3 v0.0.0-20230310161213-aad17efd5632/go.mod h1:UsPYgOFBpNzDXLEti7MKOwHLpVSqdzuNGkVFPspQmnQ= -github.com/TykTechnologies/storage v1.0.11-0.20240109162024-69c223c0cc99 h1:EKYVk+Wxvvaj4EdFpV0t5gx2ClLFzrELdJD55sonrsQ= -github.com/TykTechnologies/storage v1.0.11-0.20240109162024-69c223c0cc99/go.mod h1:zcANqpIsDL/l/1zsMMERmjBeJYpER9XMi/dw2Gqa7m4= +github.com/TykTechnologies/storage v1.1.0 h1:OgXbnuA0QCWWROtLRlztNTX6kKC3GkAwEEdPmvuAPE4= +github.com/TykTechnologies/storage v1.1.0/go.mod h1:zcANqpIsDL/l/1zsMMERmjBeJYpER9XMi/dw2Gqa7m4= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= @@ -97,7 +97,9 @@ github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4Yn github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bmizerany/pat v0.0.0-20170815010413-6226ea591a40/go.mod h1:8rLXio+WjiTceGBHIoTvn60HIbs7Hm7bcHjyrSqYB9c= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= github.com/c-bata/go-prompt v0.2.2/go.mod h1:VzqtzE2ksDBcdln8G7mk2RX9QyGjH+OVqOCSiVIqS34= github.com/cenkalti/backoff/v4 v4.0.2 h1:JIufpQLbh4DkbQoii76ItQIUFzevQSqOLZca4eamEDs= github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8Bog/87DQnVg= @@ -334,6 +336,7 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -790,6 +793,7 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= From feca5d209888153c322d7d455a7ae98dd687b2ce Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Wed, 24 Jan 2024 11:23:20 -0300 Subject: [PATCH 09/25] Fixing GetAndDeleteSet method --- storage/redis.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/storage/redis.go b/storage/redis.go index 483c58e6b..da9285973 100644 --- a/storage/redis.go +++ b/storage/redis.go @@ -274,14 +274,22 @@ func (r *RedisClusterStorageManager) GetAndDeleteSet(keyName string, chunkSize i "prefix": redisLogPrefix, }).Debug("Fixed keyname is: ", fixedKey) + // In Pump, we used to delete a key when chunkSize was 0. + // This is not the case with Storage Library. So we need to check if chunkSize is 0 and set it to -1. + if chunkSize == 0 { + chunkSize = -1 + } + result, err := r.db.list.Pop(ctx, fixedKey, chunkSize) if err != nil { return nil, err } - err = r.db.kv.Expire(ctx, fixedKey, expire) - if err != nil { - return nil, err + if chunkSize != -1 { + err = r.db.kv.Expire(ctx, fixedKey, expire) + if err != nil { + return nil, err + } } intResult := []interface{}{} From 476061c37764b309178c9d5938f34c784ff18695 Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Wed, 24 Jan 2024 11:26:15 -0300 Subject: [PATCH 10/25] Modifying errors to 'fatal' type --- main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index 04804dbb5..7d076e29a 100644 --- a/main.go +++ b/main.go @@ -222,7 +222,7 @@ func StartPurgeLoop(wg *sync.WaitGroup, ctx context.Context, secInterval int, ch if err != nil { log.WithFields(logrus.Fields{ "prefix": mainPrefix, - }).Error("Error trying to execute GetAndDeleteSet: " + err.Error()) + }).Fatal("Error trying to execute GetAndDeleteSet: " + err.Error()) return } if len(AnalyticsValues) > 0 { @@ -239,7 +239,7 @@ func StartPurgeLoop(wg *sync.WaitGroup, ctx context.Context, secInterval int, ch if err != nil { log.WithFields(logrus.Fields{ "prefix": mainPrefix, - }).Error("Error trying to execute GetAndDeleteSet: " + err.Error()) + }).Fatal("Error trying to execute GetAndDeleteSet: " + err.Error()) return } UptimePump.WriteUptimeData(UptimeValues) From f3db86bfe23b304e4b051ff7e823b152a4aa1c54 Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Wed, 24 Jan 2024 11:31:03 -0300 Subject: [PATCH 11/25] Running go mod tidy --- go.mod | 2 +- go.sum | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 5e01217d9..9a25d858c 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/TykTechnologies/gorpc v0.0.0-20210624160652-fe65bda0ccb9 github.com/TykTechnologies/murmur3 v0.0.0-20230310161213-aad17efd5632 github.com/TykTechnologies/storage v1.1.0 - github.com/aws/aws-sdk-go-v2 v1.16.14 + github.com/aws/aws-sdk-go-v2 v1.22.1 github.com/aws/aws-sdk-go-v2/config v1.9.0 github.com/aws/aws-sdk-go-v2/credentials v1.5.0 github.com/aws/aws-sdk-go-v2/service/sqs v1.26.0 diff --git a/go.sum b/go.sum index 9a81a4d07..bf2957168 100644 --- a/go.sum +++ b/go.sum @@ -395,6 +395,8 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6f github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU= +github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= github.com/olivere/elastic v6.2.31+incompatible h1:zwJIIsgfiDBuDS3sb6MCbm/e03BPEJoGZvqevZXM254= github.com/olivere/elastic v6.2.31+incompatible/go.mod h1:J+q1zQJTgAz9woqsbVRqGeB5G1iqDKVBWLNSYW8yfJ8= github.com/olivere/elastic/v7 v7.0.12/go.mod h1:14rWX28Pnh3qCKYRVnSGXWLf9MbLonYS/4FDCY3LAPo= From 920fed3c83a8a7c9e3a499129047a7c80b28b536 Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Wed, 24 Jan 2024 12:25:02 -0300 Subject: [PATCH 12/25] improving tests --- storage/redis.go | 16 +++++------ storage/redis_test.go | 67 +++++++++++++++++++++++++++---------------- 2 files changed, 49 insertions(+), 34 deletions(-) diff --git a/storage/redis.go b/storage/redis.go index da9285973..c6d817f62 100644 --- a/storage/redis.go +++ b/storage/redis.go @@ -95,13 +95,13 @@ type RedisClusterStorageManager struct { Config RedisStorageConfig } -func NewRedisClusterPool(forceReconnect bool, config *RedisStorageConfig) *RedisManager { +func NewRedisClusterPool(forceReconnect bool, config *RedisStorageConfig) { if !forceReconnect { if redisClusterSingleton != nil { log.WithFields(logrus.Fields{ "prefix": redisLogPrefix, }).Debug("Redis pool already INITIALISED") - return redisClusterSingleton + return } } else { if redisClusterSingleton != nil { @@ -112,7 +112,6 @@ func NewRedisClusterPool(forceReconnect bool, config *RedisStorageConfig) *Redis }).Error("Error disconnecting Redis: " + err.Error()) } - return redisClusterSingleton } } @@ -151,27 +150,25 @@ func NewRedisClusterPool(forceReconnect bool, config *RedisStorageConfig) *Redis conn, err := connector.NewConnector(model.RedisV9Type, model.WithRedisConfig(opts), model.WithTLS(tlsOptions)) if err != nil { log.WithFields(logrus.Fields{"prefix": redisLogPrefix}).Error(err) - return nil + return } kv, err := keyvalue.NewKeyValue(conn) if err != nil { log.WithFields(logrus.Fields{"prefix": redisLogPrefix}).Error(err) - return nil + return } l, err := list.NewList(conn) if err != nil { log.WithFields(logrus.Fields{"prefix": redisLogPrefix}).Error(err) - return nil + return } redisClusterSingleton = &RedisManager{} redisClusterSingleton.kv = kv redisClusterSingleton.list = l redisClusterSingleton.conn = conn - - return redisClusterSingleton } func getRedisAddrs(config RedisStorageConfig) (addrs []string) { @@ -224,7 +221,8 @@ func (r *RedisClusterStorageManager) Connect() bool { log.WithFields(logrus.Fields{ "prefix": redisLogPrefix, }).Debug("Connecting to redis cluster") - r.db = NewRedisClusterPool(false, &r.Config) + NewRedisClusterPool(false, &r.Config) + r.db = redisClusterSingleton return true } diff --git a/storage/redis_test.go b/storage/redis_test.go index 04ed1fc74..368d26106 100644 --- a/storage/redis_test.go +++ b/storage/redis_test.go @@ -81,12 +81,7 @@ func TestRedisClusterStorageManager_GetAndDeleteSet(t *testing.T) { conf["host"] = "localhost" conf["port"] = 6379 - r := RedisClusterStorageManager{ - Config: RedisStorageConfig{ - Host: "localhost", - Port: 6379, - }, - } + r := RedisClusterStorageManager{} if err := r.Init(conf); err != nil { t.Fatal("unable to connect", err.Error()) } @@ -144,31 +139,53 @@ func TestRedisClusterStorageManager_GetAndDeleteSet(t *testing.T) { } func TestNewRedisClusterPool(t *testing.T) { - tcs := []struct { - name string - cfg *RedisStorageConfig + testCases := []struct { + forceReconnect bool + config *RedisStorageConfig + expectNil bool + expectConnection bool + testName string }{ { - name: "connect to localhost:6379", - cfg: &RedisStorageConfig{ - Host: "localhost", - Port: 6379, - }, + testName: "Connect to localhost:6379", + config: &RedisStorageConfig{Host: "localhost", Port: 6379}, + expectNil: false, + expectConnection: true, + }, + { + testName: "Force reconnect with existing singleton", + forceReconnect: true, + config: &RedisStorageConfig{Host: "localhost", Port: 6379}, + expectNil: false, + expectConnection: true, + }, + + { + testName: "Invalid configuration", + config: &RedisStorageConfig{Host: "invalid-host", Port: 6379}, + expectNil: false, + expectConnection: false, }, } - for _, tc := range tcs { - t.Run(tc.name, func(t *testing.T) { - pool := NewRedisClusterPool(false, tc.cfg) - if pool == nil { - t.Fatal("pool is nil") - } + for _, tc := range testCases { + t.Run(tc.testName, func(t *testing.T) { + NewRedisClusterPool(tc.forceReconnect, tc.config) + + if tc.expectNil { + assert.Nil(t, redisClusterSingleton, "Expected redisClusterSingleton to be nil") + } else { + assert.NotNil(t, redisClusterSingleton, "Expected redisClusterSingleton not to be nil") - assert.NotNil(t, pool.conn) - assert.NotNil(t, pool.kv) - assert.NotNil(t, pool.list) - assert.Equal(t, pool.conn.Type(), model.RedisV9Type) - assert.NoError(t, pool.conn.Ping(context.Background())) + assert.NotNil(t, redisClusterSingleton.conn, "Expected connection not to be nil") + assert.NotNil(t, redisClusterSingleton.kv, "Expected kv not to be nil") + assert.NotNil(t, redisClusterSingleton.list, "Expected list not to be nil") + assert.Equal(t, model.RedisV9Type, redisClusterSingleton.conn.Type(), "Expected connection type to be RedisV9Type") + + if tc.expectConnection { + assert.NoError(t, redisClusterSingleton.conn.Ping(context.Background()), "Expected no error on ping") + } + } }) } } From 048684cf5ca2b40859cd986a33dbfab2d3b3fbe6 Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Wed, 24 Jan 2024 12:26:40 -0300 Subject: [PATCH 13/25] improving tests --- storage/redis_test.go | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/storage/redis_test.go b/storage/redis_test.go index 368d26106..53fb97c16 100644 --- a/storage/redis_test.go +++ b/storage/redis_test.go @@ -142,29 +142,26 @@ func TestNewRedisClusterPool(t *testing.T) { testCases := []struct { forceReconnect bool config *RedisStorageConfig - expectNil bool expectConnection bool testName string }{ { testName: "Connect to localhost:6379", config: &RedisStorageConfig{Host: "localhost", Port: 6379}, - expectNil: false, expectConnection: true, }, { testName: "Force reconnect with existing singleton", forceReconnect: true, config: &RedisStorageConfig{Host: "localhost", Port: 6379}, - expectNil: false, expectConnection: true, }, { testName: "Invalid configuration", config: &RedisStorageConfig{Host: "invalid-host", Port: 6379}, - expectNil: false, expectConnection: false, + forceReconnect: true, }, } @@ -172,20 +169,19 @@ func TestNewRedisClusterPool(t *testing.T) { t.Run(tc.testName, func(t *testing.T) { NewRedisClusterPool(tc.forceReconnect, tc.config) - if tc.expectNil { - assert.Nil(t, redisClusterSingleton, "Expected redisClusterSingleton to be nil") - } else { - assert.NotNil(t, redisClusterSingleton, "Expected redisClusterSingleton not to be nil") + assert.NotNil(t, redisClusterSingleton, "Expected redisClusterSingleton not to be nil") - assert.NotNil(t, redisClusterSingleton.conn, "Expected connection not to be nil") - assert.NotNil(t, redisClusterSingleton.kv, "Expected kv not to be nil") - assert.NotNil(t, redisClusterSingleton.list, "Expected list not to be nil") - assert.Equal(t, model.RedisV9Type, redisClusterSingleton.conn.Type(), "Expected connection type to be RedisV9Type") + assert.NotNil(t, redisClusterSingleton.conn, "Expected connection not to be nil") + assert.NotNil(t, redisClusterSingleton.kv, "Expected kv not to be nil") + assert.NotNil(t, redisClusterSingleton.list, "Expected list not to be nil") + assert.Equal(t, model.RedisV9Type, redisClusterSingleton.conn.Type(), "Expected connection type to be RedisV9Type") - if tc.expectConnection { - assert.NoError(t, redisClusterSingleton.conn.Ping(context.Background()), "Expected no error on ping") - } + if tc.expectConnection { + assert.NoError(t, redisClusterSingleton.conn.Ping(context.Background()), "Expected no error on ping") + } else { + assert.Error(t, redisClusterSingleton.conn.Ping(context.Background()), "Expected error on ping") } + }) } } From 1cef0ff8f80b01f7b4d5741ae63259c0d1db7874 Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Wed, 24 Jan 2024 13:37:15 -0300 Subject: [PATCH 14/25] linting --- storage/redis.go | 1 - storage/redis_test.go | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/storage/redis.go b/storage/redis.go index c6d817f62..a589893c0 100644 --- a/storage/redis.go +++ b/storage/redis.go @@ -111,7 +111,6 @@ func NewRedisClusterPool(forceReconnect bool, config *RedisStorageConfig) { "prefix": redisLogPrefix, }).Error("Error disconnecting Redis: " + err.Error()) } - } } diff --git a/storage/redis_test.go b/storage/redis_test.go index 53fb97c16..d569ed94b 100644 --- a/storage/redis_test.go +++ b/storage/redis_test.go @@ -11,7 +11,6 @@ import ( ) func TestRedisAddressConfiguration(t *testing.T) { - t.Run("Host but no port", func(t *testing.T) { cfg := RedisStorageConfig{Host: "host"} if len(getRedisAddrs(cfg)) != 0 { @@ -43,7 +42,6 @@ func TestRedisAddressConfiguration(t *testing.T) { t.Fatal("Wrong address") } }) - } var testData = []struct { @@ -140,10 +138,10 @@ func TestRedisClusterStorageManager_GetAndDeleteSet(t *testing.T) { func TestNewRedisClusterPool(t *testing.T) { testCases := []struct { - forceReconnect bool config *RedisStorageConfig - expectConnection bool testName string + forceReconnect bool + expectConnection bool }{ { testName: "Connect to localhost:6379", @@ -181,7 +179,6 @@ func TestNewRedisClusterPool(t *testing.T) { } else { assert.Error(t, redisClusterSingleton.conn.Ping(context.Background()), "Expected error on ping") } - }) } } From 7d412e0af0d2d117a5465536160be9037f8da032 Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Thu, 25 Jan 2024 09:46:06 -0300 Subject: [PATCH 15/25] removing fatal error --- config.go | 8 +- main.go | 34 +- storage/init.go | 4 +- storage/redis.go | 341 ---------------- storage/temporal_storage.go | 369 ++++++++++++++++++ ...redis_test.go => temporal_storage_test.go} | 62 +-- 6 files changed, 414 insertions(+), 404 deletions(-) delete mode 100644 storage/redis.go create mode 100644 storage/temporal_storage.go rename storage/{redis_test.go => temporal_storage_test.go} (63%) diff --git a/config.go b/config.go index bdc3c3fdd..762c7a4b4 100644 --- a/config.go +++ b/config.go @@ -175,7 +175,7 @@ type TykPumpConfiguration struct { // Sets the analytics storage type. Where the pump will be fetching data from. Currently, only // the `redis` option is supported. AnalyticsStorageType string `json:"analytics_storage_type"` - // Example Redis storage configuration: + // Example Temporal storage configuration: // ```{.json} // "analytics_storage_config": { // "type": "redis", @@ -188,11 +188,11 @@ type TykPumpConfiguration struct { // "optimisation_max_idle": 100, // "optimisation_max_active": 0, // "enable_cluster": false, - // "redis_use_ssl": false, - // "redis_ssl_insecure_skip_verify": false + // "use_ssl": false, + // "ssl_insecure_skip_verify": false // }, // ``` - AnalyticsStorageConfig storage.RedisStorageConfig `json:"analytics_storage_config"` + AnalyticsStorageConfig storage.TemporalStorageConfig `json:"analytics_storage_config"` // Connection string for StatsD monitoring for information please see the // [Instrumentation docs](https://tyk.io/docs/basic-config-and-security/report-monitor-trigger-events/instrumentation/). StatsdConnectionString string `json:"statsd_connection_string"` diff --git a/main.go b/main.go index 7d076e29a..20730f01e 100644 --- a/main.go +++ b/main.go @@ -102,11 +102,27 @@ func Init() { func setupAnalyticsStore() { switch SystemConfig.AnalyticsStorageType { case "redis": - AnalyticsStore = &storage.RedisClusterStorageManager{} - UptimeStorage = &storage.RedisClusterStorageManager{} + AnalyticsStore = &storage.TemporalStorageHandler{ + Config: storage.TemporalStorageConfig{ + Type: "redis", + }, + } + UptimeStorage = &storage.TemporalStorageHandler{ + Config: storage.TemporalStorageConfig{ + Type: "redis", + }, + } default: - AnalyticsStore = &storage.RedisClusterStorageManager{} - UptimeStorage = &storage.RedisClusterStorageManager{} + AnalyticsStore = &storage.TemporalStorageHandler{ + Config: storage.TemporalStorageConfig{ + Type: "redis", + }, + } + UptimeStorage = &storage.TemporalStorageHandler{ + Config: storage.TemporalStorageConfig{ + Type: "redis", + }, + } } AnalyticsStore.Init(SystemConfig.AnalyticsStorageConfig) @@ -115,12 +131,12 @@ func setupAnalyticsStore() { uptimeConf := SystemConfig.AnalyticsStorageConfig // Swap key prefixes for uptime purger - uptimeConf.RedisKeyPrefix = "host-checker:" + uptimeConf.KeyPrefix = "host-checker:" UptimeStorage.Init(uptimeConf) } func storeVersion() { - var versionStore = &storage.RedisClusterStorageManager{} + var versionStore = &storage.TemporalStorageHandler{} versionConf := SystemConfig.AnalyticsStorageConfig versionStore.KeyPrefix = "version-check-" versionStore.Config = versionConf @@ -222,8 +238,7 @@ func StartPurgeLoop(wg *sync.WaitGroup, ctx context.Context, secInterval int, ch if err != nil { log.WithFields(logrus.Fields{ "prefix": mainPrefix, - }).Fatal("Error trying to execute GetAndDeleteSet: " + err.Error()) - return + }).Error("Error trying to execute GetAndDeleteSet: " + err.Error()) } if len(AnalyticsValues) > 0 { PreprocessAnalyticsValues(AnalyticsValues, serializerMethod, analyticsKeyName, omitDetails, job, startTime, secInterval) @@ -239,8 +254,7 @@ func StartPurgeLoop(wg *sync.WaitGroup, ctx context.Context, secInterval int, ch if err != nil { log.WithFields(logrus.Fields{ "prefix": mainPrefix, - }).Fatal("Error trying to execute GetAndDeleteSet: " + err.Error()) - return + }).Error("Error trying to execute GetAndDeleteSet: " + err.Error()) } UptimePump.WriteUptimeData(UptimeValues) } diff --git a/storage/init.go b/storage/init.go index bd853ca88..f2d7ea856 100644 --- a/storage/init.go +++ b/storage/init.go @@ -11,5 +11,7 @@ func init() { AvailableStores = make(map[string]AnalyticsStorage) // Register all the storage handlers here - AvailableStores["redis"] = &RedisClusterStorageManager{} + AvailableStores["redis"] = &TemporalStorageHandler{Config: TemporalStorageConfig{ + Type: "redis", + }} } diff --git a/storage/redis.go b/storage/redis.go deleted file mode 100644 index a589893c0..000000000 --- a/storage/redis.go +++ /dev/null @@ -1,341 +0,0 @@ -package storage - -import ( - "context" - "strconv" - "strings" - "time" - - "github.com/TykTechnologies/storage/temporal/connector" - keyvalue "github.com/TykTechnologies/storage/temporal/keyvalue" - "github.com/TykTechnologies/storage/temporal/list" - "github.com/TykTechnologies/storage/temporal/model" - - "github.com/sirupsen/logrus" - - "github.com/kelseyhightower/envconfig" - "github.com/mitchellh/mapstructure" -) - -// ------------------- REDIS CLUSTER STORAGE MANAGER ------------------------------- - -var ( - redisClusterSingleton *RedisManager - redisLogPrefix = "redis" - envRedisPrefix = "TYK_PMP_REDIS" - ctx = context.Background() -) - -type RedisManager struct { - list list.List - kv keyvalue.KeyValue - conn model.Connector -} - -type EnvMapString map[string]string - -func (e *EnvMapString) Decode(value string) error { - units := strings.Split(value, ",") - m := make(map[string]string) - for _, unit := range units { - kvArr := strings.Split(unit, ":") - if len(kvArr) > 1 { - m[kvArr[0]] = kvArr[1] - } - } - - *e = m - - return nil -} - -type RedisStorageConfig struct { - // Deprecated. - Type string `json:"type" mapstructure:"type"` - // Redis host value. - Host string `json:"host" mapstructure:"host"` - // Redis port value. - Port int `json:"port" mapstructure:"port"` - // Deprecated. Use Addrs instead. - Hosts EnvMapString `json:"hosts" mapstructure:"hosts"` - // Use instead of the host value if you're running a redis cluster with mutliple instances. - Addrs []string `json:"addrs" mapstructure:"addrs"` - // Sentinel redis master name. - MasterName string `json:"master_name" mapstructure:"master_name"` - // Sentinel redis password. - SentinelPassword string `json:"sentinel_password" mapstructure:"sentinel_password"` - // Redis username. - Username string `json:"username" mapstructure:"username"` - // Redis password. - Password string `json:"password" mapstructure:"password"` - // Redis database. - Database int `json:"database" mapstructure:"database"` - // How long to allow for new connections to be established (in milliseconds). Defaults to 5sec. - Timeout int `json:"timeout" mapstructure:"timeout"` - // Maximum number of idle connections in the pool. - MaxIdle int `json:"optimisation_max_idle" mapstructure:"optimisation_max_idle"` - // 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. Defaults to 500. - MaxActive int `json:"optimisation_max_active" mapstructure:"optimisation_max_active"` - // Enable this option if you are using a redis cluster. Default is `false`. - EnableCluster bool `json:"enable_cluster" mapstructure:"enable_cluster"` - // Prefix the redis key names. Defaults to "analytics-". - RedisKeyPrefix string `json:"redis_key_prefix" mapstructure:"redis_key_prefix"` - // Setting this to true to use SSL when connecting to Redis. - RedisUseSSL bool `json:"redis_use_ssl" mapstructure:"redis_use_ssl"` - // Set this to `true` to tell Pump to ignore Redis' cert validation. - RedisSSLInsecureSkipVerify bool `json:"redis_ssl_insecure_skip_verify" mapstructure:"redis_ssl_insecure_skip_verify"` -} - -// RedisClusterStorageManager is a storage manager that uses the redis database. -type RedisClusterStorageManager struct { - db *RedisManager - KeyPrefix string - HashKeys bool - Config RedisStorageConfig -} - -func NewRedisClusterPool(forceReconnect bool, config *RedisStorageConfig) { - if !forceReconnect { - if redisClusterSingleton != nil { - log.WithFields(logrus.Fields{ - "prefix": redisLogPrefix, - }).Debug("Redis pool already INITIALISED") - return - } - } else { - if redisClusterSingleton != nil { - err := redisClusterSingleton.conn.Disconnect(ctx) - if err != nil { - log.WithFields(logrus.Fields{ - "prefix": redisLogPrefix, - }).Error("Error disconnecting Redis: " + err.Error()) - } - } - } - - log.WithFields(logrus.Fields{ - "prefix": redisLogPrefix, - }).Debug("Creating new Redis connection pool") - - maxActive := 500 - if config.MaxActive > 0 { - maxActive = config.MaxActive - } - - timeout := 5 - - if config.Timeout > 0 { - timeout = config.Timeout - } - - opts := &model.RedisOptions{ - MasterName: config.MasterName, - SentinelPassword: config.SentinelPassword, - Addrs: getRedisAddrs(*config), - Database: config.Database, - Username: config.Username, - Password: config.Password, - MaxActive: maxActive, - Timeout: timeout, - EnableCluster: config.EnableCluster, - } - - tlsOptions := &model.TLS{ - Enable: config.RedisUseSSL, - InsecureSkipVerify: config.RedisSSLInsecureSkipVerify, - } - - conn, err := connector.NewConnector(model.RedisV9Type, model.WithRedisConfig(opts), model.WithTLS(tlsOptions)) - if err != nil { - log.WithFields(logrus.Fields{"prefix": redisLogPrefix}).Error(err) - return - } - - kv, err := keyvalue.NewKeyValue(conn) - if err != nil { - log.WithFields(logrus.Fields{"prefix": redisLogPrefix}).Error(err) - return - } - - l, err := list.NewList(conn) - if err != nil { - log.WithFields(logrus.Fields{"prefix": redisLogPrefix}).Error(err) - return - } - - redisClusterSingleton = &RedisManager{} - redisClusterSingleton.kv = kv - redisClusterSingleton.list = l - redisClusterSingleton.conn = conn -} - -func getRedisAddrs(config RedisStorageConfig) (addrs []string) { - if len(config.Addrs) != 0 { - addrs = config.Addrs - } else { - for h, p := range config.Hosts { - addr := h + ":" + p - addrs = append(addrs, addr) - } - } - - if len(addrs) == 0 && config.Port != 0 { - addr := config.Host + ":" + strconv.Itoa(config.Port) - addrs = append(addrs, addr) - } - - return addrs -} - -func (r *RedisClusterStorageManager) GetName() string { - return "redis" -} - -func (r *RedisClusterStorageManager) Init(config interface{}) error { - r.Config = RedisStorageConfig{} - err := mapstructure.Decode(config, &r.Config) - if err != nil { - log.WithFields(logrus.Fields{ - "prefix": redisLogPrefix, - }).Fatal("Failed to decode configuration: ", err) - } - - overrideErr := envconfig.Process(envRedisPrefix, &r.Config) - if overrideErr != nil { - log.Error("Failed to process environment variables for redis: ", overrideErr) - } - - if r.Config.RedisKeyPrefix == "" { - r.KeyPrefix = RedisKeyPrefix - } else { - r.KeyPrefix = r.Config.RedisKeyPrefix - } - return nil -} - -// Connect will establish a connection to the r.db -func (r *RedisClusterStorageManager) Connect() bool { - if r.db == nil { - log.WithFields(logrus.Fields{ - "prefix": redisLogPrefix, - }).Debug("Connecting to redis cluster") - NewRedisClusterPool(false, &r.Config) - r.db = redisClusterSingleton - return true - } - - log.WithFields(logrus.Fields{ - "prefix": redisLogPrefix, - }).Debug("Storage Engine already initialized...") - - // Reset it just in case - r.db = redisClusterSingleton - return true -} - -func (r *RedisClusterStorageManager) hashKey(in string) string { - return in -} - -func (r *RedisClusterStorageManager) fixKey(keyName string) string { - setKeyName := r.KeyPrefix + r.hashKey(keyName) - - log.WithFields(logrus.Fields{ - "prefix": redisLogPrefix, - }).Debug("Input key was: ", setKeyName) - - return setKeyName -} - -func (r *RedisClusterStorageManager) GetAndDeleteSet(keyName string, chunkSize int64, expire time.Duration) ([]interface{}, error) { - log.WithFields(logrus.Fields{ - "prefix": redisLogPrefix, - }).Debug("Getting raw key set: ", keyName) - - if r.db == nil { - log.WithFields(logrus.Fields{ - "prefix": redisLogPrefix, - }).Warning("Connection dropped, connecting..") - r.Connect() - return r.GetAndDeleteSet(keyName, chunkSize, expire) - } - - log.WithFields(logrus.Fields{ - "prefix": redisLogPrefix, - }).Debug("keyName is: ", keyName) - - fixedKey := r.fixKey(keyName) - - log.WithFields(logrus.Fields{ - "prefix": redisLogPrefix, - }).Debug("Fixed keyname is: ", fixedKey) - - // In Pump, we used to delete a key when chunkSize was 0. - // This is not the case with Storage Library. So we need to check if chunkSize is 0 and set it to -1. - if chunkSize == 0 { - chunkSize = -1 - } - - result, err := r.db.list.Pop(ctx, fixedKey, chunkSize) - if err != nil { - return nil, err - } - - if chunkSize != -1 { - err = r.db.kv.Expire(ctx, fixedKey, expire) - if err != nil { - return nil, err - } - } - - intResult := []interface{}{} - for _, v := range result { - intResult = append(intResult, v) - } - - return intResult, nil -} - -// SetKey will create (or update) a key value in the store -func (r *RedisClusterStorageManager) SetKey(keyName, session string, timeout int64) error { - log.Debug("[STORE] SET Raw key is: ", keyName) - log.Debug("[STORE] Setting key: ", r.fixKey(keyName)) - - r.ensureConnection() - err := r.db.kv.Set(ctx, r.fixKey(keyName), session, 0) - if timeout > 0 { - if err := r.SetExp(keyName, timeout); err != nil { - return err - } - } - if err != nil { - log.Error("Error trying to set value: ", err) - return err - } - return nil -} - -func (r *RedisClusterStorageManager) SetExp(keyName string, timeout int64) error { - err := r.db.kv.Expire(ctx, r.fixKey(keyName), time.Duration(timeout)*time.Second) - if err != nil { - log.Error("Could not EXPIRE key: ", err) - } - return err -} - -func (r *RedisClusterStorageManager) ensureConnection() { - if r.db != nil { - // already connected - return - } - log.Info("Connection dropped, reconnecting...") - for { - r.Connect() - if r.db != nil { - // reconnection worked - return - } - log.Info("Reconnecting again...") - } -} diff --git a/storage/temporal_storage.go b/storage/temporal_storage.go new file mode 100644 index 000000000..7800b1d8f --- /dev/null +++ b/storage/temporal_storage.go @@ -0,0 +1,369 @@ +package storage + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/TykTechnologies/storage/temporal/connector" + keyvalue "github.com/TykTechnologies/storage/temporal/keyvalue" + "github.com/TykTechnologies/storage/temporal/list" + "github.com/TykTechnologies/storage/temporal/model" + + "github.com/sirupsen/logrus" + + "github.com/kelseyhightower/envconfig" + "github.com/mitchellh/mapstructure" +) + +// ------------------- TEMPORAL CLUSTER STORAGE MANAGER ------------------------------- + +var ( + temporalStorageSingleton *storageHandler + logPrefix = "temporal-storage" + // Deprecated. Use envTemporalStoragePrefix instead. + envRedisPrefix = "TYK_PMP_REDIS" + envTemporalStoragePrefix = "TYK_PMP_TEMPORAL_STORAGE" + ctx = context.Background() +) + +type storageHandler struct { + list list.List + kv keyvalue.KeyValue + conn model.Connector +} + +type EnvMapString map[string]string + +func (e *EnvMapString) Decode(value string) error { + units := strings.Split(value, ",") + m := make(map[string]string) + for _, unit := range units { + kvArr := strings.Split(unit, ":") + if len(kvArr) > 1 { + m[kvArr[0]] = kvArr[1] + } + } + + *e = m + + return nil +} + +type TemporalStorageConfig struct { + // Deprecated. + Type string `json:"type" mapstructure:"type"` + // Host value. For example: "localhost". + Host string `json:"host" mapstructure:"host"` + // Port value. For example: 6379. + Port int `json:"port" mapstructure:"port"` + // Deprecated. Use Addrs instead. + Hosts EnvMapString `json:"hosts" mapstructure:"hosts"` + // Use instead of the host value if you're running a cluster instance with mutliple instances. + Addrs []string `json:"addrs" mapstructure:"addrs"` + // Sentinel master name. + MasterName string `json:"master_name" mapstructure:"master_name"` + // Sentinel password. + SentinelPassword string `json:"sentinel_password" mapstructure:"sentinel_password"` + // DB username. + Username string `json:"username" mapstructure:"username"` + // DB password. + Password string `json:"password" mapstructure:"password"` + // Database name. + Database int `json:"database" mapstructure:"database"` + // How long to allow for new connections to be established (in milliseconds). Defaults to 5sec. + Timeout int `json:"timeout" mapstructure:"timeout"` + // Maximum number of idle connections in the pool. + MaxIdle int `json:"optimisation_max_idle" mapstructure:"optimisation_max_idle"` + // 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. Defaults to 500. + MaxActive int `json:"optimisation_max_active" mapstructure:"optimisation_max_active"` + // Enable this option if you are using a cluster instance. Default is `false`. + EnableCluster bool `json:"enable_cluster" mapstructure:"enable_cluster"` + // Prefix the key names. Defaults to "analytics-". + // Deprecated. Use KeyPrefix instead. + RedisKeyPrefix string `json:"redis_key_prefix" mapstructure:"redis_key_prefix"` + // Setting this to true to use SSL when connecting to the DB. + // Deprecated. Use UseSSL instead. + RedisUseSSL bool `json:"redis_use_ssl" mapstructure:"redis_use_ssl"` + // Set this to `true` to tell Pump to ignore database's cert validation. + // Deprecated. Use SSLInsecureSkipVerify instead. + RedisSSLInsecureSkipVerify bool `json:"redis_ssl_insecure_skip_verify" mapstructure:"redis_ssl_insecure_skip_verify"` + // Prefix the key names. Defaults to "analytics-". + KeyPrefix string `json:"key_prefix" mapstructure:"key_prefix"` + // Setting this to true to use SSL when connecting to the DB. + UseSSL bool `json:"use_ssl" mapstructure:"use_ssl"` + // Set this to `true` to tell Pump to ignore database's cert validation. + SSLInsecureSkipVerify bool `json:"ssl_insecure_skip_verify" mapstructure:"ssl_insecure_skip_verify"` +} + +// TemporalStorageHandler is a storage manager that uses non data-persistent databases, like Redis. +type TemporalStorageHandler struct { + db *storageHandler + KeyPrefix string + HashKeys bool + Config TemporalStorageConfig +} + +func NewTemporalStorageHandler(forceReconnect bool, config *TemporalStorageConfig) error { + switch config.Type { + case "redis", "": + if !forceReconnect { + if temporalStorageSingleton != nil { + log.WithFields(logrus.Fields{ + "prefix": logPrefix, + }).Debug("Redis pool already INITIALISED") + return nil + } + } else { + if temporalStorageSingleton != nil { + err := temporalStorageSingleton.conn.Disconnect(ctx) + if err != nil { + return fmt.Errorf("error disconnecting Redis: %s", err.Error()) + } + } + } + + log.WithFields(logrus.Fields{ + "prefix": logPrefix, + }).Debug("Creating new Redis connection pool") + + maxActive := 500 + if config.MaxActive > 0 { + maxActive = config.MaxActive + } + + timeout := 5 + + if config.Timeout > 0 { + timeout = config.Timeout + } + + opts := &model.RedisOptions{ + MasterName: config.MasterName, + SentinelPassword: config.SentinelPassword, + Addrs: config.Addrs, + Database: config.Database, + Username: config.Username, + Password: config.Password, + MaxActive: maxActive, + Timeout: timeout, + EnableCluster: config.EnableCluster, + } + + enableTLS := config.UseSSL + if !enableTLS { + enableTLS = config.RedisUseSSL + } + + insecureSkipVerify := config.SSLInsecureSkipVerify + if !insecureSkipVerify { + insecureSkipVerify = config.RedisSSLInsecureSkipVerify + } + + tlsOptions := &model.TLS{ + Enable: enableTLS, + InsecureSkipVerify: insecureSkipVerify, + } + + conn, err := connector.NewConnector(model.RedisV9Type, model.WithRedisConfig(opts), model.WithTLS(tlsOptions)) + if err != nil { + return err + } + + kv, err := keyvalue.NewKeyValue(conn) + if err != nil { + return err + } + + l, err := list.NewList(conn) + if err != nil { + return err + } + + temporalStorageSingleton = &storageHandler{} + temporalStorageSingleton.kv = kv + temporalStorageSingleton.list = l + temporalStorageSingleton.conn = conn + + return nil + default: + return fmt.Errorf("unsupported database type: %s", config.Type) + } + +} + +func (r *TemporalStorageHandler) GetName() string { + if r.Config.Type != "" { + return r.Config.Type + } + + return "redis" +} + +func (r *TemporalStorageHandler) Init(config interface{}) error { + r.Config = TemporalStorageConfig{} + err := mapstructure.Decode(config, &r.Config) + if err != nil { + log.WithFields(logrus.Fields{ + "prefix": logPrefix, + }).Fatal("Failed to decode configuration: ", err) + } + + overrideErr := envconfig.Process(envRedisPrefix, &r.Config) + if overrideErr != nil { + log.Error("Failed to process environment variables from redis prefix: ", overrideErr) + } + + overrideErr = envconfig.Process(envTemporalStoragePrefix, &r.Config) + if overrideErr != nil { + log.Error("Failed to process environment variables from temporal storage prefix: ", overrideErr) + } + + if r.Config.KeyPrefix != "" { + r.KeyPrefix = r.Config.KeyPrefix + } else if r.Config.RedisKeyPrefix != "" { + r.KeyPrefix = r.Config.RedisKeyPrefix + } else { + r.KeyPrefix = RedisKeyPrefix + } + + if r.Config.Type != "" { + logPrefix = r.Config.Type + } + + return nil +} + +// Connect will establish a connection to the r.db +func (r *TemporalStorageHandler) Connect() bool { + if r.db == nil { + log.WithFields(logrus.Fields{ + "prefix": logPrefix, + }).Debug("Connecting to temporal storage") + err := NewTemporalStorageHandler(false, &r.Config) + if err != nil { + log.WithFields(logrus.Fields{ + "prefix": logPrefix, + }).Error("Error connecting to temporal storage: ", err) + return false + } + r.db = temporalStorageSingleton + return true + } + + log.WithFields(logrus.Fields{ + "prefix": logPrefix, + }).Debug("Storage Engine already initialized...") + + // Reset it just in case + r.db = temporalStorageSingleton + return true +} + +func (r *TemporalStorageHandler) hashKey(in string) string { + return in +} + +func (r *TemporalStorageHandler) fixKey(keyName string) string { + setKeyName := r.KeyPrefix + r.hashKey(keyName) + + log.WithFields(logrus.Fields{ + "prefix": logPrefix, + }).Debug("Input key was: ", setKeyName) + + return setKeyName +} + +func (r *TemporalStorageHandler) GetAndDeleteSet(keyName string, chunkSize int64, expire time.Duration) ([]interface{}, error) { + log.WithFields(logrus.Fields{ + "prefix": logPrefix, + }).Debug("Getting raw key set: ", keyName) + + if r.db == nil { + log.WithFields(logrus.Fields{ + "prefix": logPrefix, + }).Warning("Connection dropped, connecting..") + r.Connect() + return r.GetAndDeleteSet(keyName, chunkSize, expire) + } + + log.WithFields(logrus.Fields{ + "prefix": logPrefix, + }).Debug("keyName is: ", keyName) + + fixedKey := r.fixKey(keyName) + + log.WithFields(logrus.Fields{ + "prefix": logPrefix, + }).Debug("Fixed keyname is: ", fixedKey) + + // In Pump, we used to delete a key when chunkSize was 0. + // This is not the case with Storage Library. So we need to check if chunkSize is 0 and set it to -1. + if chunkSize == 0 { + chunkSize = -1 + } + + result, err := r.db.list.Pop(ctx, fixedKey, chunkSize) + if err != nil { + return nil, err + } + + if chunkSize != -1 { + err = r.db.kv.Expire(ctx, fixedKey, expire) + if err != nil { + return nil, err + } + } + + intResult := []interface{}{} + for _, v := range result { + intResult = append(intResult, v) + } + + return intResult, nil +} + +// SetKey will create (or update) a key value in the store +func (r *TemporalStorageHandler) SetKey(keyName, session string, timeout int64) error { + log.Debug("[STORE] SET Raw key is: ", keyName) + log.Debug("[STORE] Setting key: ", r.fixKey(keyName)) + + r.ensureConnection() + err := r.db.kv.Set(ctx, r.fixKey(keyName), session, 0) + if timeout > 0 { + if err := r.SetExp(keyName, timeout); err != nil { + return err + } + } + if err != nil { + log.Error("Error trying to set value: ", err) + return err + } + return nil +} + +func (r *TemporalStorageHandler) SetExp(keyName string, timeout int64) error { + err := r.db.kv.Expire(ctx, r.fixKey(keyName), time.Duration(timeout)*time.Second) + if err != nil { + log.Error("Could not EXPIRE key: ", err) + } + return err +} + +func (r *TemporalStorageHandler) ensureConnection() { + if r.db != nil { + // already connected + return + } + log.Info("Connection dropped, reconnecting...") + for { + r.Connect() + if r.db != nil { + // reconnection worked + return + } + log.Info("Reconnecting again...") + } +} diff --git a/storage/redis_test.go b/storage/temporal_storage_test.go similarity index 63% rename from storage/redis_test.go rename to storage/temporal_storage_test.go index d569ed94b..03af19355 100644 --- a/storage/redis_test.go +++ b/storage/temporal_storage_test.go @@ -10,40 +10,6 @@ import ( "github.com/stretchr/testify/assert" ) -func TestRedisAddressConfiguration(t *testing.T) { - t.Run("Host but no port", func(t *testing.T) { - cfg := RedisStorageConfig{Host: "host"} - if len(getRedisAddrs(cfg)) != 0 { - t.Fatal("Port is 0, there is no valid addr") - } - }) - - t.Run("Port but no host", func(t *testing.T) { - cfg := RedisStorageConfig{Port: 30000} - - addrs := getRedisAddrs(cfg) - if addrs[0] != ":30000" || len(addrs) != 1 { - t.Fatal("Port is valid, it is a valid addr") - } - }) - - t.Run("addrs parameter should have precedence", func(t *testing.T) { - cfg := RedisStorageConfig{Host: "host", Port: 30000} - - addrs := getRedisAddrs(cfg) - if addrs[0] != "host:30000" || len(addrs) != 1 { - t.Fatal("Wrong address") - } - - cfg.Addrs = []string{"override:30000"} - - addrs = getRedisAddrs(cfg) - if addrs[0] != "override:30000" || len(addrs) != 1 { - t.Fatal("Wrong address") - } - }) -} - var testData = []struct { in []string chunk int64 @@ -79,7 +45,7 @@ func TestRedisClusterStorageManager_GetAndDeleteSet(t *testing.T) { conf["host"] = "localhost" conf["port"] = 6379 - r := RedisClusterStorageManager{} + r := TemporalStorageHandler{} if err := r.Init(conf); err != nil { t.Fatal("unable to connect", err.Error()) } @@ -136,28 +102,28 @@ func TestRedisClusterStorageManager_GetAndDeleteSet(t *testing.T) { } } -func TestNewRedisClusterPool(t *testing.T) { +func TestNewTemporalClusterStorageHandler(t *testing.T) { testCases := []struct { - config *RedisStorageConfig + config *TemporalStorageConfig testName string forceReconnect bool expectConnection bool }{ { testName: "Connect to localhost:6379", - config: &RedisStorageConfig{Host: "localhost", Port: 6379}, + config: &TemporalStorageConfig{Host: "localhost", Port: 6379}, expectConnection: true, }, { testName: "Force reconnect with existing singleton", forceReconnect: true, - config: &RedisStorageConfig{Host: "localhost", Port: 6379}, + config: &TemporalStorageConfig{Host: "localhost", Port: 6379}, expectConnection: true, }, { testName: "Invalid configuration", - config: &RedisStorageConfig{Host: "invalid-host", Port: 6379}, + config: &TemporalStorageConfig{Host: "invalid-host", Port: 6379}, expectConnection: false, forceReconnect: true, }, @@ -165,19 +131,19 @@ func TestNewRedisClusterPool(t *testing.T) { for _, tc := range testCases { t.Run(tc.testName, func(t *testing.T) { - NewRedisClusterPool(tc.forceReconnect, tc.config) + NewTemporalStorageHandler(tc.forceReconnect, tc.config) - assert.NotNil(t, redisClusterSingleton, "Expected redisClusterSingleton not to be nil") + assert.NotNil(t, temporalStorageSingleton, "Expected temporalStorageSingleton not to be nil") - assert.NotNil(t, redisClusterSingleton.conn, "Expected connection not to be nil") - assert.NotNil(t, redisClusterSingleton.kv, "Expected kv not to be nil") - assert.NotNil(t, redisClusterSingleton.list, "Expected list not to be nil") - assert.Equal(t, model.RedisV9Type, redisClusterSingleton.conn.Type(), "Expected connection type to be RedisV9Type") + assert.NotNil(t, temporalStorageSingleton.conn, "Expected connection not to be nil") + assert.NotNil(t, temporalStorageSingleton.kv, "Expected kv not to be nil") + assert.NotNil(t, temporalStorageSingleton.list, "Expected list not to be nil") + assert.Equal(t, model.RedisV9Type, temporalStorageSingleton.conn.Type(), "Expected connection type to be RedisV9Type") if tc.expectConnection { - assert.NoError(t, redisClusterSingleton.conn.Ping(context.Background()), "Expected no error on ping") + assert.NoError(t, temporalStorageSingleton.conn.Ping(context.Background()), "Expected no error on ping") } else { - assert.Error(t, redisClusterSingleton.conn.Ping(context.Background()), "Expected error on ping") + assert.Error(t, temporalStorageSingleton.conn.Ping(context.Background()), "Expected error on ping") } }) } From 1c6b11edad2a4912be63cc9bf4cc94ba40685d79 Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Thu, 25 Jan 2024 10:17:24 -0300 Subject: [PATCH 16/25] linting --- main.go | 31 +++++++++++---------- storage/store.go | 2 +- storage/temporal_storage.go | 46 +++++++++++++++++--------------- storage/temporal_storage_test.go | 5 ++-- 4 files changed, 43 insertions(+), 41 deletions(-) diff --git a/main.go b/main.go index 20730f01e..b750d27e8 100644 --- a/main.go +++ b/main.go @@ -23,12 +23,14 @@ import ( kingpin "gopkg.in/alecthomas/kingpin.v2" ) -var SystemConfig TykPumpConfiguration -var AnalyticsStore storage.AnalyticsStorage -var UptimeStorage storage.AnalyticsStorage -var Pumps []pumps.Pump -var UptimePump pumps.UptimePump -var AnalyticsSerializers []serializer.AnalyticsSerializer +var ( + SystemConfig TykPumpConfiguration + AnalyticsStore storage.AnalyticsStorage + UptimeStorage storage.AnalyticsStorage + Pumps []pumps.Pump + UptimePump pumps.UptimePump + AnalyticsSerializers []serializer.AnalyticsSerializer +) var log = logger.GetLogger() @@ -66,7 +68,7 @@ func Init() { demoMode = &envDemo } - //Serializer init + // Serializer init AnalyticsSerializers = []serializer.AnalyticsSerializer{serializer.NewAnalyticsSerializer(serializer.MSGP_SERIALIZER), serializer.NewAnalyticsSerializer(serializer.PROTOBUF_SERIALIZER)} log.WithFields(logrus.Fields{ @@ -96,7 +98,6 @@ func Init() { if *debugMode { log.Level = logrus.DebugLevel } - } func setupAnalyticsStore() { @@ -136,7 +137,7 @@ func setupAnalyticsStore() { } func storeVersion() { - var versionStore = &storage.TemporalStorageHandler{} + versionStore := &storage.TemporalStorageHandler{} versionConf := SystemConfig.AnalyticsStorageConfig versionStore.KeyPrefix = "version-check-" versionStore.Config = versionConf @@ -193,7 +194,6 @@ func initialisePumps() { if !SystemConfig.DontPurgeUptimeData { initialiseUptimePump() } - } func initialiseUptimePump() { @@ -226,7 +226,7 @@ func StartPurgeLoop(wg *sync.WaitGroup, ctx context.Context, secInterval int, ch for i := -1; i < 10; i++ { var analyticsKeyName string if i == -1 { - //if it's the first iteration, we look for tyk-system-analytics to maintain backwards compatibility or if analytics_config.enable_multiple_analytics_keys is disabled in the gateway + // if it's the first iteration, we look for tyk-system-analytics to maintain backwards compatibility or if analytics_config.enable_multiple_analytics_keys is disabled in the gateway analyticsKeyName = storage.ANALYTICS_KEYNAME } else { analyticsKeyName = fmt.Sprintf("%v_%v", storage.ANALYTICS_KEYNAME, i) @@ -238,7 +238,7 @@ func StartPurgeLoop(wg *sync.WaitGroup, ctx context.Context, secInterval int, ch if err != nil { log.WithFields(logrus.Fields{ "prefix": mainPrefix, - }).Error("Error trying to execute GetAndDeleteSet: " + err.Error()) + }).Error("Error on Purge Loop. Is Temporal Storage down?: " + err.Error()) } if len(AnalyticsValues) > 0 { PreprocessAnalyticsValues(AnalyticsValues, serializerMethod, analyticsKeyName, omitDetails, job, startTime, secInterval) @@ -254,7 +254,7 @@ func StartPurgeLoop(wg *sync.WaitGroup, ctx context.Context, secInterval int, ch if err != nil { log.WithFields(logrus.Fields{ "prefix": mainPrefix, - }).Error("Error trying to execute GetAndDeleteSet: " + err.Error()) + }).Error("Error on Purge Loop. Is Temporal Storage down?: " + err.Error()) } UptimePump.WriteUptimeData(UptimeValues) } @@ -331,7 +331,6 @@ func writeToPumps(keys []interface{}, job *health.Job, startTime time.Time, purg } func filterData(pump pumps.Pump, keys []interface{}) []interface{} { - shouldTrim := SystemConfig.MaxRecordSize != 0 || pump.GetMaxRecordSize() != 0 filters := pump.GetFilters() ignoreFields := pump.GetIgnoreFields() @@ -407,11 +406,11 @@ func execPumpWriting(wg *sync.WaitGroup, pmp pumps.Pump, keys *[]interface{}, pu }).Debug("Writing to: ", pmp.GetName()) ch := make(chan error, 1) - //Load pump timeout + // Load pump timeout timeout := pmp.GetTimeout() var ctx context.Context var cancel context.CancelFunc - //Initialize context depending if the pump has a configured timeout + // Initialize context depending if the pump has a configured timeout if timeout > 0 { ctx, cancel = context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second) } else { diff --git a/storage/store.go b/storage/store.go index 55d4b9857..91b60bba4 100644 --- a/storage/store.go +++ b/storage/store.go @@ -10,7 +10,7 @@ type AnalyticsStorage interface { } const ( - RedisKeyPrefix string = "analytics-" + KeyPrefix string = "analytics-" ANALYTICS_KEYNAME string = "tyk-system-analytics" UptimeAnalytics_KEYNAME string = "tyk-uptime-analytics" ) diff --git a/storage/temporal_storage.go b/storage/temporal_storage.go index 7800b1d8f..90a016cca 100644 --- a/storage/temporal_storage.go +++ b/storage/temporal_storage.go @@ -22,7 +22,7 @@ import ( var ( temporalStorageSingleton *storageHandler logPrefix = "temporal-storage" - // Deprecated. Use envTemporalStoragePrefix instead. + // Deprecated: use envTemporalStoragePrefix instead. envRedisPrefix = "TYK_PMP_REDIS" envTemporalStoragePrefix = "TYK_PMP_TEMPORAL_STORAGE" ctx = context.Background() @@ -52,16 +52,10 @@ func (e *EnvMapString) Decode(value string) error { } type TemporalStorageConfig struct { - // Deprecated. + // Type is deprecated. Type string `json:"type" mapstructure:"type"` // Host value. For example: "localhost". Host string `json:"host" mapstructure:"host"` - // Port value. For example: 6379. - Port int `json:"port" mapstructure:"port"` - // Deprecated. Use Addrs instead. - Hosts EnvMapString `json:"hosts" mapstructure:"hosts"` - // Use instead of the host value if you're running a cluster instance with mutliple instances. - Addrs []string `json:"addrs" mapstructure:"addrs"` // Sentinel master name. MasterName string `json:"master_name" mapstructure:"master_name"` // Sentinel password. @@ -70,6 +64,18 @@ type TemporalStorageConfig struct { Username string `json:"username" mapstructure:"username"` // DB password. Password string `json:"password" mapstructure:"password"` + // Prefix the key names. Defaults to "analytics-". + // Deprecated: use KeyPrefix instead. + RedisKeyPrefix string `json:"redis_key_prefix" mapstructure:"redis_key_prefix"` + // Prefix the key names. Defaults to "analytics-". + KeyPrefix string `json:"key_prefix" mapstructure:"key_prefix"` + // Deprecated: use Addrs instead. + Hosts EnvMapString `json:"hosts" mapstructure:"hosts"` + // Use instead of the host value if you're running a cluster instance with multiple instances. + Addrs []string `json:"addrs" mapstructure:"addrs"` + + // Port value. For example: 6379. + Port int `json:"port" mapstructure:"port"` // Database name. Database int `json:"database" mapstructure:"database"` // How long to allow for new connections to be established (in milliseconds). Defaults to 5sec. @@ -79,19 +85,15 @@ type TemporalStorageConfig struct { // 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. Defaults to 500. MaxActive int `json:"optimisation_max_active" mapstructure:"optimisation_max_active"` + // Enable this option if you are using a cluster instance. Default is `false`. EnableCluster bool `json:"enable_cluster" mapstructure:"enable_cluster"` - // Prefix the key names. Defaults to "analytics-". - // Deprecated. Use KeyPrefix instead. - RedisKeyPrefix string `json:"redis_key_prefix" mapstructure:"redis_key_prefix"` // Setting this to true to use SSL when connecting to the DB. - // Deprecated. Use UseSSL instead. + // Deprecated: use UseSSL instead. RedisUseSSL bool `json:"redis_use_ssl" mapstructure:"redis_use_ssl"` // Set this to `true` to tell Pump to ignore database's cert validation. - // Deprecated. Use SSLInsecureSkipVerify instead. + // Deprecated: use SSLInsecureSkipVerify instead. RedisSSLInsecureSkipVerify bool `json:"redis_ssl_insecure_skip_verify" mapstructure:"redis_ssl_insecure_skip_verify"` - // Prefix the key names. Defaults to "analytics-". - KeyPrefix string `json:"key_prefix" mapstructure:"key_prefix"` // Setting this to true to use SSL when connecting to the DB. UseSSL bool `json:"use_ssl" mapstructure:"use_ssl"` // Set this to `true` to tell Pump to ignore database's cert validation. @@ -100,10 +102,10 @@ type TemporalStorageConfig struct { // TemporalStorageHandler is a storage manager that uses non data-persistent databases, like Redis. type TemporalStorageHandler struct { - db *storageHandler KeyPrefix string - HashKeys bool + db *storageHandler Config TemporalStorageConfig + HashKeys bool } func NewTemporalStorageHandler(forceReconnect bool, config *TemporalStorageConfig) error { @@ -191,7 +193,6 @@ func NewTemporalStorageHandler(forceReconnect bool, config *TemporalStorageConfi default: return fmt.Errorf("unsupported database type: %s", config.Type) } - } func (r *TemporalStorageHandler) GetName() string { @@ -221,12 +222,13 @@ func (r *TemporalStorageHandler) Init(config interface{}) error { log.Error("Failed to process environment variables from temporal storage prefix: ", overrideErr) } - if r.Config.KeyPrefix != "" { + switch { + case r.Config.KeyPrefix != "": r.KeyPrefix = r.Config.KeyPrefix - } else if r.Config.RedisKeyPrefix != "" { + case r.Config.RedisKeyPrefix != "": r.KeyPrefix = r.Config.RedisKeyPrefix - } else { - r.KeyPrefix = RedisKeyPrefix + default: + r.KeyPrefix = KeyPrefix } if r.Config.Type != "" { diff --git a/storage/temporal_storage_test.go b/storage/temporal_storage_test.go index 03af19355..c225e2f10 100644 --- a/storage/temporal_storage_test.go +++ b/storage/temporal_storage_test.go @@ -79,7 +79,7 @@ func TestRedisClusterStorageManager_GetAndDeleteSet(t *testing.T) { if tt.chunk > 0 { iterations = len(tt.in) / int(tt.chunk) if rem := len(tt.in) % int(tt.chunk); rem > 0 { - iterations += 1 + iterations++ } } @@ -131,7 +131,8 @@ func TestNewTemporalClusterStorageHandler(t *testing.T) { for _, tc := range testCases { t.Run(tc.testName, func(t *testing.T) { - NewTemporalStorageHandler(tc.forceReconnect, tc.config) + err := NewTemporalStorageHandler(tc.forceReconnect, tc.config) + assert.NoError(t, err, "Expected no error on NewTemporalStorageHandler") assert.NotNil(t, temporalStorageSingleton, "Expected temporalStorageSingleton not to be nil") From d0ba49a8a4808460369d1d8ed1bfa70997fe7945 Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Thu, 25 Jan 2024 10:37:22 -0300 Subject: [PATCH 17/25] fixing test --- storage/temporal_storage.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/storage/temporal_storage.go b/storage/temporal_storage.go index 90a016cca..0e420640d 100644 --- a/storage/temporal_storage.go +++ b/storage/temporal_storage.go @@ -152,6 +152,9 @@ func NewTemporalStorageHandler(forceReconnect bool, config *TemporalStorageConfi MaxActive: maxActive, Timeout: timeout, EnableCluster: config.EnableCluster, + Host: config.Host, + Port: config.Port, + Hosts: config.Hosts, } enableTLS := config.UseSSL From be20d1c17c73e0995ce634917b3793acb60f422f Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Thu, 25 Jan 2024 11:01:05 -0300 Subject: [PATCH 18/25] adding TLS Options --- storage/temporal_storage.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/storage/temporal_storage.go b/storage/temporal_storage.go index 0e420640d..5269f848e 100644 --- a/storage/temporal_storage.go +++ b/storage/temporal_storage.go @@ -98,6 +98,16 @@ type TemporalStorageConfig struct { UseSSL bool `json:"use_ssl" mapstructure:"use_ssl"` // Set this to `true` to tell Pump to ignore database's cert validation. SSLInsecureSkipVerify bool `json:"ssl_insecure_skip_verify" mapstructure:"ssl_insecure_skip_verify"` + // Path to the CA file. + SSLCAFile string `json:"ssl_ca_file" mapstructure:"ssl_ca_file"` + // Path to the cert file. + SSLCertFile string `json:"ssl_cert_file" mapstructure:"ssl_cert_file"` + // Path to the key file. + SSLKeyFile string `json:"ssl_key_file" mapstructure:"ssl_key_file"` + // Maximum supported TLS version. Defaults to TLS 1.3, valid values are TLS 1.0, 1.1, 1.2, 1.3. + SSLMaxVersion string `json:"ssl_max_version" mapstructure:"ssl_max_version"` + // Minimum supported TLS version. Defaults to TLS 1.2, valid values are TLS 1.0, 1.1, 1.2, 1.3. + SSLMinVersion string `json:"ssl_min_version" mapstructure:"ssl_min_version"` } // TemporalStorageHandler is a storage manager that uses non data-persistent databases, like Redis. @@ -170,6 +180,11 @@ func NewTemporalStorageHandler(forceReconnect bool, config *TemporalStorageConfi tlsOptions := &model.TLS{ Enable: enableTLS, InsecureSkipVerify: insecureSkipVerify, + CAFile: config.SSLCAFile, + CertFile: config.SSLCertFile, + KeyFile: config.SSLKeyFile, + MaxVersion: config.SSLMaxVersion, + MinVersion: config.SSLMinVersion, } conn, err := connector.NewConnector(model.RedisV9Type, model.WithRedisConfig(opts), model.WithTLS(tlsOptions)) From d6c3bbf69b5ef5193ad890ca012418a2f3ffedca Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Thu, 25 Jan 2024 11:01:48 -0300 Subject: [PATCH 19/25] linting --- storage/temporal_storage.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/storage/temporal_storage.go b/storage/temporal_storage.go index 5269f848e..c59329c81 100644 --- a/storage/temporal_storage.go +++ b/storage/temporal_storage.go @@ -69,11 +69,20 @@ type TemporalStorageConfig struct { RedisKeyPrefix string `json:"redis_key_prefix" mapstructure:"redis_key_prefix"` // Prefix the key names. Defaults to "analytics-". KeyPrefix string `json:"key_prefix" mapstructure:"key_prefix"` + // Path to the CA file. + SSLCAFile string `json:"ssl_ca_file" mapstructure:"ssl_ca_file"` + // Path to the cert file. + SSLCertFile string `json:"ssl_cert_file" mapstructure:"ssl_cert_file"` + // Path to the key file. + SSLKeyFile string `json:"ssl_key_file" mapstructure:"ssl_key_file"` + // Maximum supported TLS version. Defaults to TLS 1.3, valid values are TLS 1.0, 1.1, 1.2, 1.3. + SSLMaxVersion string `json:"ssl_max_version" mapstructure:"ssl_max_version"` + // Minimum supported TLS version. Defaults to TLS 1.2, valid values are TLS 1.0, 1.1, 1.2, 1.3. + SSLMinVersion string `json:"ssl_min_version" mapstructure:"ssl_min_version"` // Deprecated: use Addrs instead. Hosts EnvMapString `json:"hosts" mapstructure:"hosts"` // Use instead of the host value if you're running a cluster instance with multiple instances. Addrs []string `json:"addrs" mapstructure:"addrs"` - // Port value. For example: 6379. Port int `json:"port" mapstructure:"port"` // Database name. @@ -98,16 +107,6 @@ type TemporalStorageConfig struct { UseSSL bool `json:"use_ssl" mapstructure:"use_ssl"` // Set this to `true` to tell Pump to ignore database's cert validation. SSLInsecureSkipVerify bool `json:"ssl_insecure_skip_verify" mapstructure:"ssl_insecure_skip_verify"` - // Path to the CA file. - SSLCAFile string `json:"ssl_ca_file" mapstructure:"ssl_ca_file"` - // Path to the cert file. - SSLCertFile string `json:"ssl_cert_file" mapstructure:"ssl_cert_file"` - // Path to the key file. - SSLKeyFile string `json:"ssl_key_file" mapstructure:"ssl_key_file"` - // Maximum supported TLS version. Defaults to TLS 1.3, valid values are TLS 1.0, 1.1, 1.2, 1.3. - SSLMaxVersion string `json:"ssl_max_version" mapstructure:"ssl_max_version"` - // Minimum supported TLS version. Defaults to TLS 1.2, valid values are TLS 1.0, 1.1, 1.2, 1.3. - SSLMinVersion string `json:"ssl_min_version" mapstructure:"ssl_min_version"` } // TemporalStorageHandler is a storage manager that uses non data-persistent databases, like Redis. From c4d6763a5f21c1c0bfe00d0b843635a763f52684 Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Thu, 25 Jan 2024 11:03:05 -0300 Subject: [PATCH 20/25] simplifying code --- storage/temporal_storage.go | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/storage/temporal_storage.go b/storage/temporal_storage.go index c59329c81..09eece433 100644 --- a/storage/temporal_storage.go +++ b/storage/temporal_storage.go @@ -166,15 +166,9 @@ func NewTemporalStorageHandler(forceReconnect bool, config *TemporalStorageConfi Hosts: config.Hosts, } - enableTLS := config.UseSSL - if !enableTLS { - enableTLS = config.RedisUseSSL - } + enableTLS := config.UseSSL || config.RedisUseSSL - insecureSkipVerify := config.SSLInsecureSkipVerify - if !insecureSkipVerify { - insecureSkipVerify = config.RedisSSLInsecureSkipVerify - } + insecureSkipVerify := config.SSLInsecureSkipVerify || config.RedisSSLInsecureSkipVerify tlsOptions := &model.TLS{ Enable: enableTLS, From 8b240d2b733417cb75745d2d1a9c005afb54e358 Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Thu, 25 Jan 2024 13:50:39 -0300 Subject: [PATCH 21/25] Improving code readingness --- analytics/aggregate_test.go | 2 - main.go | 72 ++--- pumps/splunk.go | 3 +- pumps/splunk_test.go | 4 - {http-retry => retry}/http-retry.go | 2 +- retry/storage-retry.go | 16 ++ storage/init.go | 17 -- storage/store.go | 88 +++++- storage/temporal_storage.go | 397 +++++++++++----------------- storage/temporal_storage_test.go | 37 +-- 10 files changed, 323 insertions(+), 315 deletions(-) rename {http-retry => retry}/http-retry.go (99%) create mode 100644 retry/storage-retry.go delete mode 100644 storage/init.go diff --git a/analytics/aggregate_test.go b/analytics/aggregate_test.go index 24240b2b6..c9401d957 100644 --- a/analytics/aggregate_test.go +++ b/analytics/aggregate_test.go @@ -1,7 +1,6 @@ package analytics import ( - "fmt" "testing" "time" @@ -326,7 +325,6 @@ func TestAggregateGraphData_Dimension(t *testing.T) { r.Len(aggregated, 1) aggre := aggregated["test-api"] dimensions := aggre.Dimensions() - fmt.Println(dimensions) for d, values := range responsesCheck { for _, v := range values { found := false diff --git a/main.go b/main.go index b750d27e8..dd8ffc2bc 100644 --- a/main.go +++ b/main.go @@ -102,47 +102,51 @@ func Init() { func setupAnalyticsStore() { switch SystemConfig.AnalyticsStorageType { - case "redis": - AnalyticsStore = &storage.TemporalStorageHandler{ - Config: storage.TemporalStorageConfig{ - Type: "redis", - }, - } - UptimeStorage = &storage.TemporalStorageHandler{ - Config: storage.TemporalStorageConfig{ - Type: "redis", - }, - } - default: - AnalyticsStore = &storage.TemporalStorageHandler{ - Config: storage.TemporalStorageConfig{ - Type: "redis", - }, - } - UptimeStorage = &storage.TemporalStorageHandler{ - Config: storage.TemporalStorageConfig{ - Type: "redis", - }, + case "redis", "": + err := AnalyticsStore.Init(SystemConfig.AnalyticsStorageConfig) + if err != nil { + log.WithFields(logrus.Fields{ + "prefix": mainPrefix, + }).Fatal("Error connecting to Redis: ", err) } - } - AnalyticsStore.Init(SystemConfig.AnalyticsStorageConfig) + // Copy across the redis configuration + uptimeConf := SystemConfig.AnalyticsStorageConfig + // Swap key prefixes for uptime purger + uptimeConf.KeyPrefix = "host-checker:" - // Copy across the redis configuration - uptimeConf := SystemConfig.AnalyticsStorageConfig + err = UptimeStorage.Init(uptimeConf) + if err != nil { + log.WithFields(logrus.Fields{ + "prefix": mainPrefix, + }).Fatal("Error connecting to Redis: ", err) + } - // Swap key prefixes for uptime purger - uptimeConf.KeyPrefix = "host-checker:" - UptimeStorage.Init(uptimeConf) + default: + log.WithFields(logrus.Fields{ + "prefix": mainPrefix, + }).Fatal("Invalid analytics storage type: ", SystemConfig.AnalyticsStorageType) + } } func storeVersion() { - versionStore := &storage.TemporalStorageHandler{} - versionConf := SystemConfig.AnalyticsStorageConfig - versionStore.KeyPrefix = "version-check-" - versionStore.Config = versionConf - versionStore.Connect() - err := versionStore.SetKey("pump", pumps.Version, 0) + versionConf := &SystemConfig.AnalyticsStorageConfig + versionConf.KeyPrefix = "version-check-" + versionStore, err := storage.NewTemporalStorageHandler(versionConf, false) + if err != nil { + log.WithFields(logrus.Fields{ + "prefix": mainPrefix, + }).Fatal("Error connecting to Temporal Storage: ", err) + } + + err = versionStore.Init() + if err != nil { + log.WithFields(logrus.Fields{ + "prefix": mainPrefix, + }).Fatal("Error connecting to Temporal Storage: ", err) + } + + err = versionStore.SetKey("pump", pumps.Version, 0) if err != nil { log.WithFields(logrus.Fields{ "prefix": mainPrefix, diff --git a/pumps/splunk.go b/pumps/splunk.go index 1c7c27218..ab4972edc 100644 --- a/pumps/splunk.go +++ b/pumps/splunk.go @@ -11,7 +11,8 @@ import ( "strings" "github.com/TykTechnologies/tyk-pump/analytics" - retry "github.com/TykTechnologies/tyk-pump/http-retry" + "github.com/TykTechnologies/tyk-pump/retry" + "github.com/mitchellh/mapstructure" ) diff --git a/pumps/splunk_test.go b/pumps/splunk_test.go index 81a5ebbc7..9ec67513a 100644 --- a/pumps/splunk_test.go +++ b/pumps/splunk_test.go @@ -3,7 +3,6 @@ package pumps import ( "context" "encoding/json" - "fmt" "io/ioutil" "net/http" "net/http/httptest" @@ -54,7 +53,6 @@ func (h *testHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { r.Body.Close() if h.returnErrors >= h.reqCount { - fmt.Println("returning err.......") w.WriteHeader(http.StatusInternalServerError) _, err := w.Write([]byte("splunk internal error")) if err != nil { @@ -249,8 +247,6 @@ func Test_SplunkWriteDataBatch(t *testing.T) { keys[1] = analytics.AnalyticsRecord{OrgID: "1", APIID: "123", Path: "/test-path", Method: "POST", TimeStamp: time.Now()} keys[2] = analytics.AnalyticsRecord{OrgID: "1", APIID: "123", Path: "/test-path", Method: "POST", TimeStamp: time.Now()} - fmt.Println(maxContentLength) - pmp := SplunkPump{} cfg := make(map[string]interface{}) diff --git a/http-retry/http-retry.go b/retry/http-retry.go similarity index 99% rename from http-retry/http-retry.go rename to retry/http-retry.go index 3a6bdd3e7..50908e9a8 100644 --- a/http-retry/http-retry.go +++ b/retry/http-retry.go @@ -1,4 +1,4 @@ -package httpretry +package retry import ( "bytes" diff --git a/retry/storage-retry.go b/retry/storage-retry.go new file mode 100644 index 000000000..ab3ef6131 --- /dev/null +++ b/retry/storage-retry.go @@ -0,0 +1,16 @@ +package retry + +import ( + "time" + + "github.com/cenkalti/backoff/v4" +) + +func GetTemporalStorageExponentialBackoff() *backoff.ExponentialBackOff { + exponentialBackoff := backoff.NewExponentialBackOff() + exponentialBackoff.Multiplier = 2 + exponentialBackoff.MaxInterval = 10 * time.Second + exponentialBackoff.MaxElapsedTime = 0 + + return exponentialBackoff +} diff --git a/storage/init.go b/storage/init.go deleted file mode 100644 index f2d7ea856..000000000 --- a/storage/init.go +++ /dev/null @@ -1,17 +0,0 @@ -package storage - -import ( - "github.com/TykTechnologies/tyk-pump/logger" -) - -var log = logger.GetLogger() -var AvailableStores map[string]AnalyticsStorage - -func init() { - AvailableStores = make(map[string]AnalyticsStorage) - - // Register all the storage handlers here - AvailableStores["redis"] = &TemporalStorageHandler{Config: TemporalStorageConfig{ - Type: "redis", - }} -} diff --git a/storage/store.go b/storage/store.go index 91b60bba4..833019806 100644 --- a/storage/store.go +++ b/storage/store.go @@ -1,11 +1,16 @@ package storage -import "time" +import ( + "strings" + "time" + + "github.com/TykTechnologies/tyk-pump/logger" +) type AnalyticsStorage interface { Init(config interface{}) error GetName() string - Connect() bool + connect() error GetAndDeleteSet(setName string, chunkSize int64, expire time.Duration) ([]interface{}, error) } @@ -14,3 +19,82 @@ const ( ANALYTICS_KEYNAME string = "tyk-system-analytics" UptimeAnalytics_KEYNAME string = "tyk-uptime-analytics" ) + +var log = logger.GetLogger() + +// nolint:govet +type TemporalStorageConfig struct { + // Deprecated. + Type string `json:"type" mapstructure:"type"` + // Host value. For example: "localhost". + Host string `json:"host" mapstructure:"host"` + // Port value. For example: 6379. + Port int `json:"port" mapstructure:"port"` + // Deprecated: use Addrs instead. + Hosts EnvMapString `json:"hosts" mapstructure:"hosts"` + // Use instead of the host value if you're running a cluster instance with multiple instances. + Addrs []string `json:"addrs" mapstructure:"addrs"` + // Sentinel master name. + MasterName string `json:"master_name" mapstructure:"master_name"` + // Sentinel password. + SentinelPassword string `json:"sentinel_password" mapstructure:"sentinel_password"` + // DB username. + Username string `json:"username" mapstructure:"username"` + // DB password. + Password string `json:"password" mapstructure:"password"` + // Database name. + Database int `json:"database" mapstructure:"database"` + // How long to allow for new connections to be established (in milliseconds). Defaults to 5sec. + Timeout int `json:"timeout" mapstructure:"timeout"` + // Maximum number of idle connections in the pool. + MaxIdle int `json:"optimisation_max_idle" mapstructure:"optimisation_max_idle"` + // 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. Defaults to 500. + MaxActive int `json:"optimisation_max_active" mapstructure:"optimisation_max_active"` + // Enable this option if you are using a cluster instance. Default is `false`. + EnableCluster bool `json:"enable_cluster" mapstructure:"enable_cluster"` + + // Prefix the key names. Defaults to "analytics-". + // Deprecated: use KeyPrefix instead. + RedisKeyPrefix string `json:"redis_key_prefix" mapstructure:"redis_key_prefix"` + // Prefix the key names. Defaults to "analytics-". + KeyPrefix string `json:"key_prefix" mapstructure:"key_prefix"` + + // Setting this to true to use SSL when connecting to the DB. + UseSSL bool `json:"use_ssl" mapstructure:"use_ssl"` + // Set this to `true` to tell Pump to ignore database's cert validation. + SSLInsecureSkipVerify bool `json:"ssl_insecure_skip_verify" mapstructure:"ssl_insecure_skip_verify"` + // Path to the CA file. + SSLCAFile string `json:"ssl_ca_file" mapstructure:"ssl_ca_file"` + // Path to the cert file. + SSLCertFile string `json:"ssl_cert_file" mapstructure:"ssl_cert_file"` + // Path to the key file. + SSLKeyFile string `json:"ssl_key_file" mapstructure:"ssl_key_file"` + // Maximum supported TLS version. Defaults to TLS 1.3, valid values are TLS 1.0, 1.1, 1.2, 1.3. + SSLMaxVersion string `json:"ssl_max_version" mapstructure:"ssl_max_version"` + // Minimum supported TLS version. Defaults to TLS 1.2, valid values are TLS 1.0, 1.1, 1.2, 1.3. + SSLMinVersion string `json:"ssl_min_version" mapstructure:"ssl_min_version"` + // Setting this to true to use SSL when connecting to the DB. + // Deprecated: use UseSSL instead. + RedisUseSSL bool `json:"redis_use_ssl" mapstructure:"redis_use_ssl"` + // Set this to `true` to tell Pump to ignore database's cert validation. + // Deprecated: use SSLInsecureSkipVerify instead. + RedisSSLInsecureSkipVerify bool `json:"redis_ssl_insecure_skip_verify" mapstructure:"redis_ssl_insecure_skip_verify"` +} + +type EnvMapString map[string]string + +func (e *EnvMapString) Decode(value string) error { + units := strings.Split(value, ",") + m := make(map[string]string) + for _, unit := range units { + kvArr := strings.Split(unit, ":") + if len(kvArr) > 1 { + m[kvArr[0]] = kvArr[1] + } + } + + *e = m + + return nil +} diff --git a/storage/temporal_storage.go b/storage/temporal_storage.go index 09eece433..634553979 100644 --- a/storage/temporal_storage.go +++ b/storage/temporal_storage.go @@ -3,13 +3,15 @@ package storage import ( "context" "fmt" - "strings" "time" "github.com/TykTechnologies/storage/temporal/connector" keyvalue "github.com/TykTechnologies/storage/temporal/keyvalue" "github.com/TykTechnologies/storage/temporal/list" "github.com/TykTechnologies/storage/temporal/model" + "github.com/TykTechnologies/tyk-pump/retry" + + "github.com/cenkalti/backoff/v4" "github.com/sirupsen/logrus" @@ -17,270 +19,195 @@ import ( "github.com/mitchellh/mapstructure" ) -// ------------------- TEMPORAL CLUSTER STORAGE MANAGER ------------------------------- - var ( - temporalStorageSingleton *storageHandler - logPrefix = "temporal-storage" + connectorSingleton *TemporalStorageHandler + logPrefix = "temporal-storage" // Deprecated: use envTemporalStoragePrefix instead. envRedisPrefix = "TYK_PMP_REDIS" envTemporalStoragePrefix = "TYK_PMP_TEMPORAL_STORAGE" ctx = context.Background() ) -type storageHandler struct { - list list.List - kv keyvalue.KeyValue - conn model.Connector -} - -type EnvMapString map[string]string - -func (e *EnvMapString) Decode(value string) error { - units := strings.Split(value, ",") - m := make(map[string]string) - for _, unit := range units { - kvArr := strings.Split(unit, ":") - if len(kvArr) > 1 { - m[kvArr[0]] = kvArr[1] - } - } - - *e = m - - return nil -} - -type TemporalStorageConfig struct { - // Type is deprecated. - Type string `json:"type" mapstructure:"type"` - // Host value. For example: "localhost". - Host string `json:"host" mapstructure:"host"` - // Sentinel master name. - MasterName string `json:"master_name" mapstructure:"master_name"` - // Sentinel password. - SentinelPassword string `json:"sentinel_password" mapstructure:"sentinel_password"` - // DB username. - Username string `json:"username" mapstructure:"username"` - // DB password. - Password string `json:"password" mapstructure:"password"` - // Prefix the key names. Defaults to "analytics-". - // Deprecated: use KeyPrefix instead. - RedisKeyPrefix string `json:"redis_key_prefix" mapstructure:"redis_key_prefix"` - // Prefix the key names. Defaults to "analytics-". - KeyPrefix string `json:"key_prefix" mapstructure:"key_prefix"` - // Path to the CA file. - SSLCAFile string `json:"ssl_ca_file" mapstructure:"ssl_ca_file"` - // Path to the cert file. - SSLCertFile string `json:"ssl_cert_file" mapstructure:"ssl_cert_file"` - // Path to the key file. - SSLKeyFile string `json:"ssl_key_file" mapstructure:"ssl_key_file"` - // Maximum supported TLS version. Defaults to TLS 1.3, valid values are TLS 1.0, 1.1, 1.2, 1.3. - SSLMaxVersion string `json:"ssl_max_version" mapstructure:"ssl_max_version"` - // Minimum supported TLS version. Defaults to TLS 1.2, valid values are TLS 1.0, 1.1, 1.2, 1.3. - SSLMinVersion string `json:"ssl_min_version" mapstructure:"ssl_min_version"` - // Deprecated: use Addrs instead. - Hosts EnvMapString `json:"hosts" mapstructure:"hosts"` - // Use instead of the host value if you're running a cluster instance with multiple instances. - Addrs []string `json:"addrs" mapstructure:"addrs"` - // Port value. For example: 6379. - Port int `json:"port" mapstructure:"port"` - // Database name. - Database int `json:"database" mapstructure:"database"` - // How long to allow for new connections to be established (in milliseconds). Defaults to 5sec. - Timeout int `json:"timeout" mapstructure:"timeout"` - // Maximum number of idle connections in the pool. - MaxIdle int `json:"optimisation_max_idle" mapstructure:"optimisation_max_idle"` - // 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. Defaults to 500. - MaxActive int `json:"optimisation_max_active" mapstructure:"optimisation_max_active"` - - // Enable this option if you are using a cluster instance. Default is `false`. - EnableCluster bool `json:"enable_cluster" mapstructure:"enable_cluster"` - // Setting this to true to use SSL when connecting to the DB. - // Deprecated: use UseSSL instead. - RedisUseSSL bool `json:"redis_use_ssl" mapstructure:"redis_use_ssl"` - // Set this to `true` to tell Pump to ignore database's cert validation. - // Deprecated: use SSLInsecureSkipVerify instead. - RedisSSLInsecureSkipVerify bool `json:"redis_ssl_insecure_skip_verify" mapstructure:"redis_ssl_insecure_skip_verify"` - // Setting this to true to use SSL when connecting to the DB. - UseSSL bool `json:"use_ssl" mapstructure:"use_ssl"` - // Set this to `true` to tell Pump to ignore database's cert validation. - SSLInsecureSkipVerify bool `json:"ssl_insecure_skip_verify" mapstructure:"ssl_insecure_skip_verify"` -} - // TemporalStorageHandler is a storage manager that uses non data-persistent databases, like Redis. type TemporalStorageHandler struct { - KeyPrefix string - db *storageHandler - Config TemporalStorageConfig - HashKeys bool + Config *TemporalStorageConfig + conn model.Connector + kv model.KeyValue + list model.List + forceReconnect bool } -func NewTemporalStorageHandler(forceReconnect bool, config *TemporalStorageConfig) error { - switch config.Type { - case "redis", "": - if !forceReconnect { - if temporalStorageSingleton != nil { - log.WithFields(logrus.Fields{ - "prefix": logPrefix, - }).Debug("Redis pool already INITIALISED") - return nil - } - } else { - if temporalStorageSingleton != nil { - err := temporalStorageSingleton.conn.Disconnect(ctx) - if err != nil { - return fmt.Errorf("error disconnecting Redis: %s", err.Error()) - } - } - } - - log.WithFields(logrus.Fields{ - "prefix": logPrefix, - }).Debug("Creating new Redis connection pool") - - maxActive := 500 - if config.MaxActive > 0 { - maxActive = config.MaxActive - } - - timeout := 5 - - if config.Timeout > 0 { - timeout = config.Timeout - } - - opts := &model.RedisOptions{ - MasterName: config.MasterName, - SentinelPassword: config.SentinelPassword, - Addrs: config.Addrs, - Database: config.Database, - Username: config.Username, - Password: config.Password, - MaxActive: maxActive, - Timeout: timeout, - EnableCluster: config.EnableCluster, - Host: config.Host, - Port: config.Port, - Hosts: config.Hosts, - } - - enableTLS := config.UseSSL || config.RedisUseSSL - - insecureSkipVerify := config.SSLInsecureSkipVerify || config.RedisSSLInsecureSkipVerify - - tlsOptions := &model.TLS{ - Enable: enableTLS, - InsecureSkipVerify: insecureSkipVerify, - CAFile: config.SSLCAFile, - CertFile: config.SSLCertFile, - KeyFile: config.SSLKeyFile, - MaxVersion: config.SSLMaxVersion, - MinVersion: config.SSLMinVersion, - } +func NewTemporalStorageHandler(config interface{}, forceReconnect bool) (*TemporalStorageHandler, error) { + r := &TemporalStorageHandler{ + forceReconnect: forceReconnect, + } - conn, err := connector.NewConnector(model.RedisV9Type, model.WithRedisConfig(opts), model.WithTLS(tlsOptions)) + switch c := config.(type) { + case map[string]interface{}: + err := mapstructure.Decode(config, &r.Config) if err != nil { - return err + return nil, err } - kv, err := keyvalue.NewKeyValue(conn) - if err != nil { - return err - } + return r, nil - l, err := list.NewList(conn) - if err != nil { - return err - } + case *TemporalStorageConfig: + r.Config = c - temporalStorageSingleton = &storageHandler{} - temporalStorageSingleton.kv = kv - temporalStorageSingleton.list = l - temporalStorageSingleton.conn = conn + return r, nil - return nil default: - return fmt.Errorf("unsupported database type: %s", config.Type) + return nil, fmt.Errorf("unsupported config type: %T", config) } } -func (r *TemporalStorageHandler) GetName() string { - if r.Config.Type != "" { - return r.Config.Type - } - - return "redis" -} - -func (r *TemporalStorageHandler) Init(config interface{}) error { - r.Config = TemporalStorageConfig{} - err := mapstructure.Decode(config, &r.Config) - if err != nil { +func (r *TemporalStorageHandler) Init() error { + if r.Config == nil { + r.Config = &TemporalStorageConfig{} log.WithFields(logrus.Fields{ "prefix": logPrefix, - }).Fatal("Failed to decode configuration: ", err) + }).Debug("Config is nil, using default config") } - overrideErr := envconfig.Process(envRedisPrefix, &r.Config) + overrideErr := envconfig.Process(envRedisPrefix, r.Config) if overrideErr != nil { - log.Error("Failed to process environment variables from redis prefix: ", overrideErr) + return overrideErr } - overrideErr = envconfig.Process(envTemporalStoragePrefix, &r.Config) + overrideErr = envconfig.Process(envTemporalStoragePrefix, r.Config) if overrideErr != nil { - log.Error("Failed to process environment variables from temporal storage prefix: ", overrideErr) + return overrideErr } switch { case r.Config.KeyPrefix != "": - r.KeyPrefix = r.Config.KeyPrefix + // Keep the KeyPrefix as is case r.Config.RedisKeyPrefix != "": - r.KeyPrefix = r.Config.RedisKeyPrefix + r.Config.KeyPrefix = r.Config.RedisKeyPrefix default: - r.KeyPrefix = KeyPrefix + r.Config.KeyPrefix = KeyPrefix } if r.Config.Type != "" { logPrefix = r.Config.Type } - return nil + return r.connect() } // Connect will establish a connection to the r.db -func (r *TemporalStorageHandler) Connect() bool { - if r.db == nil { +func (r *TemporalStorageHandler) connect() error { + if connectorSingleton == nil || r.forceReconnect { log.WithFields(logrus.Fields{ "prefix": logPrefix, }).Debug("Connecting to temporal storage") - err := NewTemporalStorageHandler(false, &r.Config) - if err != nil { - log.WithFields(logrus.Fields{ - "prefix": logPrefix, - }).Error("Error connecting to temporal storage: ", err) - return false + if r.Config.Type != "redis" && r.Config.Type != "" { + return fmt.Errorf("unsupported database type: %s", r.Config.Type) + } + + if err := r.resetConnection(r.Config); err != nil { + return err } - r.db = temporalStorageSingleton - return true + + log.WithFields(logrus.Fields{"prefix": logPrefix}).Debug("Temporal Storage already INITIALISED") } log.WithFields(logrus.Fields{ "prefix": logPrefix, }).Debug("Storage Engine already initialized...") - // Reset it just in case - r.db = temporalStorageSingleton - return true + return nil } -func (r *TemporalStorageHandler) hashKey(in string) string { - return in +func (r *TemporalStorageHandler) resetConnection(config *TemporalStorageConfig) error { + if connectorSingleton != nil { + if err := connectorSingleton.conn.Disconnect(ctx); err != nil { + return fmt.Errorf("error disconnecting Temporal Storage: %s", err) + } + } + + log.WithFields(logrus.Fields{ + "prefix": logPrefix, + }).Debug("Creating new Redis connection pool") + + maxActive := 500 + if config.MaxActive > 0 { + maxActive = config.MaxActive + } + + timeout := 5 + + if config.Timeout > 0 { + timeout = config.Timeout + } + + opts := &model.RedisOptions{ + MasterName: config.MasterName, + SentinelPassword: config.SentinelPassword, + Addrs: config.Addrs, + Database: config.Database, + Username: config.Username, + Password: config.Password, + MaxActive: maxActive, + Timeout: timeout, + EnableCluster: config.EnableCluster, + Host: config.Host, + Port: config.Port, + Hosts: config.Hosts, + } + + tlsOptions := &model.TLS{ + Enable: config.UseSSL || config.RedisUseSSL, + InsecureSkipVerify: config.SSLInsecureSkipVerify || config.RedisSSLInsecureSkipVerify, + CAFile: config.SSLCAFile, + CertFile: config.SSLCertFile, + KeyFile: config.SSLKeyFile, + MaxVersion: config.SSLMaxVersion, + MinVersion: config.SSLMinVersion, + } + + conn, kv, list, err := createConnector(opts, tlsOptions) + if err != nil { + return err + } + + connectorSingleton = r + connectorSingleton.kv = kv + connectorSingleton.list = list + connectorSingleton.conn = conn + + return nil +} + +func createConnector(opts *model.RedisOptions, tlsOptions *model.TLS) (model.Connector, model.KeyValue, model.List, error) { + conn, err := connector.NewConnector(model.RedisV9Type, model.WithRedisConfig(opts), model.WithTLS(tlsOptions)) + if err != nil { + return nil, nil, nil, err + } + + kv, err := keyvalue.NewKeyValue(conn) + if err != nil { + return nil, nil, nil, err + } + + l, err := list.NewList(conn) + if err != nil { + return nil, nil, nil, err + } + + return conn, kv, l, nil +} + +func (r *TemporalStorageHandler) GetName() string { + if r.Config.Type != "" { + return r.Config.Type + } + + return "redis" } func (r *TemporalStorageHandler) fixKey(keyName string) string { - setKeyName := r.KeyPrefix + r.hashKey(keyName) + setKeyName := r.Config.KeyPrefix + keyName log.WithFields(logrus.Fields{ "prefix": logPrefix, @@ -294,12 +221,9 @@ func (r *TemporalStorageHandler) GetAndDeleteSet(keyName string, chunkSize int64 "prefix": logPrefix, }).Debug("Getting raw key set: ", keyName) - if r.db == nil { - log.WithFields(logrus.Fields{ - "prefix": logPrefix, - }).Warning("Connection dropped, connecting..") - r.Connect() - return r.GetAndDeleteSet(keyName, chunkSize, expire) + err := r.ensureConnection() + if err != nil { + return nil, err } log.WithFields(logrus.Fields{ @@ -318,13 +242,13 @@ func (r *TemporalStorageHandler) GetAndDeleteSet(keyName string, chunkSize int64 chunkSize = -1 } - result, err := r.db.list.Pop(ctx, fixedKey, chunkSize) + result, err := r.list.Pop(ctx, fixedKey, chunkSize) if err != nil { return nil, err } if chunkSize != -1 { - err = r.db.kv.Expire(ctx, fixedKey, expire) + err = r.kv.Expire(ctx, fixedKey, expire) if err != nil { return nil, err } @@ -343,13 +267,12 @@ func (r *TemporalStorageHandler) SetKey(keyName, session string, timeout int64) log.Debug("[STORE] SET Raw key is: ", keyName) log.Debug("[STORE] Setting key: ", r.fixKey(keyName)) - r.ensureConnection() - err := r.db.kv.Set(ctx, r.fixKey(keyName), session, 0) - if timeout > 0 { - if err := r.SetExp(keyName, timeout); err != nil { - return err - } + err := r.ensureConnection() + if err != nil { + return err } + + err = r.kv.Set(ctx, r.fixKey(keyName), session, time.Duration(timeout)*time.Second) if err != nil { log.Error("Error trying to set value: ", err) return err @@ -357,26 +280,28 @@ func (r *TemporalStorageHandler) SetKey(keyName, session string, timeout int64) return nil } -func (r *TemporalStorageHandler) SetExp(keyName string, timeout int64) error { - err := r.db.kv.Expire(ctx, r.fixKey(keyName), time.Duration(timeout)*time.Second) - if err != nil { - log.Error("Could not EXPIRE key: ", err) +func (r *TemporalStorageHandler) ensureConnection() error { + if connectorSingleton != nil { + return nil } - return err -} -func (r *TemporalStorageHandler) ensureConnection() { - if r.db != nil { - // already connected - return - } log.Info("Connection dropped, reconnecting...") - for { - r.Connect() - if r.db != nil { - // reconnection worked - return + backoffStrategy := retry.GetTemporalStorageExponentialBackoff() + + operation := func() error { + if err := r.connect(); err != nil { + return err } - log.Info("Reconnecting again...") + + if r.conn == nil { + return fmt.Errorf("connection failed") + } + return nil } + + if err := backoff.Retry(operation, backoffStrategy); err != nil { + return fmt.Errorf("failed to reconnect after several attempts: %w", err) + } + + return nil } diff --git a/storage/temporal_storage_test.go b/storage/temporal_storage_test.go index c225e2f10..f3abca52d 100644 --- a/storage/temporal_storage_test.go +++ b/storage/temporal_storage_test.go @@ -45,18 +45,17 @@ func TestRedisClusterStorageManager_GetAndDeleteSet(t *testing.T) { conf["host"] = "localhost" conf["port"] = 6379 - r := TemporalStorageHandler{} - if err := r.Init(conf); err != nil { - t.Fatal("unable to connect", err.Error()) + r, err := NewTemporalStorageHandler(conf, false) + if err != nil { + t.Fatal(err) } - connected := r.Connect() - if !connected { - t.Fatal("failed to connect") + if err := r.Init(); err != nil { + t.Fatal("unable to connect", err.Error()) } - if r.db == nil { - t.Fatal("db is empty") + if r.conn == nil { + t.Fatal("conn is empty") } mockKeyName := "testanalytics" @@ -69,7 +68,7 @@ func TestRedisClusterStorageManager_GetAndDeleteSet(t *testing.T) { for _, v := range tt.in { in = append(in, []byte(v)) } - err := r.db.list.Append(ctx, false, r.fixKey(mockKeyName), in...) + err := r.list.Append(ctx, false, r.fixKey(mockKeyName), in...) if err != nil { t.Fatal(err) } @@ -120,7 +119,6 @@ func TestNewTemporalClusterStorageHandler(t *testing.T) { config: &TemporalStorageConfig{Host: "localhost", Port: 6379}, expectConnection: true, }, - { testName: "Invalid configuration", config: &TemporalStorageConfig{Host: "invalid-host", Port: 6379}, @@ -131,20 +129,23 @@ func TestNewTemporalClusterStorageHandler(t *testing.T) { for _, tc := range testCases { t.Run(tc.testName, func(t *testing.T) { - err := NewTemporalStorageHandler(tc.forceReconnect, tc.config) + tsh, err := NewTemporalStorageHandler(tc.config, tc.forceReconnect) assert.NoError(t, err, "Expected no error on NewTemporalStorageHandler") - assert.NotNil(t, temporalStorageSingleton, "Expected temporalStorageSingleton not to be nil") + err = tsh.Init() + assert.NoError(t, err, "Expected no error on NewTemporalStorageHandler Init method") + + assert.NotNil(t, connectorSingleton, "Expected connectorSingleton not to be nil") - assert.NotNil(t, temporalStorageSingleton.conn, "Expected connection not to be nil") - assert.NotNil(t, temporalStorageSingleton.kv, "Expected kv not to be nil") - assert.NotNil(t, temporalStorageSingleton.list, "Expected list not to be nil") - assert.Equal(t, model.RedisV9Type, temporalStorageSingleton.conn.Type(), "Expected connection type to be RedisV9Type") + assert.NotNil(t, connectorSingleton.conn, "Expected connection not to be nil") + assert.NotNil(t, connectorSingleton.kv, "Expected kv not to be nil") + assert.NotNil(t, connectorSingleton.list, "Expected list not to be nil") + assert.Equal(t, model.RedisV9Type, connectorSingleton.conn.Type(), "Expected connection type to be RedisV9Type") if tc.expectConnection { - assert.NoError(t, temporalStorageSingleton.conn.Ping(context.Background()), "Expected no error on ping") + assert.NoError(t, connectorSingleton.conn.Ping(context.Background()), "Expected no error on ping") } else { - assert.Error(t, temporalStorageSingleton.conn.Ping(context.Background()), "Expected error on ping") + assert.Error(t, connectorSingleton.conn.Ping(context.Background()), "Expected error on ping") } }) } From b9938212dc6387d6cfe3c1f2102fa154bb560027 Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Thu, 25 Jan 2024 16:12:15 -0300 Subject: [PATCH 22/25] Making model.Connector global variable a singleton --- main.go | 20 +++++++++++-- storage/store.go | 3 +- storage/temporal_storage.go | 51 ++++++++++++++++++++++++++------ storage/temporal_storage_test.go | 16 +++++----- 4 files changed, 68 insertions(+), 22 deletions(-) diff --git a/main.go b/main.go index dd8ffc2bc..824fd2a47 100644 --- a/main.go +++ b/main.go @@ -103,11 +103,18 @@ func Init() { func setupAnalyticsStore() { switch SystemConfig.AnalyticsStorageType { case "redis", "": - err := AnalyticsStore.Init(SystemConfig.AnalyticsStorageConfig) + var err error + AnalyticsStore, err = storage.NewTemporalStorageHandler(SystemConfig.AnalyticsStorageConfig, false) if err != nil { log.WithFields(logrus.Fields{ "prefix": mainPrefix, - }).Fatal("Error connecting to Redis: ", err) + }).Fatal("Error connecting to Temporal Storage: ", err) + } + err = AnalyticsStore.Init() + if err != nil { + log.WithFields(logrus.Fields{ + "prefix": mainPrefix, + }).Fatal("Error connecting to Temporal Storage: ", err) } // Copy across the redis configuration @@ -115,7 +122,14 @@ func setupAnalyticsStore() { // Swap key prefixes for uptime purger uptimeConf.KeyPrefix = "host-checker:" - err = UptimeStorage.Init(uptimeConf) + UptimeStorage, err = storage.NewTemporalStorageHandler(uptimeConf, false) + if err != nil { + log.WithFields(logrus.Fields{ + "prefix": mainPrefix, + }).Fatal("Error connecting to Temporal Storage: ", err) + } + + err = UptimeStorage.Init() if err != nil { log.WithFields(logrus.Fields{ "prefix": mainPrefix, diff --git a/storage/store.go b/storage/store.go index 833019806..109320efb 100644 --- a/storage/store.go +++ b/storage/store.go @@ -8,9 +8,8 @@ import ( ) type AnalyticsStorage interface { - Init(config interface{}) error + Init() error GetName() string - connect() error GetAndDeleteSet(setName string, chunkSize int64, expire time.Duration) ([]interface{}, error) } diff --git a/storage/temporal_storage.go b/storage/temporal_storage.go index 634553979..884e17136 100644 --- a/storage/temporal_storage.go +++ b/storage/temporal_storage.go @@ -20,7 +20,7 @@ import ( ) var ( - connectorSingleton *TemporalStorageHandler + connectorSingleton model.Connector logPrefix = "temporal-storage" // Deprecated: use envTemporalStoragePrefix instead. envRedisPrefix = "TYK_PMP_REDIS" @@ -31,7 +31,6 @@ var ( // TemporalStorageHandler is a storage manager that uses non data-persistent databases, like Redis. type TemporalStorageHandler struct { Config *TemporalStorageConfig - conn model.Connector kv model.KeyValue list model.List forceReconnect bool @@ -56,6 +55,11 @@ func NewTemporalStorageHandler(config interface{}, forceReconnect bool) (*Tempor return r, nil + case TemporalStorageConfig: + r.Config = &c + + return r, nil + default: return nil, fmt.Errorf("unsupported config type: %T", config) } @@ -97,6 +101,7 @@ func (r *TemporalStorageHandler) Init() error { // Connect will establish a connection to the r.db func (r *TemporalStorageHandler) connect() error { + var err error if connectorSingleton == nil || r.forceReconnect { log.WithFields(logrus.Fields{ "prefix": logPrefix, @@ -105,11 +110,22 @@ func (r *TemporalStorageHandler) connect() error { return fmt.Errorf("unsupported database type: %s", r.Config.Type) } - if err := r.resetConnection(r.Config); err != nil { + if err = r.resetConnection(r.Config); err != nil { return err } log.WithFields(logrus.Fields{"prefix": logPrefix}).Debug("Temporal Storage already INITIALISED") + } else if r.kv == nil || r.list == nil { + // This is the case when the connector is already created but we're instantiating a new TemporalStorageHandler + r.kv, err = getKVFromConnector() + if err != nil { + return err + } + + r.list, err = getListFromConnector() + if err != nil { + return err + } } log.WithFields(logrus.Fields{ @@ -121,7 +137,7 @@ func (r *TemporalStorageHandler) connect() error { func (r *TemporalStorageHandler) resetConnection(config *TemporalStorageConfig) error { if connectorSingleton != nil { - if err := connectorSingleton.conn.Disconnect(ctx); err != nil { + if err := connectorSingleton.Disconnect(ctx); err != nil { return fmt.Errorf("error disconnecting Temporal Storage: %s", err) } } @@ -171,10 +187,9 @@ func (r *TemporalStorageHandler) resetConnection(config *TemporalStorageConfig) return err } - connectorSingleton = r - connectorSingleton.kv = kv - connectorSingleton.list = list - connectorSingleton.conn = conn + connectorSingleton = conn + r.kv = kv + r.list = list return nil } @@ -198,6 +213,24 @@ func createConnector(opts *model.RedisOptions, tlsOptions *model.TLS) (model.Con return conn, kv, l, nil } +func getKVFromConnector() (model.KeyValue, error) { + kv, err := keyvalue.NewKeyValue(connectorSingleton) + if err != nil { + return nil, err + } + + return kv, nil +} + +func getListFromConnector() (model.List, error) { + l, err := list.NewList(connectorSingleton) + if err != nil { + return nil, err + } + + return l, nil +} + func (r *TemporalStorageHandler) GetName() string { if r.Config.Type != "" { return r.Config.Type @@ -293,7 +326,7 @@ func (r *TemporalStorageHandler) ensureConnection() error { return err } - if r.conn == nil { + if connectorSingleton == nil { return fmt.Errorf("connection failed") } return nil diff --git a/storage/temporal_storage_test.go b/storage/temporal_storage_test.go index f3abca52d..033984b4d 100644 --- a/storage/temporal_storage_test.go +++ b/storage/temporal_storage_test.go @@ -54,8 +54,8 @@ func TestRedisClusterStorageManager_GetAndDeleteSet(t *testing.T) { t.Fatal("unable to connect", err.Error()) } - if r.conn == nil { - t.Fatal("conn is empty") + if connectorSingleton == nil { + t.Fatal("connectorSingleton is nil") } mockKeyName := "testanalytics" @@ -137,15 +137,15 @@ func TestNewTemporalClusterStorageHandler(t *testing.T) { assert.NotNil(t, connectorSingleton, "Expected connectorSingleton not to be nil") - assert.NotNil(t, connectorSingleton.conn, "Expected connection not to be nil") - assert.NotNil(t, connectorSingleton.kv, "Expected kv not to be nil") - assert.NotNil(t, connectorSingleton.list, "Expected list not to be nil") - assert.Equal(t, model.RedisV9Type, connectorSingleton.conn.Type(), "Expected connection type to be RedisV9Type") + assert.NotNil(t, connectorSingleton, "Expected connectorSingleton not to be nil") + assert.NotNil(t, tsh.kv, "Expected kv not to be nil") + assert.NotNil(t, tsh.list, "Expected list not to be nil") + assert.Equal(t, model.RedisV9Type, connectorSingleton.Type(), "Expected connection type to be RedisV9Type") if tc.expectConnection { - assert.NoError(t, connectorSingleton.conn.Ping(context.Background()), "Expected no error on ping") + assert.NoError(t, connectorSingleton.Ping(context.Background()), "Expected no error on ping") } else { - assert.Error(t, connectorSingleton.conn.Ping(context.Background()), "Expected error on ping") + assert.Error(t, connectorSingleton.Ping(context.Background()), "Expected error on ping") } }) } From bb51e9f6e02916d6dc0e92411d27f08363493b85 Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Thu, 25 Jan 2024 17:57:17 -0300 Subject: [PATCH 23/25] Update dependencies --- go.mod | 2 +- go.sum | 2 + pumps/version.go | 2 +- storage/temporal_storage_test.go | 153 +++++++++++++++++++++++++++++++ 4 files changed, 157 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 9a25d858c..d960621e5 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/DataDog/datadog-go v4.7.0+incompatible github.com/TykTechnologies/gorpc v0.0.0-20210624160652-fe65bda0ccb9 github.com/TykTechnologies/murmur3 v0.0.0-20230310161213-aad17efd5632 - github.com/TykTechnologies/storage v1.1.0 + github.com/TykTechnologies/storage v1.1.1 github.com/aws/aws-sdk-go-v2 v1.22.1 github.com/aws/aws-sdk-go-v2/config v1.9.0 github.com/aws/aws-sdk-go-v2/credentials v1.5.0 diff --git a/go.sum b/go.sum index bf2957168..ed52db105 100644 --- a/go.sum +++ b/go.sum @@ -43,6 +43,8 @@ github.com/TykTechnologies/murmur3 v0.0.0-20230310161213-aad17efd5632 h1:T5NWziF github.com/TykTechnologies/murmur3 v0.0.0-20230310161213-aad17efd5632/go.mod h1:UsPYgOFBpNzDXLEti7MKOwHLpVSqdzuNGkVFPspQmnQ= github.com/TykTechnologies/storage v1.1.0 h1:OgXbnuA0QCWWROtLRlztNTX6kKC3GkAwEEdPmvuAPE4= github.com/TykTechnologies/storage v1.1.0/go.mod h1:zcANqpIsDL/l/1zsMMERmjBeJYpER9XMi/dw2Gqa7m4= +github.com/TykTechnologies/storage v1.1.1 h1:mD9etJLHsXbluQIDX8/05Lk+ciqaAMXL5Pts9Wy+WAU= +github.com/TykTechnologies/storage v1.1.1/go.mod h1:zcANqpIsDL/l/1zsMMERmjBeJYpER9XMi/dw2Gqa7m4= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= diff --git a/pumps/version.go b/pumps/version.go index 119270cfb..82baeec46 100644 --- a/pumps/version.go +++ b/pumps/version.go @@ -1,7 +1,7 @@ package pumps var ( - Version = "v1.8.0" + Version = "v1.9.0" BuiltBy string Commit string BuildDate string diff --git a/storage/temporal_storage_test.go b/storage/temporal_storage_test.go index 033984b4d..f47860b22 100644 --- a/storage/temporal_storage_test.go +++ b/storage/temporal_storage_test.go @@ -2,6 +2,7 @@ package storage import ( "context" + "errors" "fmt" "testing" "time" @@ -150,3 +151,155 @@ func TestNewTemporalClusterStorageHandler(t *testing.T) { }) } } +func TestTemporalStorageHandler_ensureConnection(t *testing.T) { + conf := make(map[string]interface{}) + conf["host"] = "localhost" + conf["port"] = 6379 + + r, err := NewTemporalStorageHandler(conf, false) + if err != nil { + t.Fatal(err) + } + + if err := r.Init(); err != nil { + t.Fatal("unable to connect", err.Error()) + } + + if connectorSingleton == nil { + t.Fatal("connectorSingleton is nil") + } + + t.Run("Connection already established", func(t *testing.T) { + err := r.ensureConnection() + assert.NoError(t, err, "Expected no error when connection is already established") + }) + + t.Run("Connection dropped, reconnecting", func(t *testing.T) { + connectorSingleton = nil + err := r.ensureConnection() + assert.NoError(t, err, "Expected no error when reconnecting") + assert.NotNil(t, connectorSingleton, "Expected connectorSingleton not to be nil after reconnecting") + }) + + // This test timeouts because of the exponential backoff: + // t.Run("Connection failed after several attempts", func(t *testing.T) { + // connectorSingleton = nil + // conf["type"] = "invalid" + // r, err = NewTemporalStorageHandler(conf, true) + // assert.NoError(t, err, "Expected no error when creating new TemporalStorageHandler") + + // err = r.ensureConnection() + // assert.Error(t, err, "Expected error when reconnecting after connection failure") + // }) +} + +func TestTemporalStorageHandler_SetKey(t *testing.T) { + conf := make(map[string]interface{}) + conf["host"] = "localhost" + conf["port"] = 6379 + + r, err := NewTemporalStorageHandler(conf, false) + if err != nil { + t.Fatal(err) + } + + if err := r.Init(); err != nil { + t.Fatal("unable to connect", err.Error()) + } + + if connectorSingleton == nil { + t.Fatal("connectorSingleton is nil") + } + + keyName := "testKey" + session := "testSession" + timeout := int64(60) + + err = r.SetKey(keyName, session, timeout) + if err != nil { + t.Fatal(err) + } + + // Verify that the key was set correctly + res, err := r.kv.Get(ctx, r.fixKey(keyName)) + if err != nil { + t.Fatal(err) + } + + if res != session { + t.Fatalf("Expected value %s, got %s", session, res) + } +} +func TestTemporalStorageHandler_GetName(t *testing.T) { + conf := make(map[string]interface{}) + conf["host"] = "localhost" + conf["port"] = 6379 + + r, err := NewTemporalStorageHandler(conf, false) + if err != nil { + t.Fatal(err) + } + + if err := r.Init(); err != nil { + t.Fatal("unable to connect", err.Error()) + } + + if connectorSingleton == nil { + t.Fatal("connectorSingleton is nil") + } + + expected := "redis" + result := r.GetName() + + if result != expected { + t.Fatalf("Expected %s, but got %s", expected, result) + } +} +func TestTemporalStorageHandler_Init(t *testing.T) { + testCases := []struct { + name string + conf map[string]interface{} + forceReconnect bool + errExpected error + }{ + { + name: "Valid configuration", + conf: map[string]interface{}{ + "host": "localhost", + "port": 6379, + }, + }, + { + name: "Invalid configuration", + conf: map[string]interface{}{ + "host": "abc", + "port": 6379, + "type": "invalid", + }, + forceReconnect: true, + errExpected: errors.New("unsupported database type: invalid"), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + r, err := NewTemporalStorageHandler(tc.conf, tc.forceReconnect) + if err != nil { + t.Fatal(err) + } + + err = r.Init() + if err != nil { + assert.Error(t, err, tc.errExpected) + return + } + + assert.NotNil(t, r.Config, "Expected Config not to be nil") + assert.NotNil(t, connectorSingleton, "Expected connectorSingleton not to be nil") + assert.NotNil(t, r.kv, "Expected kv not to be nil") + assert.NotNil(t, r.list, "Expected list not to be nil") + assert.Equal(t, model.RedisV9Type, connectorSingleton.Type(), "Expected connection type to be RedisV9Type") + assert.NoError(t, connectorSingleton.Ping(context.Background()), "Expected no error on ping") + }) + } +} From f27c763d249b2bd9edd3fd49c67187f4c25f5716 Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Thu, 25 Jan 2024 18:01:28 -0300 Subject: [PATCH 24/25] removing commented code --- storage/temporal_storage_test.go | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/storage/temporal_storage_test.go b/storage/temporal_storage_test.go index f47860b22..ce8627cbc 100644 --- a/storage/temporal_storage_test.go +++ b/storage/temporal_storage_test.go @@ -180,17 +180,6 @@ func TestTemporalStorageHandler_ensureConnection(t *testing.T) { assert.NoError(t, err, "Expected no error when reconnecting") assert.NotNil(t, connectorSingleton, "Expected connectorSingleton not to be nil after reconnecting") }) - - // This test timeouts because of the exponential backoff: - // t.Run("Connection failed after several attempts", func(t *testing.T) { - // connectorSingleton = nil - // conf["type"] = "invalid" - // r, err = NewTemporalStorageHandler(conf, true) - // assert.NoError(t, err, "Expected no error when creating new TemporalStorageHandler") - - // err = r.ensureConnection() - // assert.Error(t, err, "Expected error when reconnecting after connection failure") - // }) } func TestTemporalStorageHandler_SetKey(t *testing.T) { From 52d7dfc085ceee48d34276a892d249f9e41ef118 Mon Sep 17 00:00:00 2001 From: Matias <83959431+mativm02@users.noreply.github.com> Date: Thu, 25 Jan 2024 18:13:41 -0300 Subject: [PATCH 25/25] linting --- storage/temporal_storage_test.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/storage/temporal_storage_test.go b/storage/temporal_storage_test.go index ce8627cbc..cd2f2eadb 100644 --- a/storage/temporal_storage_test.go +++ b/storage/temporal_storage_test.go @@ -63,7 +63,6 @@ func TestRedisClusterStorageManager_GetAndDeleteSet(t *testing.T) { for _, tt := range testData { t.Run(fmt.Sprintf("in: %v", tt), func(t *testing.T) { - ctx := context.Background() if tt.in != nil { in := [][]byte{} for _, v := range tt.in { @@ -151,6 +150,7 @@ func TestNewTemporalClusterStorageHandler(t *testing.T) { }) } } + func TestTemporalStorageHandler_ensureConnection(t *testing.T) { conf := make(map[string]interface{}) conf["host"] = "localhost" @@ -219,6 +219,7 @@ func TestTemporalStorageHandler_SetKey(t *testing.T) { t.Fatalf("Expected value %s, got %s", session, res) } } + func TestTemporalStorageHandler_GetName(t *testing.T) { conf := make(map[string]interface{}) conf["host"] = "localhost" @@ -244,12 +245,13 @@ func TestTemporalStorageHandler_GetName(t *testing.T) { t.Fatalf("Expected %s, but got %s", expected, result) } } + func TestTemporalStorageHandler_Init(t *testing.T) { testCases := []struct { - name string conf map[string]interface{} - forceReconnect bool errExpected error + name string + forceReconnect bool }{ { name: "Valid configuration",