go.mercari.io/datastore@v1.8.2/dsmiddleware/aememcache/aememcache.go (about)

     1  package aememcache
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/gob"
     7  	"time"
     8  
     9  	"go.mercari.io/datastore"
    10  	"go.mercari.io/datastore/dsmiddleware/storagecache"
    11  	"google.golang.org/appengine"
    12  	"google.golang.org/appengine/memcache"
    13  )
    14  
    15  var _ storagecache.Storage = &cacheHandler{}
    16  var _ datastore.Middleware = &cacheHandler{}
    17  
    18  // New AE Memcache middleware creates & returns.
    19  func New(opts ...CacheOption) interface {
    20  	datastore.Middleware
    21  	storagecache.Storage
    22  } {
    23  	ch := &cacheHandler{
    24  		stOpts: &storagecache.Options{},
    25  	}
    26  
    27  	for _, opt := range opts {
    28  		opt.Apply(ch)
    29  	}
    30  
    31  	s := storagecache.New(ch, ch.stOpts)
    32  	ch.Middleware = s
    33  
    34  	if ch.logf == nil {
    35  		ch.logf = func(ctx context.Context, format string, args ...interface{}) {}
    36  	}
    37  	if ch.cacheKey == nil {
    38  		ch.cacheKey = func(key datastore.Key) string {
    39  			return "mercari:aememcache:" + key.Encode()
    40  		}
    41  	}
    42  
    43  	return ch
    44  }
    45  
    46  type cacheHandler struct {
    47  	datastore.Middleware
    48  	stOpts *storagecache.Options
    49  
    50  	raiseMemcacheError bool
    51  	expireDuration     time.Duration
    52  	logf               func(ctx context.Context, format string, args ...interface{})
    53  	cacheKey           func(key datastore.Key) string
    54  }
    55  
    56  // A CacheOption is an cache option for a AE Memcache middleware.
    57  type CacheOption interface {
    58  	Apply(*cacheHandler)
    59  }
    60  
    61  func (ch *cacheHandler) SetMulti(ctx context.Context, cis []*storagecache.CacheItem) error {
    62  
    63  	ch.logf(ctx, "dsmiddleware/aememcache.SetMulti: incoming len=%d", len(cis))
    64  
    65  	itemList := make([]*memcache.Item, 0, len(cis))
    66  	for _, ci := range cis {
    67  		if ci.Key.Incomplete() {
    68  			panic("incomplete key incoming")
    69  		}
    70  		var buf bytes.Buffer
    71  		enc := gob.NewEncoder(&buf)
    72  		err := enc.Encode(ci.PropertyList)
    73  		if err != nil {
    74  			ch.logf(ctx, "dsmiddleware/aememcache.SetMulti: gob.Encode error key=%s err=%s", ci.Key.String(), err.Error())
    75  			continue
    76  		}
    77  		itemList = append(itemList, &memcache.Item{
    78  			Key:        ch.cacheKey(ci.Key),
    79  			Value:      buf.Bytes(),
    80  			Expiration: ch.expireDuration,
    81  		})
    82  	}
    83  
    84  	ch.logf(ctx, "dsmiddleware/aememcache.SetMulti: len=%d", len(itemList))
    85  
    86  	err := memcache.SetMulti(ctx, itemList)
    87  	if err != nil {
    88  		ch.logf(ctx, "dsmiddleware/aememcache: error on memcache.SetMulti %s", err.Error())
    89  		if ch.raiseMemcacheError {
    90  			if merr, ok := err.(appengine.MultiError); ok {
    91  				for _, err := range merr {
    92  					if err == nil || err == memcache.ErrCacheMiss {
    93  						continue
    94  					}
    95  					return merr
    96  				}
    97  			} else {
    98  				return err
    99  			}
   100  		}
   101  
   102  		keys := make([]string, 0, len(cis))
   103  		for _, ci := range cis {
   104  			keys = append(keys, ci.Key.Encode())
   105  		}
   106  		err = memcache.DeleteMulti(ctx, keys)
   107  		if err != nil {
   108  			ch.logf(ctx, "dsmiddleware/aememcache: error on memcache.DeleteMulti %s", err.Error())
   109  			if ch.raiseMemcacheError {
   110  				if merr, ok := err.(appengine.MultiError); ok {
   111  					for _, err := range merr {
   112  						if err == nil || err == memcache.ErrCacheMiss {
   113  							continue
   114  						}
   115  						return merr
   116  					}
   117  				} else {
   118  					return err
   119  				}
   120  			}
   121  		}
   122  	}
   123  
   124  	return nil
   125  }
   126  
   127  func (ch *cacheHandler) GetMulti(ctx context.Context, keys []datastore.Key) ([]*storagecache.CacheItem, error) {
   128  
   129  	ch.logf(ctx, "dsmiddleware/aememcache.GetMulti: incoming len=%d", len(keys))
   130  
   131  	resultList := make([]*storagecache.CacheItem, len(keys))
   132  
   133  	cacheKeys := make([]string, 0, len(keys))
   134  	for _, key := range keys {
   135  		cacheKeys = append(cacheKeys, ch.cacheKey(key))
   136  	}
   137  
   138  	itemMap, err := memcache.GetMulti(ctx, cacheKeys)
   139  
   140  	if err != nil {
   141  		ch.logf(ctx, "dsmiddleware/aememcache: error on memcache.GetMulti %s", err.Error())
   142  		if ch.raiseMemcacheError {
   143  			if merr, ok := err.(appengine.MultiError); ok {
   144  				for _, err := range merr {
   145  					if err == nil || err == memcache.ErrCacheMiss {
   146  						continue
   147  					}
   148  					return nil, datastore.MultiError(merr)
   149  				}
   150  			} else {
   151  				return nil, err
   152  			}
   153  		}
   154  		return resultList, nil
   155  	}
   156  
   157  	hit, miss := 0, 0
   158  	for idx, key := range keys {
   159  		item, ok := itemMap[ch.cacheKey(key)]
   160  		if !ok {
   161  			resultList[idx] = nil
   162  			miss++
   163  			continue
   164  		}
   165  		buf := bytes.NewBuffer(item.Value)
   166  		dec := gob.NewDecoder(buf)
   167  		var ps datastore.PropertyList
   168  		err = dec.Decode(&ps)
   169  		if err != nil {
   170  			resultList[idx] = nil
   171  			ch.logf(ctx, "dsmiddleware/aememcache.GetMulti: gob.Decode error key=%s err=%s", key.String(), err.Error())
   172  			miss++
   173  			continue
   174  		}
   175  
   176  		resultList[idx] = &storagecache.CacheItem{
   177  			Key:          key,
   178  			PropertyList: ps,
   179  		}
   180  		hit++
   181  	}
   182  
   183  	ch.logf(ctx, "dsmiddleware/aememcache.GetMulti: hit=%d miss=%d", hit, miss)
   184  
   185  	return resultList, nil
   186  }
   187  
   188  func (ch *cacheHandler) DeleteMulti(ctx context.Context, keys []datastore.Key) error {
   189  	ch.logf(ctx, "dsmiddleware/aememcache.DeleteMulti: incoming len=%d", len(keys))
   190  
   191  	cacheKeys := make([]string, 0, len(keys))
   192  	for _, key := range keys {
   193  		cacheKeys = append(cacheKeys, ch.cacheKey(key))
   194  	}
   195  
   196  	err := memcache.DeleteMulti(ctx, cacheKeys)
   197  	if err != nil {
   198  		ch.logf(ctx, "dsmiddleware/aememcache: error on memcache.DeleteMulti %s", err.Error())
   199  		if ch.raiseMemcacheError {
   200  			if merr, ok := err.(appengine.MultiError); ok {
   201  				for _, err := range merr {
   202  					if err == nil || err == memcache.ErrCacheMiss {
   203  						continue
   204  					}
   205  					return merr
   206  				}
   207  			} else {
   208  				return err
   209  			}
   210  		}
   211  	}
   212  
   213  	return nil
   214  }