github.com/craicoverflow/tyk@v2.9.6-rc3+incompatible/storage/redis_cluster.go (about) 1 package storage 2 3 import ( 4 "errors" 5 "fmt" 6 "strconv" 7 "strings" 8 "sync" 9 "time" 10 11 "crypto/tls" 12 13 "github.com/go-redis/redis" 14 uuid "github.com/satori/go.uuid" 15 "github.com/sirupsen/logrus" 16 17 "github.com/TykTechnologies/tyk/config" 18 ) 19 20 // ------------------- REDIS CLUSTER STORAGE MANAGER ------------------------------- 21 22 const ( 23 waitStorageRetriesNum = 5 24 waitStorageRetriesInterval = 1 * time.Second 25 26 defaultRedisPort = 6379 27 ) 28 29 var ( 30 redisSingletonMu sync.RWMutex 31 32 redisClusterSingleton redis.UniversalClient 33 redisCacheClusterSingleton redis.UniversalClient 34 ) 35 36 // RedisCluster is a storage manager that uses the redis database. 37 type RedisCluster struct { 38 KeyPrefix string 39 HashKeys bool 40 IsCache bool 41 } 42 43 func clusterConnectionIsOpen(cluster *RedisCluster) bool { 44 testKey := "redis-test-" + uuid.NewV4().String() 45 // set test key 46 if err := cluster.SetKey(testKey, "test", 1); err != nil { 47 return false 48 } 49 // get test key 50 if _, err := cluster.GetKey(testKey); err != nil { 51 return false 52 } 53 return true 54 } 55 56 // IsConnected waits with retries until Redis connection pools are connected 57 func IsConnected() bool { 58 // create temporary ones to access singletons 59 testClusters := []*RedisCluster{ 60 {}, 61 } 62 if config.Global().EnableSeperateCacheStore { 63 testClusters = append(testClusters, &RedisCluster{IsCache: true}) 64 } 65 for _, cluster := range testClusters { 66 cluster.Connect() 67 } 68 69 // wait for connection pools with retries 70 retryNum := 0 71 for { 72 if retryNum == waitStorageRetriesNum { 73 log.Error("Waiting for Redis connection pools failed") 74 return false 75 } 76 77 // check that redis is available 78 var redisIsReady bool 79 for _, cluster := range testClusters { 80 redisIsReady = cluster.singleton() != nil && clusterConnectionIsOpen(cluster) 81 if !redisIsReady { 82 break 83 } 84 } 85 if redisIsReady { 86 break 87 } 88 89 // sleep before next check 90 log.WithField("currRetry", retryNum).Info("Waiting for Redis connection pools to be ready") 91 time.Sleep(waitStorageRetriesInterval) 92 retryNum++ 93 } 94 log.WithField("currRetry", retryNum).Info("Redis connection pools are ready after number of retires") 95 96 return true 97 } 98 99 func NewRedisClusterPool(isCache bool) redis.UniversalClient { 100 // redisSingletonMu is locked and we know the singleton is nil 101 cfg := config.Global().Storage 102 if isCache && config.Global().EnableSeperateCacheStore { 103 cfg = config.Global().CacheStorage 104 } 105 106 log.Debug("Creating new Redis connection pool") 107 108 // poolSize applies per cluster node and not for the whole cluster. 109 poolSize := 500 110 if cfg.MaxActive > 0 { 111 poolSize = cfg.MaxActive 112 } 113 114 timeout := 5 * time.Second 115 116 if cfg.Timeout > 0 { 117 timeout = time.Duration(cfg.Timeout) * time.Second 118 } 119 120 var tlsConfig *tls.Config 121 122 if cfg.UseSSL { 123 tlsConfig = &tls.Config{ 124 InsecureSkipVerify: cfg.SSLInsecureSkipVerify, 125 } 126 } 127 128 var client redis.UniversalClient 129 opts := &RedisOpts{ 130 Addrs: getRedisAddrs(cfg), 131 MasterName: cfg.MasterName, 132 Password: cfg.Password, 133 DB: cfg.Database, 134 DialTimeout: timeout, 135 ReadTimeout: timeout, 136 WriteTimeout: timeout, 137 IdleTimeout: 240 * timeout, 138 PoolSize: poolSize, 139 TLSConfig: tlsConfig, 140 } 141 142 if opts.MasterName != "" { 143 log.Info("--> [REDIS] Creating sentinel-backed failover client") 144 client = redis.NewFailoverClient(opts.failover()) 145 } else if cfg.EnableCluster { 146 log.Info("--> [REDIS] Creating cluster client") 147 client = redis.NewClusterClient(opts.cluster()) 148 } else { 149 log.Info("--> [REDIS] Creating single-node client") 150 client = redis.NewClient(opts.simple()) 151 } 152 153 return client 154 } 155 156 func getRedisAddrs(config config.StorageOptionsConf) (addrs []string) { 157 if len(config.Addrs) != 0 { 158 addrs = config.Addrs 159 } else { 160 for h, p := range config.Hosts { 161 addr := h + ":" + p 162 addrs = append(addrs, addr) 163 } 164 } 165 166 if len(addrs) == 0 && config.Port != 0 { 167 addr := config.Host + ":" + strconv.Itoa(config.Port) 168 addrs = append(addrs, addr) 169 } 170 171 return addrs 172 } 173 174 // RedisOpts is the overriden type of redis.UniversalOptions. simple() and cluster() functions are not public 175 // in redis library. Therefore, they are redefined in here to use in creation of new redis cluster logic. 176 // We don't want to use redis.NewUniversalClient() logic. 177 type RedisOpts redis.UniversalOptions 178 179 func (o *RedisOpts) cluster() *redis.ClusterOptions { 180 if len(o.Addrs) == 0 { 181 o.Addrs = []string{"127.0.0.1:6379"} 182 } 183 184 return &redis.ClusterOptions{ 185 Addrs: o.Addrs, 186 OnConnect: o.OnConnect, 187 188 Password: o.Password, 189 190 MaxRedirects: o.MaxRedirects, 191 ReadOnly: o.ReadOnly, 192 RouteByLatency: o.RouteByLatency, 193 RouteRandomly: o.RouteRandomly, 194 195 MaxRetries: o.MaxRetries, 196 MinRetryBackoff: o.MinRetryBackoff, 197 MaxRetryBackoff: o.MaxRetryBackoff, 198 199 DialTimeout: o.DialTimeout, 200 ReadTimeout: o.ReadTimeout, 201 WriteTimeout: o.WriteTimeout, 202 PoolSize: o.PoolSize, 203 MinIdleConns: o.MinIdleConns, 204 MaxConnAge: o.MaxConnAge, 205 PoolTimeout: o.PoolTimeout, 206 IdleTimeout: o.IdleTimeout, 207 IdleCheckFrequency: o.IdleCheckFrequency, 208 209 TLSConfig: o.TLSConfig, 210 } 211 } 212 213 func (o *RedisOpts) simple() *redis.Options { 214 addr := "127.0.0.1:6379" 215 if len(o.Addrs) > 0 { 216 addr = o.Addrs[0] 217 } 218 219 return &redis.Options{ 220 Addr: addr, 221 OnConnect: o.OnConnect, 222 223 DB: o.DB, 224 Password: o.Password, 225 226 MaxRetries: o.MaxRetries, 227 MinRetryBackoff: o.MinRetryBackoff, 228 MaxRetryBackoff: o.MaxRetryBackoff, 229 230 DialTimeout: o.DialTimeout, 231 ReadTimeout: o.ReadTimeout, 232 WriteTimeout: o.WriteTimeout, 233 234 PoolSize: o.PoolSize, 235 MinIdleConns: o.MinIdleConns, 236 MaxConnAge: o.MaxConnAge, 237 PoolTimeout: o.PoolTimeout, 238 IdleTimeout: o.IdleTimeout, 239 IdleCheckFrequency: o.IdleCheckFrequency, 240 241 TLSConfig: o.TLSConfig, 242 } 243 } 244 245 func (o *RedisOpts) failover() *redis.FailoverOptions { 246 if len(o.Addrs) == 0 { 247 o.Addrs = []string{"127.0.0.1:26379"} 248 } 249 250 return &redis.FailoverOptions{ 251 SentinelAddrs: o.Addrs, 252 MasterName: o.MasterName, 253 OnConnect: o.OnConnect, 254 255 DB: o.DB, 256 Password: o.Password, 257 258 MaxRetries: o.MaxRetries, 259 MinRetryBackoff: o.MinRetryBackoff, 260 MaxRetryBackoff: o.MaxRetryBackoff, 261 262 DialTimeout: o.DialTimeout, 263 ReadTimeout: o.ReadTimeout, 264 WriteTimeout: o.WriteTimeout, 265 266 PoolSize: o.PoolSize, 267 MinIdleConns: o.MinIdleConns, 268 MaxConnAge: o.MaxConnAge, 269 PoolTimeout: o.PoolTimeout, 270 IdleTimeout: o.IdleTimeout, 271 IdleCheckFrequency: o.IdleCheckFrequency, 272 273 TLSConfig: o.TLSConfig, 274 } 275 } 276 277 // Connect will establish a connection to the r.singleton() 278 func (r *RedisCluster) Connect() bool { 279 redisSingletonMu.Lock() 280 defer redisSingletonMu.Unlock() 281 282 disconnected := redisClusterSingleton == nil 283 if r.IsCache { 284 disconnected = redisCacheClusterSingleton == nil 285 } 286 287 if disconnected { 288 log.Debug("Connecting to redis cluster") 289 if r.IsCache { 290 redisCacheClusterSingleton = NewRedisClusterPool(true) 291 return true 292 } 293 redisClusterSingleton = NewRedisClusterPool(false) 294 return true 295 } 296 297 log.Debug("Storage Engine already initialised...") 298 return true 299 } 300 301 func (r *RedisCluster) singleton() redis.UniversalClient { 302 redisSingletonMu.RLock() 303 defer redisSingletonMu.RUnlock() 304 305 if r.IsCache { 306 return redisCacheClusterSingleton 307 } 308 return redisClusterSingleton 309 } 310 311 func (r *RedisCluster) hashKey(in string) string { 312 if !r.HashKeys { 313 // Not hashing? Return the raw key 314 return in 315 } 316 return HashStr(in) 317 } 318 319 func (r *RedisCluster) fixKey(keyName string) string { 320 return r.KeyPrefix + r.hashKey(keyName) 321 } 322 323 func (r *RedisCluster) cleanKey(keyName string) string { 324 return strings.Replace(keyName, r.KeyPrefix, "", 1) 325 } 326 327 func (r *RedisCluster) ensureConnection() { 328 if r.singleton() != nil { 329 // already connected 330 return 331 } 332 log.Info("Connection dropped, reconnecting...") 333 for { 334 r.Connect() 335 if r.singleton() != nil { 336 // reconnection worked 337 return 338 } 339 log.Info("Reconnecting again...") 340 } 341 } 342 343 // GetKey will retrieve a key from the database 344 func (r *RedisCluster) GetKey(keyName string) (string, error) { 345 r.ensureConnection() 346 347 cluster := r.singleton() 348 349 value, err := cluster.Get(r.fixKey(keyName)).Result() 350 if err != nil { 351 log.Debug("Error trying to get value:", err) 352 return "", ErrKeyNotFound 353 } 354 355 return value, nil 356 } 357 358 // GetMultiKey gets multiple keys from the database 359 func (r *RedisCluster) GetMultiKey(keys []string) ([]string, error) { 360 r.ensureConnection() 361 cluster := r.singleton() 362 keyNames := make([]string, len(keys)) 363 copy(keyNames, keys) 364 for index, val := range keyNames { 365 keyNames[index] = r.fixKey(val) 366 } 367 368 result := make([]string, 0) 369 370 switch v := cluster.(type) { 371 case *redis.ClusterClient: 372 { 373 getCmds := make([]*redis.StringCmd, 0) 374 pipe := v.Pipeline() 375 for _, key := range keyNames { 376 getCmds = append(getCmds, pipe.Get(key)) 377 } 378 _, err := pipe.Exec() 379 if err != nil && err != redis.Nil { 380 log.WithError(err).Debug("Error trying to get value") 381 return nil, ErrKeyNotFound 382 } 383 for _, cmd := range getCmds { 384 result = append(result, cmd.Val()) 385 } 386 } 387 case *redis.Client: 388 { 389 values, err := cluster.MGet(keyNames...).Result() 390 if err != nil { 391 log.WithError(err).Debug("Error trying to get value") 392 return nil, ErrKeyNotFound 393 } 394 for _, val := range values { 395 strVal := fmt.Sprint(val) 396 if strVal == "<nil>" { 397 strVal = "" 398 } 399 result = append(result, strVal) 400 } 401 } 402 } 403 404 for _, val := range result { 405 if val != "" { 406 return result, nil 407 } 408 } 409 410 return nil, ErrKeyNotFound 411 } 412 413 func (r *RedisCluster) GetKeyTTL(keyName string) (ttl int64, err error) { 414 r.ensureConnection() 415 duration, err := r.singleton().TTL(r.fixKey(keyName)).Result() 416 return int64(duration.Seconds()), err 417 } 418 419 func (r *RedisCluster) GetRawKey(keyName string) (string, error) { 420 r.ensureConnection() 421 value, err := r.singleton().Get(keyName).Result() 422 if err != nil { 423 log.Debug("Error trying to get value:", err) 424 return "", ErrKeyNotFound 425 } 426 427 return value, nil 428 } 429 430 func (r *RedisCluster) GetExp(keyName string) (int64, error) { 431 // log.Debug("Getting exp for key: ", r.fixKey(keyName)) 432 r.ensureConnection() 433 434 value, err := r.singleton().TTL(r.fixKey(keyName)).Result() 435 if err != nil { 436 log.Error("Error trying to get TTL: ", err) 437 return 0, ErrKeyNotFound 438 } 439 return int64(value.Seconds()), nil 440 } 441 442 func (r *RedisCluster) SetExp(keyName string, timeout int64) error { 443 err := r.singleton().Expire(r.fixKey(keyName), time.Duration(timeout)*time.Second).Err() 444 if err != nil { 445 log.Error("Could not EXPIRE key: ", err) 446 } 447 return err 448 } 449 450 // SetKey will create (or update) a key value in the store 451 func (r *RedisCluster) SetKey(keyName, session string, timeout int64) error { 452 //log.Debug("[STORE] SET Raw key is: ", keyName) 453 //log.Debug("[STORE] Setting key: ", r.fixKey(keyName)) 454 455 r.ensureConnection() 456 err := r.singleton().Set(r.fixKey(keyName), session, 0).Err() 457 if err != nil { 458 log.Error("Error trying to set value: ", err) 459 return err 460 } 461 462 if timeout > 0 { 463 err := r.singleton().Expire(r.fixKey(keyName), time.Duration(timeout)*time.Second).Err() 464 if err != nil { 465 log.Error("Error trying to set expiry:", err) 466 return err 467 } 468 } 469 470 return nil 471 } 472 473 func (r *RedisCluster) SetRawKey(keyName, session string, timeout int64) error { 474 r.ensureConnection() 475 err := r.singleton().Set(keyName, session, time.Duration(timeout)*time.Second).Err() 476 if err != nil { 477 log.Error("Error trying to set value: ", err) 478 return err 479 } 480 return nil 481 } 482 483 // Decrement will decrement a key in redis 484 func (r *RedisCluster) Decrement(keyName string) { 485 keyName = r.fixKey(keyName) 486 // log.Debug("Decrementing key: ", keyName) 487 r.ensureConnection() 488 err := r.singleton().Decr(keyName).Err() 489 if err != nil { 490 log.Error("Error trying to decrement value:", err) 491 } 492 } 493 494 // IncrementWithExpire will increment a key in redis 495 func (r *RedisCluster) IncrememntWithExpire(keyName string, expire int64) int64 { 496 // log.Debug("Incrementing raw key: ", keyName) 497 r.ensureConnection() 498 499 // This function uses a raw key, so we shouldn't call fixKey 500 fixedKey := keyName 501 val, err := r.singleton().Incr(fixedKey).Result() 502 503 if err != nil { 504 log.Error("Error trying to increment value:", err) 505 } else { 506 log.Debug("Incremented key: ", fixedKey, ", val is: ", val) 507 } 508 509 if val == 1 && expire != 0 { 510 log.Debug("--> Setting Expire") 511 r.singleton().Expire(fixedKey, time.Duration(expire)*time.Second) 512 } 513 514 return val 515 } 516 517 // GetKeys will return all keys according to the filter (filter is a prefix - e.g. tyk.keys.*) 518 func (r *RedisCluster) GetKeys(filter string) []string { 519 r.ensureConnection() 520 client := r.singleton() 521 522 filterHash := "" 523 if filter != "" { 524 filterHash = r.hashKey(filter) 525 } 526 searchStr := r.KeyPrefix + filterHash + "*" 527 log.Debug("[STORE] Getting list by: ", searchStr) 528 529 fnFetchKeys := func(client *redis.Client) ([]string, error) { 530 values := make([]string, 0) 531 532 iter := client.Scan(0, searchStr, 0).Iterator() 533 for iter.Next() { 534 values = append(values, iter.Val()) 535 } 536 537 if err := iter.Err(); err != nil { 538 return nil, err 539 } 540 541 return values, nil 542 } 543 544 var err error 545 sessions := make([]string, 0) 546 547 switch v := client.(type) { 548 case *redis.ClusterClient: 549 ch := make(chan []string) 550 551 go func() { 552 err = v.ForEachMaster(func(client *redis.Client) error { 553 values, err := fnFetchKeys(client) 554 if err != nil { 555 return err 556 } 557 558 ch <- values 559 return nil 560 }) 561 close(ch) 562 }() 563 564 for res := range ch { 565 sessions = append(sessions, res...) 566 } 567 case *redis.Client: 568 sessions, err = fnFetchKeys(v) 569 } 570 571 if err != nil { 572 log.Error("Error while fetching keys:", err) 573 return nil 574 } 575 576 for i, v := range sessions { 577 sessions[i] = r.cleanKey(v) 578 } 579 580 return sessions 581 } 582 583 // GetKeysAndValuesWithFilter will return all keys and their values with a filter 584 func (r *RedisCluster) GetKeysAndValuesWithFilter(filter string) map[string]string { 585 r.ensureConnection() 586 keys := r.GetKeys(filter) 587 if keys == nil { 588 log.Error("Error trying to get filtered client keys") 589 return nil 590 } 591 592 if len(keys) == 0 { 593 return nil 594 } 595 596 for i, v := range keys { 597 keys[i] = r.KeyPrefix + v 598 } 599 600 client := r.singleton() 601 values := make([]string, 0) 602 603 switch v := client.(type) { 604 case *redis.ClusterClient: 605 { 606 getCmds := make([]*redis.StringCmd, 0) 607 pipe := v.Pipeline() 608 for _, key := range keys { 609 getCmds = append(getCmds, pipe.Get(key)) 610 } 611 _, err := pipe.Exec() 612 if err != nil && err != redis.Nil { 613 log.Error("Error trying to get client keys: ", err) 614 return nil 615 } 616 617 for _, cmd := range getCmds { 618 values = append(values, cmd.Val()) 619 } 620 } 621 case *redis.Client: 622 { 623 result, err := v.MGet(keys...).Result() 624 if err != nil { 625 log.Error("Error trying to get client keys: ", err) 626 return nil 627 } 628 629 for _, val := range result { 630 strVal := fmt.Sprint(val) 631 if strVal == "<nil>" { 632 strVal = "" 633 } 634 values = append(values, strVal) 635 } 636 } 637 } 638 639 m := make(map[string]string) 640 for i, v := range keys { 641 m[r.cleanKey(v)] = values[i] 642 643 } 644 645 return m 646 } 647 648 // GetKeysAndValues will return all keys and their values - not to be used lightly 649 func (r *RedisCluster) GetKeysAndValues() map[string]string { 650 return r.GetKeysAndValuesWithFilter("") 651 } 652 653 // DeleteKey will remove a key from the database 654 func (r *RedisCluster) DeleteKey(keyName string) bool { 655 r.ensureConnection() 656 log.Debug("DEL Key was: ", keyName) 657 log.Debug("DEL Key became: ", r.fixKey(keyName)) 658 n, err := r.singleton().Del(r.fixKey(keyName)).Result() 659 if err != nil { 660 log.WithError(err).Error("Error trying to delete key") 661 } 662 663 return n > 0 664 } 665 666 // DeleteAllKeys will remove all keys from the database. 667 func (r *RedisCluster) DeleteAllKeys() bool { 668 r.ensureConnection() 669 n, err := r.singleton().FlushAll().Result() 670 if err != nil { 671 log.WithError(err).Error("Error trying to delete keys") 672 } 673 674 if n == "OK" { 675 return true 676 } 677 678 return false 679 } 680 681 // DeleteKey will remove a key from the database without prefixing, assumes user knows what they are doing 682 func (r *RedisCluster) DeleteRawKey(keyName string) bool { 683 r.ensureConnection() 684 n, err := r.singleton().Del(keyName).Result() 685 if err != nil { 686 log.WithError(err).Error("Error trying to delete key") 687 } 688 689 return n > 0 690 } 691 692 // DeleteKeys will remove a group of keys in bulk 693 func (r *RedisCluster) DeleteScanMatch(pattern string) bool { 694 r.ensureConnection() 695 client := r.singleton() 696 log.Debug("Deleting: ", pattern) 697 698 fnScan := func(client *redis.Client) ([]string, error) { 699 values := make([]string, 0) 700 701 iter := client.Scan(0, pattern, 0).Iterator() 702 for iter.Next() { 703 values = append(values, iter.Val()) 704 } 705 706 if err := iter.Err(); err != nil { 707 return nil, err 708 } 709 710 return values, nil 711 } 712 713 var err error 714 var keys []string 715 716 switch v := client.(type) { 717 case *redis.ClusterClient: 718 ch := make(chan []string) 719 go func() { 720 err = v.ForEachMaster(func(client *redis.Client) error { 721 values, err := fnScan(client) 722 if err != nil { 723 return err 724 } 725 726 ch <- values 727 return nil 728 }) 729 close(ch) 730 }() 731 732 for vals := range ch { 733 keys = append(keys, vals...) 734 } 735 case *redis.Client: 736 keys, err = fnScan(v) 737 } 738 739 if err != nil { 740 log.Error("SCAN command field with err:", err) 741 return false 742 } 743 744 if len(keys) > 0 { 745 for _, name := range keys { 746 log.Info("Deleting: ", name) 747 err := client.Del(name).Err() 748 if err != nil { 749 log.Error("Error trying to delete key: ", name, " - ", err) 750 } 751 } 752 log.Info("Deleted: ", len(keys), " records") 753 } else { 754 log.Debug("RedisCluster called DEL - Nothing to delete") 755 } 756 757 return true 758 } 759 760 // DeleteKeys will remove a group of keys in bulk 761 func (r *RedisCluster) DeleteKeys(keys []string) bool { 762 r.ensureConnection() 763 if len(keys) > 0 { 764 for i, v := range keys { 765 keys[i] = r.fixKey(v) 766 } 767 768 log.Debug("Deleting: ", keys) 769 client := r.singleton() 770 switch v := client.(type) { 771 case *redis.ClusterClient: 772 { 773 pipe := v.Pipeline() 774 for _, k := range keys { 775 pipe.Del(k) 776 } 777 778 if _, err := pipe.Exec(); err != nil { 779 log.Error("Error trying to delete keys:", err) 780 } 781 } 782 case *redis.Client: 783 { 784 _, err := v.Del(keys...).Result() 785 if err != nil { 786 log.Error("Error trying to delete keys: ", err) 787 } 788 } 789 } 790 } else { 791 log.Debug("RedisCluster called DEL - Nothing to delete") 792 } 793 794 return true 795 } 796 797 // StartPubSubHandler will listen for a signal and run the callback for 798 // every subscription and message event. 799 func (r *RedisCluster) StartPubSubHandler(channel string, callback func(interface{})) error { 800 r.ensureConnection() 801 client := r.singleton() 802 if client == nil { 803 return errors.New("Redis connection failed") 804 } 805 806 pubsub := client.Subscribe(channel) 807 defer pubsub.Close() 808 809 for { 810 msg, err := pubsub.Receive() 811 if err != nil { 812 log.Error("Error while receiving pubsub message:", err) 813 return err 814 } 815 switch v := msg.(type) { 816 case *redis.Message: 817 callback(v) 818 819 case *redis.Subscription: 820 callback(v) 821 822 case error: 823 log.Error("Redis disconnected or error received, attempting to reconnect: ", v) 824 return v 825 } 826 } 827 } 828 829 func (r *RedisCluster) Publish(channel, message string) error { 830 r.ensureConnection() 831 err := r.singleton().Publish(channel, message).Err() 832 if err != nil { 833 log.Error("Error trying to set value: ", err) 834 return err 835 } 836 return nil 837 } 838 839 func (r *RedisCluster) GetAndDeleteSet(keyName string) []interface{} { 840 log.Debug("Getting raw key set: ", keyName) 841 r.ensureConnection() 842 log.Debug("keyName is: ", keyName) 843 fixedKey := r.fixKey(keyName) 844 log.Debug("Fixed keyname is: ", fixedKey) 845 846 client := r.singleton() 847 848 var lrange *redis.StringSliceCmd 849 _, err := client.TxPipelined(func(pipe redis.Pipeliner) error { 850 lrange = pipe.LRange(fixedKey, 0, -1) 851 pipe.Del(fixedKey) 852 return nil 853 }) 854 if err != nil { 855 log.Error("Multi command failed: ", err) 856 return nil 857 } 858 859 vals := lrange.Val() 860 log.Debug("Analytics returned: ", len(vals)) 861 if len(vals) == 0 { 862 return nil 863 } 864 865 log.Debug("Unpacked vals: ", len(vals)) 866 result := make([]interface{}, len(vals)) 867 for i, v := range vals { 868 result[i] = v 869 } 870 871 return result 872 } 873 874 func (r *RedisCluster) AppendToSet(keyName, value string) { 875 fixedKey := r.fixKey(keyName) 876 log.WithField("keyName", keyName).Debug("Pushing to raw key list") 877 log.WithField("fixedKey", fixedKey).Debug("Appending to fixed key list") 878 879 r.ensureConnection() 880 if err := r.singleton().RPush(fixedKey, value).Err(); err != nil { 881 log.WithError(err).Error("Error trying to append to set keys") 882 } 883 } 884 885 //Exists check if keyName exists 886 func (r *RedisCluster) Exists(keyName string) (bool, error) { 887 fixedKey := r.fixKey(keyName) 888 log.WithField("keyName", fixedKey).Debug("Checking if exists") 889 890 exists, err := r.singleton().Exists(fixedKey).Result() 891 if err != nil { 892 log.Error("Error trying to check if key exists: ", err) 893 return false, err 894 } 895 if exists == 1 { 896 return true, nil 897 } 898 return false, nil 899 } 900 901 // RemoveFromList delete an value from a list idetinfied with the keyName 902 func (r *RedisCluster) RemoveFromList(keyName, value string) error { 903 fixedKey := r.fixKey(keyName) 904 logEntry := logrus.Fields{ 905 "keyName": keyName, 906 "fixedKey": fixedKey, 907 "value": value, 908 } 909 log.WithFields(logEntry).Debug("Removing value from list") 910 911 if err := r.singleton().LRem(fixedKey, 0, value).Err(); err != nil { 912 log.WithFields(logEntry).WithError(err).Error("LREM command failed") 913 return err 914 } 915 916 return nil 917 } 918 919 // GetListRange gets range of elements of list identified by keyName 920 func (r *RedisCluster) GetListRange(keyName string, from, to int64) ([]string, error) { 921 fixedKey := r.fixKey(keyName) 922 logEntry := logrus.Fields{ 923 "keyName": keyName, 924 "fixedKey": fixedKey, 925 "from": from, 926 "to": to, 927 } 928 log.WithFields(logEntry).Debug("Getting list range") 929 930 elements, err := r.singleton().LRange(fixedKey, from, to).Result() 931 if err != nil { 932 log.WithFields(logEntry).WithError(err).Error("LRANGE command failed") 933 return nil, err 934 } 935 936 return elements, nil 937 } 938 939 func (r *RedisCluster) AppendToSetPipelined(key string, values []string) { 940 if len(values) == 0 { 941 return 942 } 943 944 fixedKey := r.fixKey(key) 945 r.ensureConnection() 946 client := r.singleton() 947 948 pipe := client.Pipeline() 949 for _, val := range values { 950 pipe.RPush(fixedKey, val) 951 } 952 953 if _, err := pipe.Exec(); err != nil { 954 log.WithError(err).Error("Error trying to append to set keys") 955 } 956 } 957 958 func (r *RedisCluster) GetSet(keyName string) (map[string]string, error) { 959 log.Debug("Getting from key set: ", keyName) 960 log.Debug("Getting from fixed key set: ", r.fixKey(keyName)) 961 r.ensureConnection() 962 963 val, err := r.singleton().SMembers(r.fixKey(keyName)).Result() 964 if err != nil { 965 log.Error("Error trying to get key set:", err) 966 return nil, err 967 } 968 969 result := make(map[string]string) 970 for i, value := range val { 971 result[strconv.Itoa(i)] = value 972 } 973 974 return result, nil 975 } 976 977 func (r *RedisCluster) AddToSet(keyName, value string) { 978 log.Debug("Pushing to raw key set: ", keyName) 979 log.Debug("Pushing to fixed key set: ", r.fixKey(keyName)) 980 r.ensureConnection() 981 err := r.singleton().SAdd(r.fixKey(keyName), value).Err() 982 if err != nil { 983 log.Error("Error trying to append keys: ", err) 984 } 985 } 986 987 func (r *RedisCluster) RemoveFromSet(keyName, value string) { 988 log.Debug("Removing from raw key set: ", keyName) 989 log.Debug("Removing from fixed key set: ", r.fixKey(keyName)) 990 r.ensureConnection() 991 992 err := r.singleton().SRem(r.fixKey(keyName), value).Err() 993 if err != nil { 994 log.Error("Error trying to remove keys: ", err) 995 } 996 } 997 998 func (r *RedisCluster) IsMemberOfSet(keyName, value string) bool { 999 r.ensureConnection() 1000 val, err := r.singleton().SIsMember(r.fixKey(keyName), value).Result() 1001 1002 if err != nil { 1003 log.Error("Error trying to check set memeber: ", err) 1004 return false 1005 } 1006 1007 log.Debug("SISMEMBER", keyName, value, val, err) 1008 1009 return val == true 1010 } 1011 1012 // SetRollingWindow will append to a sorted set in redis and extract a timed window of values 1013 func (r *RedisCluster) SetRollingWindow(keyName string, per int64, value_override string, pipeline bool) (int, []interface{}) { 1014 log.Debug("Incrementing raw key: ", keyName) 1015 r.ensureConnection() 1016 log.Debug("keyName is: ", keyName) 1017 now := time.Now() 1018 log.Debug("Now is:", now) 1019 onePeriodAgo := now.Add(time.Duration(-1*per) * time.Second) 1020 log.Debug("Then is: ", onePeriodAgo) 1021 1022 client := r.singleton() 1023 var zrange *redis.StringSliceCmd 1024 1025 pipeFn := func(pipe redis.Pipeliner) error { 1026 pipe.ZRemRangeByScore(keyName, "-inf", strconv.Itoa(int(onePeriodAgo.UnixNano()))) 1027 zrange = pipe.ZRange(keyName, 0, -1) 1028 1029 element := redis.Z{ 1030 Score: float64(now.UnixNano()), 1031 } 1032 1033 if value_override != "-1" { 1034 element.Member = value_override 1035 } else { 1036 element.Member = strconv.Itoa(int(now.UnixNano())) 1037 } 1038 1039 pipe.ZAdd(keyName, element) 1040 pipe.Expire(keyName, time.Duration(per)*time.Second) 1041 1042 return nil 1043 } 1044 1045 var err error 1046 if pipeline { 1047 _, err = client.Pipelined(pipeFn) 1048 } else { 1049 _, err = client.TxPipelined(pipeFn) 1050 } 1051 1052 if err != nil { 1053 log.Error("Multi command failed: ", err) 1054 return 0, nil 1055 } 1056 1057 values := zrange.Val() 1058 1059 // Check actual value 1060 if values == nil { 1061 return 0, nil 1062 } 1063 1064 intVal := len(values) 1065 result := make([]interface{}, len(values)) 1066 1067 for i, v := range values { 1068 result[i] = v 1069 } 1070 1071 log.Debug("Returned: ", intVal) 1072 1073 return intVal, result 1074 } 1075 1076 func (r RedisCluster) GetRollingWindow(keyName string, per int64, pipeline bool) (int, []interface{}) { 1077 r.ensureConnection() 1078 now := time.Now() 1079 onePeriodAgo := now.Add(time.Duration(-1*per) * time.Second) 1080 1081 client := r.singleton() 1082 var zrange *redis.StringSliceCmd 1083 1084 pipeFn := func(pipe redis.Pipeliner) error { 1085 pipe.ZRemRangeByScore(keyName, "-inf", strconv.Itoa(int(onePeriodAgo.UnixNano()))) 1086 zrange = pipe.ZRange(keyName, 0, -1) 1087 1088 return nil 1089 } 1090 1091 var err error 1092 if pipeline { 1093 _, err = client.Pipelined(pipeFn) 1094 } else { 1095 _, err = client.TxPipelined(pipeFn) 1096 } 1097 if err != nil { 1098 log.Error("Multi command failed: ", err) 1099 return 0, nil 1100 } 1101 1102 values := zrange.Val() 1103 1104 // Check actual value 1105 if values == nil { 1106 return 0, nil 1107 } 1108 1109 intVal := len(values) 1110 result := make([]interface{}, intVal) 1111 for i, v := range values { 1112 result[i] = v 1113 } 1114 1115 log.Debug("Returned: ", intVal) 1116 1117 return intVal, result 1118 } 1119 1120 // GetPrefix returns storage key prefix 1121 func (r *RedisCluster) GetKeyPrefix() string { 1122 return r.KeyPrefix 1123 } 1124 1125 // AddToSortedSet adds value with given score to sorted set identified by keyName 1126 func (r *RedisCluster) AddToSortedSet(keyName, value string, score float64) { 1127 fixedKey := r.fixKey(keyName) 1128 logEntry := logrus.Fields{ 1129 "keyName": keyName, 1130 "fixedKey": fixedKey, 1131 } 1132 log.WithFields(logEntry).Debug("Pushing raw key to sorted set") 1133 1134 r.ensureConnection() 1135 member := redis.Z{Score: score, Member: value} 1136 if err := r.singleton().ZAdd(fixedKey, member).Err(); err != nil { 1137 log.WithFields(logEntry).WithError(err).Error("ZADD command failed") 1138 } 1139 } 1140 1141 // GetSortedSetRange gets range of elements of sorted set identified by keyName 1142 func (r *RedisCluster) GetSortedSetRange(keyName, scoreFrom, scoreTo string) ([]string, []float64, error) { 1143 fixedKey := r.fixKey(keyName) 1144 logEntry := logrus.Fields{ 1145 "keyName": keyName, 1146 "fixedKey": fixedKey, 1147 "scoreFrom": scoreFrom, 1148 "scoreTo": scoreTo, 1149 } 1150 log.WithFields(logEntry).Debug("Getting sorted set range") 1151 1152 args := redis.ZRangeBy{Min: scoreFrom, Max: scoreTo} 1153 values, err := r.singleton().ZRangeByScoreWithScores(fixedKey, args).Result() 1154 if err != nil { 1155 log.WithFields(logEntry).WithError(err).Error("ZRANGEBYSCORE command failed") 1156 return nil, nil, err 1157 } 1158 1159 if len(values) == 0 { 1160 return nil, nil, nil 1161 } 1162 1163 elements := make([]string, len(values)) 1164 scores := make([]float64, len(values)) 1165 1166 for i, v := range values { 1167 elements[i] = fmt.Sprint(v.Member) 1168 scores[i] = v.Score 1169 } 1170 1171 return elements, scores, nil 1172 } 1173 1174 // RemoveSortedSetRange removes range of elements from sorted set identified by keyName 1175 func (r *RedisCluster) RemoveSortedSetRange(keyName, scoreFrom, scoreTo string) error { 1176 fixedKey := r.fixKey(keyName) 1177 logEntry := logrus.Fields{ 1178 "keyName": keyName, 1179 "fixedKey": fixedKey, 1180 "scoreFrom": scoreFrom, 1181 "scoreTo": scoreTo, 1182 } 1183 log.WithFields(logEntry).Debug("Removing sorted set range") 1184 1185 if err := r.singleton().ZRemRangeByScore(fixedKey, scoreFrom, scoreTo).Err(); err != nil { 1186 log.WithFields(logEntry).WithError(err).Error("ZREMRANGEBYSCORE command failed") 1187 return err 1188 } 1189 1190 return nil 1191 }