github.com/lingyao2333/mo-zero@v1.4.1/core/stores/cache/cache.go (about)

     1  package cache
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"log"
     8  	"time"
     9  
    10  	"github.com/lingyao2333/mo-zero/core/errorx"
    11  	"github.com/lingyao2333/mo-zero/core/hash"
    12  	"github.com/lingyao2333/mo-zero/core/syncx"
    13  )
    14  
    15  type (
    16  	// Cache interface is used to define the cache implementation.
    17  	Cache interface {
    18  		// Del deletes cached values with keys.
    19  		Del(keys ...string) error
    20  		// DelCtx deletes cached values with keys.
    21  		DelCtx(ctx context.Context, keys ...string) error
    22  		// Get gets the cache with key and fills into v.
    23  		Get(key string, val interface{}) error
    24  		// GetCtx gets the cache with key and fills into v.
    25  		GetCtx(ctx context.Context, key string, val interface{}) error
    26  		// IsNotFound checks if the given error is the defined errNotFound.
    27  		IsNotFound(err error) bool
    28  		// Set sets the cache with key and v, using c.expiry.
    29  		Set(key string, val interface{}) error
    30  		// SetCtx sets the cache with key and v, using c.expiry.
    31  		SetCtx(ctx context.Context, key string, val interface{}) error
    32  		// SetWithExpire sets the cache with key and v, using given expire.
    33  		SetWithExpire(key string, val interface{}, expire time.Duration) error
    34  		// SetWithExpireCtx sets the cache with key and v, using given expire.
    35  		SetWithExpireCtx(ctx context.Context, key string, val interface{}, expire time.Duration) error
    36  		// Take takes the result from cache first, if not found,
    37  		// query from DB and set cache using c.expiry, then return the result.
    38  		Take(val interface{}, key string, query func(val interface{}) error) error
    39  		// TakeCtx takes the result from cache first, if not found,
    40  		// query from DB and set cache using c.expiry, then return the result.
    41  		TakeCtx(ctx context.Context, val interface{}, key string, query func(val interface{}) error) error
    42  		// TakeWithExpire takes the result from cache first, if not found,
    43  		// query from DB and set cache using given expire, then return the result.
    44  		TakeWithExpire(val interface{}, key string, query func(val interface{}, expire time.Duration) error) error
    45  		// TakeWithExpireCtx takes the result from cache first, if not found,
    46  		// query from DB and set cache using given expire, then return the result.
    47  		TakeWithExpireCtx(ctx context.Context, val interface{}, key string,
    48  			query func(val interface{}, expire time.Duration) error) error
    49  	}
    50  
    51  	cacheCluster struct {
    52  		dispatcher  *hash.ConsistentHash
    53  		errNotFound error
    54  	}
    55  )
    56  
    57  // New returns a Cache.
    58  func New(c ClusterConf, barrier syncx.SingleFlight, st *Stat, errNotFound error,
    59  	opts ...Option) Cache {
    60  	if len(c) == 0 || TotalWeights(c) <= 0 {
    61  		log.Fatal("no cache nodes")
    62  	}
    63  
    64  	if len(c) == 1 {
    65  		return NewNode(c[0].NewRedis(), barrier, st, errNotFound, opts...)
    66  	}
    67  
    68  	dispatcher := hash.NewConsistentHash()
    69  	for _, node := range c {
    70  		cn := NewNode(node.NewRedis(), barrier, st, errNotFound, opts...)
    71  		dispatcher.AddWithWeight(cn, node.Weight)
    72  	}
    73  
    74  	return cacheCluster{
    75  		dispatcher:  dispatcher,
    76  		errNotFound: errNotFound,
    77  	}
    78  }
    79  
    80  // Del deletes cached values with keys.
    81  func (cc cacheCluster) Del(keys ...string) error {
    82  	return cc.DelCtx(context.Background(), keys...)
    83  }
    84  
    85  // DelCtx deletes cached values with keys.
    86  func (cc cacheCluster) DelCtx(ctx context.Context, keys ...string) error {
    87  	switch len(keys) {
    88  	case 0:
    89  		return nil
    90  	case 1:
    91  		key := keys[0]
    92  		c, ok := cc.dispatcher.Get(key)
    93  		if !ok {
    94  			return cc.errNotFound
    95  		}
    96  
    97  		return c.(Cache).DelCtx(ctx, key)
    98  	default:
    99  		var be errorx.BatchError
   100  		nodes := make(map[interface{}][]string)
   101  		for _, key := range keys {
   102  			c, ok := cc.dispatcher.Get(key)
   103  			if !ok {
   104  				be.Add(fmt.Errorf("key %q not found", key))
   105  				continue
   106  			}
   107  
   108  			nodes[c] = append(nodes[c], key)
   109  		}
   110  		for c, ks := range nodes {
   111  			if err := c.(Cache).DelCtx(ctx, ks...); err != nil {
   112  				be.Add(err)
   113  			}
   114  		}
   115  
   116  		return be.Err()
   117  	}
   118  }
   119  
   120  // Get gets the cache with key and fills into v.
   121  func (cc cacheCluster) Get(key string, val interface{}) error {
   122  	return cc.GetCtx(context.Background(), key, val)
   123  }
   124  
   125  // GetCtx gets the cache with key and fills into v.
   126  func (cc cacheCluster) GetCtx(ctx context.Context, key string, val interface{}) error {
   127  	c, ok := cc.dispatcher.Get(key)
   128  	if !ok {
   129  		return cc.errNotFound
   130  	}
   131  
   132  	return c.(Cache).GetCtx(ctx, key, val)
   133  }
   134  
   135  // IsNotFound checks if the given error is the defined errNotFound.
   136  func (cc cacheCluster) IsNotFound(err error) bool {
   137  	return errors.Is(err, cc.errNotFound)
   138  }
   139  
   140  // Set sets the cache with key and v, using c.expiry.
   141  func (cc cacheCluster) Set(key string, val interface{}) error {
   142  	return cc.SetCtx(context.Background(), key, val)
   143  }
   144  
   145  // SetCtx sets the cache with key and v, using c.expiry.
   146  func (cc cacheCluster) SetCtx(ctx context.Context, key string, val interface{}) error {
   147  	c, ok := cc.dispatcher.Get(key)
   148  	if !ok {
   149  		return cc.errNotFound
   150  	}
   151  
   152  	return c.(Cache).SetCtx(ctx, key, val)
   153  }
   154  
   155  // SetWithExpire sets the cache with key and v, using given expire.
   156  func (cc cacheCluster) SetWithExpire(key string, val interface{}, expire time.Duration) error {
   157  	return cc.SetWithExpireCtx(context.Background(), key, val, expire)
   158  }
   159  
   160  // SetWithExpireCtx sets the cache with key and v, using given expire.
   161  func (cc cacheCluster) SetWithExpireCtx(ctx context.Context, key string, val interface{}, expire time.Duration) error {
   162  	c, ok := cc.dispatcher.Get(key)
   163  	if !ok {
   164  		return cc.errNotFound
   165  	}
   166  
   167  	return c.(Cache).SetWithExpireCtx(ctx, key, val, expire)
   168  }
   169  
   170  // Take takes the result from cache first, if not found,
   171  // query from DB and set cache using c.expiry, then return the result.
   172  func (cc cacheCluster) Take(val interface{}, key string, query func(val interface{}) error) error {
   173  	return cc.TakeCtx(context.Background(), val, key, query)
   174  }
   175  
   176  // TakeCtx takes the result from cache first, if not found,
   177  // query from DB and set cache using c.expiry, then return the result.
   178  func (cc cacheCluster) TakeCtx(ctx context.Context, val interface{}, key string, query func(val interface{}) error) error {
   179  	c, ok := cc.dispatcher.Get(key)
   180  	if !ok {
   181  		return cc.errNotFound
   182  	}
   183  
   184  	return c.(Cache).TakeCtx(ctx, val, key, query)
   185  }
   186  
   187  // TakeWithExpire takes the result from cache first, if not found,
   188  // query from DB and set cache using given expire, then return the result.
   189  func (cc cacheCluster) TakeWithExpire(val interface{}, key string, query func(val interface{}, expire time.Duration) error) error {
   190  	return cc.TakeWithExpireCtx(context.Background(), val, key, query)
   191  }
   192  
   193  // TakeWithExpireCtx takes the result from cache first, if not found,
   194  // query from DB and set cache using given expire, then return the result.
   195  func (cc cacheCluster) TakeWithExpireCtx(ctx context.Context, val interface{}, key string, query func(val interface{}, expire time.Duration) error) error {
   196  	c, ok := cc.dispatcher.Get(key)
   197  	if !ok {
   198  		return cc.errNotFound
   199  	}
   200  
   201  	return c.(Cache).TakeWithExpireCtx(ctx, val, key, query)
   202  }