github.com/vicanso/lru-ttl@v1.5.1/l2cache.go (about)

     1  // Copyright 2020 tree xie
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // L2Cache use lru cache for the first cache, and slow cache for the second cache.
    16  // LRU cache should be set max entries for less memory usage but faster,
    17  // slow cache is slower and using more space, but it can store more data
    18  
    19  package lruttl
    20  
    21  import (
    22  	"bytes"
    23  	"context"
    24  	"encoding/json"
    25  	"errors"
    26  	"time"
    27  )
    28  
    29  type SlowCache interface {
    30  	Get(ctx context.Context, key string) ([]byte, error)
    31  	Set(ctx context.Context, key string, value []byte, ttl time.Duration) error
    32  	TTL(ctx context.Context, key string) (time.Duration, error)
    33  	Del(ctx context.Context, key string) (int64, error)
    34  }
    35  
    36  // L2CacheOption l2cache option
    37  type L2CacheOption func(c *L2Cache)
    38  
    39  type L2CacheMarshal func(v interface{}) ([]byte, error)
    40  type L2CacheUnmarshal func(data []byte, v interface{}) error
    41  
    42  // A l2cache for frequently visited  data
    43  type L2Cache struct {
    44  	// prefix is the prefix of all key, it will auto prepend to the key
    45  	prefix string
    46  	// ttl is the duration for cache
    47  	ttl time.Duration
    48  	// ttlCache is the ttl lru cache
    49  	ttlCache *Cache
    50  	// slowCache is the slow cache for more data
    51  	slowCache SlowCache
    52  	// marshal is custom marshal function.
    53  	// It will be json.Marshal if not set
    54  	marshal L2CacheMarshal
    55  	// unmarshal is custom unmarshal function.
    56  	// It will be json.Unmarshal if not set
    57  	unmarshal L2CacheUnmarshal
    58  
    59  	nilErr error
    60  }
    61  
    62  // ErrIsNil is the error of nil cache
    63  var ErrIsNil = errors.New("cache is nil")
    64  
    65  // ErrKeyIsNil is the error of nil key
    66  var ErrKeyIsNil = errors.New("key is nil")
    67  
    68  // ErrInvalidType is the error of invalid type
    69  var ErrInvalidType = errors.New("invalid type")
    70  
    71  // BufferMarshal converts *bytes.Buffer to bytes,
    72  // it returns a ErrInvalidType if restult is not *bytes.Buffer
    73  func BufferMarshal(result interface{}) ([]byte, error) {
    74  	buf, ok := result.(*bytes.Buffer)
    75  	if !ok {
    76  		return nil, ErrInvalidType
    77  	}
    78  	return buf.Bytes(), nil
    79  }
    80  
    81  // BufferUnmarshal writes the data to buffer,
    82  // it returns a ErrInvalidType if restult is not *bytes.Buffer
    83  func BufferUnmarshal(data []byte, result interface{}) error {
    84  	buf, ok := result.(*bytes.Buffer)
    85  	if !ok {
    86  		return ErrInvalidType
    87  	}
    88  	_, err := buf.Write(data)
    89  	return err
    90  }
    91  
    92  // NewL2Cache return a new L2Cache,
    93  // it returns panic if maxEntries or defaultTTL is nil
    94  func NewL2Cache(slowCache SlowCache, maxEntries int, defaultTTL time.Duration, opts ...L2CacheOption) *L2Cache {
    95  	if defaultTTL < time.Second {
    96  		panic("default ttl should be gt one second")
    97  	}
    98  	c := &L2Cache{
    99  		ttl:       defaultTTL,
   100  		ttlCache:  New(maxEntries, defaultTTL),
   101  		slowCache: slowCache,
   102  	}
   103  	for _, opt := range opts {
   104  		opt(c)
   105  	}
   106  	return c
   107  }
   108  
   109  // L2CacheMarshalOption sets custom marshal function for l2cache
   110  func L2CacheMarshalOption(fn L2CacheMarshal) L2CacheOption {
   111  	return func(c *L2Cache) {
   112  		c.marshal = fn
   113  	}
   114  }
   115  
   116  // L2CacheUnmarshalOption sets custom unmarshal function for l2cache
   117  func L2CacheUnmarshalOption(fn L2CacheUnmarshal) L2CacheOption {
   118  	return func(c *L2Cache) {
   119  		c.unmarshal = fn
   120  	}
   121  }
   122  
   123  // L2CachePrefixOption sets prefix for l2cache
   124  func L2CachePrefixOption(prefix string) L2CacheOption {
   125  	return func(c *L2Cache) {
   126  		c.prefix = prefix
   127  	}
   128  }
   129  
   130  // L2CacheNilErrOption set nil error for l2cache
   131  func L2CacheNilErrOption(nilErr error) L2CacheOption {
   132  	return func(c *L2Cache) {
   133  		c.nilErr = nilErr
   134  	}
   135  }
   136  
   137  func (l2 *L2Cache) getKey(key string) (string, error) {
   138  	if key == "" {
   139  		return "", ErrKeyIsNil
   140  	}
   141  	return l2.prefix + key, nil
   142  }
   143  
   144  // TTL returns the ttl for key
   145  func (l2 *L2Cache) TTL(ctx context.Context, key string) (time.Duration, error) {
   146  	key, err := l2.getKey(key)
   147  	if err != nil {
   148  		return 0, err
   149  	}
   150  	d := l2.ttlCache.TTL(key)
   151  	// 小于0的表示不存在
   152  	// 由于lru有大小限制,可能由于空间不够导致不存在
   153  	// 不存在时则从slow cache获取
   154  	if d >= 0 {
   155  		return d, nil
   156  	}
   157  	return l2.slowCache.TTL(ctx, key)
   158  }
   159  
   160  // getBytes gets data from lru cache first, if not exists,
   161  // then gets the data from slow cache.
   162  func (l2 *L2Cache) getBytes(ctx context.Context, key string) ([]byte, error) {
   163  	v, ok := l2.ttlCache.Get(key)
   164  	var buf []byte
   165  	// 获取成功,而数据不为nil
   166  	// ok为false时,数据也可能不为空(已过期)
   167  	if ok && v != nil {
   168  		buf, _ = v.([]byte)
   169  	}
   170  	// 从lru中获取到可用数据
   171  	// lru中数据不存在(数据不存在或过期都有可能)
   172  	// 有可能数据未过期但lru空间较小,因此被删除
   173  	// 也有可能lru中数据过期但 slow cache中数据已更新
   174  	if len(buf) == 0 {
   175  		b, err := l2.slowCache.Get(ctx, key)
   176  		if err != nil {
   177  			return nil, err
   178  		}
   179  		buf = b
   180  		// 成功从slowcache获取缓存,则将数据设置回lru ttl
   181  		if len(buf) != 0 {
   182  			// 获取ttl失败时忽略不设置lru cache即可
   183  			// 因此忽略错误
   184  			ttl, _ := l2.slowCache.TTL(ctx, key)
   185  			if ttl != 0 {
   186  				l2.ttlCache.Add(key, buf, ttl)
   187  			}
   188  		}
   189  	}
   190  	return buf, nil
   191  }
   192  
   193  // GetBytes gets data from lur cache first, if not exists,
   194  // then gets the data from slow cache.
   195  func (l2 *L2Cache) GetBytes(ctx context.Context, key string) ([]byte, error) {
   196  	// 由公有函数来生成key,避免私有调用生成时如果循环调用多次添加prefix
   197  	key, err := l2.getKey(key)
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  	return l2.getBytes(ctx, key)
   202  }
   203  
   204  // setBytes sets data to lru cache and slow cache
   205  func (l2 *L2Cache) setBytes(ctx context.Context, key string, value []byte, ttl ...time.Duration) error {
   206  	t := l2.ttl
   207  	if len(ttl) != 0 && ttl[0] != 0 {
   208  		t = ttl[0]
   209  	}
   210  	// 先设置较慢的缓存
   211  	err := l2.slowCache.Set(ctx, key, value, t)
   212  	if err != nil {
   213  		return err
   214  	}
   215  	l2.ttlCache.Add(key, value, t)
   216  	return nil
   217  }
   218  
   219  // SetBytes sets data to lru cache and slow cache
   220  func (l2 *L2Cache) SetBytes(ctx context.Context, key string, value []byte, ttl ...time.Duration) error {
   221  	// 由公有函数来生成key,避免私有调用生成时如果循环调用多次添加prefix
   222  	key, err := l2.getKey(key)
   223  	if err != nil {
   224  		return err
   225  	}
   226  	return l2.setBytes(ctx, key, value, ttl...)
   227  }
   228  
   229  // Get gets data from lru cache first, if not exists,
   230  // then gets the data from slow cache.
   231  // Use unmarshal function coverts the data to result
   232  func (l2 *L2Cache) Get(ctx context.Context, key string, result interface{}) error {
   233  	return l2.get(ctx, key, result)
   234  }
   235  
   236  // Get gets data from lru cache first, if not exists,
   237  // then gets the data from slow cache.
   238  // Use unmarshal function coverts the data to result.
   239  // It will not return nil error.
   240  func (l2 *L2Cache) GetIgnoreNilErr(ctx context.Context, key string, result interface{}) error {
   241  	err := l2.get(ctx, key, result)
   242  	if err != nil && err == l2.nilErr {
   243  		err = nil
   244  	}
   245  	return err
   246  }
   247  
   248  func (l2 *L2Cache) get(ctx context.Context, key string, result interface{}) error {
   249  	// 由公有函数来生成key,避免私有调用生成时如果循环调用多次添加prefix
   250  	key, err := l2.getKey(key)
   251  	if err != nil {
   252  		return err
   253  	}
   254  	buf, err := l2.getBytes(ctx, key)
   255  	if err != nil {
   256  		return err
   257  	}
   258  
   259  	fn := l2.unmarshal
   260  	if fn == nil {
   261  		fn = json.Unmarshal
   262  	}
   263  	err = fn(buf, result)
   264  	if err != nil {
   265  		return err
   266  	}
   267  	return nil
   268  }
   269  
   270  // Set converts the value to bytes, then sets it to lru cache and slow cache
   271  func (l2 *L2Cache) Set(ctx context.Context, key string, value interface{}, ttl ...time.Duration) error {
   272  	key, err := l2.getKey(key)
   273  	if err != nil {
   274  		return err
   275  	}
   276  	fn := l2.marshal
   277  	if fn == nil {
   278  		fn = json.Marshal
   279  	}
   280  	buf, err := fn(value)
   281  	if err != nil {
   282  		return err
   283  	}
   284  	return l2.setBytes(ctx, key, buf, ttl...)
   285  }
   286  
   287  // Del deletes data from lru cache and slow cache
   288  func (l2 *L2Cache) Del(ctx context.Context, key string) (int64, error) {
   289  	key, err := l2.getKey(key)
   290  	if err != nil {
   291  		return 0, err
   292  	}
   293  	// 先清除ttl cache
   294  	l2.ttlCache.Remove(key)
   295  	return l2.slowCache.Del(ctx, key)
   296  }