github.com/kaydxh/golang@v0.0.131/pkg/database/redis/redis.go (about)

     1  /*
     2   *Copyright (c) 2022, kaydxh
     3   *
     4   *Permission is hereby granted, free of charge, to any person obtaining a copy
     5   *of this software and associated documentation files (the "Software"), to deal
     6   *in the Software without restriction, including without limitation the rights
     7   *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8   *copies of the Software, and to permit persons to whom the Software is
     9   *furnished to do so, subject to the following conditions:
    10   *
    11   *The above copyright notice and this permission notice shall be included in all
    12   *copies or substantial portions of the Software.
    13   *
    14   *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15   *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16   *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17   *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18   *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19   *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    20   *SOFTWARE.
    21   */
    22  package redis
    23  
    24  import (
    25  	"context"
    26  	"fmt"
    27  	"time"
    28  
    29  	"github.com/go-redis/redis/v8"
    30  
    31  	time_ "github.com/kaydxh/golang/go/time"
    32  )
    33  
    34  var redisDB RedisDB
    35  
    36  // Default values for Redis.
    37  const (
    38  	DefaultMinIdleConns = 10
    39  	DefaultPoolSize     = 10
    40  	DefaultDialTimeout  = 5 * time.Second
    41  	DefaultReadTimeout  = 5 * time.Second
    42  	DefaultWriteTimeout = 5 * time.Second
    43  	DefaultMasterName   = "mymaster"
    44  )
    45  
    46  type DBConfig struct {
    47  	Addresses []string
    48  	UserName  string
    49  	Password  string
    50  	DB        int
    51  }
    52  
    53  type RedisClient struct {
    54  	Conf DBConfig
    55  	db   *redis.Client
    56  
    57  	opts struct {
    58  		poolSize     int
    59  		minIdleConns int
    60  		dialTimeout  time.Duration
    61  		readTimeout  time.Duration
    62  		writeTimeout time.Duration
    63  		masterName   string
    64  	}
    65  }
    66  
    67  func NewRedisClient(conf DBConfig, opts ...RedisOption) *RedisClient {
    68  	c := &RedisClient{
    69  		Conf: conf,
    70  	}
    71  	c.opts.poolSize = DefaultPoolSize
    72  	c.opts.minIdleConns = DefaultMinIdleConns
    73  	c.opts.dialTimeout = DefaultDialTimeout
    74  	c.opts.readTimeout = DefaultReadTimeout
    75  	c.opts.writeTimeout = DefaultWriteTimeout
    76  	c.opts.masterName = DefaultMasterName
    77  
    78  	c.ApplyOptions(opts...)
    79  
    80  	return c
    81  }
    82  
    83  func GetDB() *redis.Client {
    84  	return redisDB.Load()
    85  }
    86  
    87  func (r *RedisClient) GetRedis(ctx context.Context) (*redis.Client, error) {
    88  	if r.db != nil {
    89  		return r.db, nil
    90  	}
    91  
    92  	if len(r.Conf.Addresses) == 0 {
    93  		return nil, fmt.Errorf("invalid redis address")
    94  	}
    95  
    96  	var db *redis.Client
    97  	if len(r.Conf.Addresses) == 1 {
    98  		db = redis.NewClient(&redis.Options{
    99  			Addr:         r.Conf.Addresses[0],
   100  			Password:     r.Conf.Password,
   101  			DB:           r.Conf.DB,
   102  			PoolSize:     r.opts.poolSize,
   103  			MinIdleConns: r.opts.minIdleConns,
   104  			DialTimeout:  r.opts.dialTimeout,
   105  			ReadTimeout:  r.opts.readTimeout,
   106  			WriteTimeout: r.opts.writeTimeout,
   107  		})
   108  	}
   109  
   110  	if len(r.Conf.Addresses) > 1 {
   111  		db = redis.NewFailoverClient(&redis.FailoverOptions{
   112  			MasterName:    r.opts.masterName,
   113  			SentinelAddrs: r.Conf.Addresses,
   114  			Password:      r.Conf.Password,
   115  			DB:            r.Conf.DB,
   116  			PoolSize:      r.opts.poolSize,
   117  			MinIdleConns:  r.opts.minIdleConns,
   118  			DialTimeout:   r.opts.dialTimeout,
   119  			ReadTimeout:   r.opts.readTimeout,
   120  			WriteTimeout:  r.opts.writeTimeout,
   121  		})
   122  	}
   123  	_, err := db.Ping(ctx).Result()
   124  	if err != nil {
   125  		return nil, err
   126  	}
   127  
   128  	r.db = db
   129  	redisDB.Store(db)
   130  
   131  	return r.db, nil
   132  }
   133  
   134  func (r *RedisClient) GetDatabaseUntil(
   135  	ctx context.Context,
   136  	maxWaitInterval time.Duration, failAfter time.Duration) (*redis.Client, error) {
   137  
   138  	var db *redis.Client
   139  	exp := time_.NewExponentialBackOff(
   140  		time_.WithExponentialBackOffOptionMaxInterval(maxWaitInterval),
   141  		time_.WithExponentialBackOffOptionMaxElapsedTime(failAfter),
   142  	)
   143  	err := time_.BackOffUntilWithContext(ctx, func(ctx context.Context) (err_ error) {
   144  		db, err_ = r.GetRedis(ctx)
   145  		if err_ != nil {
   146  			return err_
   147  		}
   148  		return nil
   149  	}, exp, true, false)
   150  	if err != nil {
   151  		return nil, fmt.Errorf("get database fail after: %v", failAfter)
   152  	}
   153  
   154  	return db, nil
   155  
   156  }
   157  
   158  func (r *RedisClient) Close() error {
   159  	if r.db == nil {
   160  		return fmt.Errorf("no redis client")
   161  	}
   162  	return r.db.Close()
   163  }