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

     1  package dsmemcache
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/MakeNowJust/heredoc/v2"
    11  	"github.com/bradfitz/gomemcache/memcache"
    12  	"go.mercari.io/datastore"
    13  	"go.mercari.io/datastore/dsmiddleware/dslog"
    14  	"go.mercari.io/datastore/dsmiddleware/storagecache"
    15  	"go.mercari.io/datastore/internal/testutils"
    16  )
    17  
    18  func inCache(ctx context.Context, ch storagecache.Storage, key datastore.Key) (bool, error) {
    19  	resp, err := ch.GetMulti(ctx, []datastore.Key{key})
    20  	if err != nil {
    21  		return false, err
    22  	} else if v := len(resp); v != 1 {
    23  		return false, nil
    24  	} else if v := resp[0]; v == nil {
    25  		return false, nil
    26  	}
    27  
    28  	return true, nil
    29  }
    30  
    31  func TestMemcache_Basic(t *testing.T) {
    32  	ctx, client, cleanUp := testutils.SetupCloudDatastore(t)
    33  	defer cleanUp()
    34  
    35  	var logs []string
    36  	logf := func(ctx context.Context, format string, args ...interface{}) {
    37  		t.Logf(format, args...)
    38  		logs = append(logs, fmt.Sprintf(format, args...))
    39  	}
    40  
    41  	// setup. strategies are first in - first apply.
    42  
    43  	bLog := dslog.NewLogger("before: ", logf)
    44  	client.AppendMiddleware(bLog)
    45  	defer func() {
    46  		// stop logging before cleanUp func called.
    47  		client.RemoveMiddleware(bLog)
    48  	}()
    49  
    50  	memcacheClient := memcache.New(os.Getenv("MEMCACHE_ADDR"))
    51  	ch := New(
    52  		memcacheClient,
    53  		WithLogger(logf),
    54  	)
    55  	client.AppendMiddleware(ch)
    56  	defer func() {
    57  		if err := memcacheClient.FlushAll(); err != nil {
    58  			t.Fatal(err)
    59  		}
    60  		// stop logging before cleanUp func called.
    61  		client.RemoveMiddleware(ch)
    62  	}()
    63  
    64  	aLog := dslog.NewLogger("after: ", logf)
    65  	client.AppendMiddleware(aLog)
    66  	defer func() {
    67  		// stop logging before cleanUp func called.
    68  		client.RemoveMiddleware(aLog)
    69  	}()
    70  
    71  	// exec.
    72  
    73  	type Data struct {
    74  		Name string
    75  	}
    76  
    77  	// Put. add to cache.
    78  	key := client.IDKey("Data", 111, nil)
    79  	objBefore := &Data{Name: "Data"}
    80  	if _, err := client.Put(ctx, key, objBefore); err != nil {
    81  		t.Fatal(err)
    82  	}
    83  
    84  	hit, err := inCache(ctx, ch, key)
    85  	if err != nil {
    86  		t.Fatal(err)
    87  	} else if v := hit; !v {
    88  		t.Fatalf("unexpected: %v", v)
    89  	}
    90  
    91  	// Get. from cache.
    92  	objAfter := &Data{}
    93  	err = client.Get(ctx, key, objAfter)
    94  	if err != nil {
    95  		t.Fatal(err)
    96  	}
    97  
    98  	// Delete.
    99  	err = client.Delete(ctx, key)
   100  	if err != nil {
   101  		t.Fatal(err)
   102  	}
   103  
   104  	hit, err = inCache(ctx, ch, key)
   105  	if err != nil {
   106  		t.Fatal(err)
   107  	} else if v := hit; v {
   108  		t.Fatalf("unexpected: %v", v)
   109  	}
   110  
   111  	expected := heredoc.Doc(`
   112  		before: PutMultiWithoutTx #1, len(keys)=1, keys=[/Data,111]
   113  		after: PutMultiWithoutTx #1, len(keys)=1, keys=[/Data,111]
   114  		after: PutMultiWithoutTx #1, keys=[/Data,111]
   115  		dsmiddleware/dsmemcache.SetMulti: incoming len=1
   116  		before: PutMultiWithoutTx #1, keys=[/Data,111]
   117  		dsmiddleware/dsmemcache.GetMulti: incoming len=1
   118  		dsmiddleware/dsmemcache.GetMulti: hit=1 miss=0
   119  		before: GetMultiWithoutTx #2, len(keys)=1, keys=[/Data,111]
   120  		dsmiddleware/dsmemcache.GetMulti: incoming len=1
   121  		dsmiddleware/dsmemcache.GetMulti: hit=1 miss=0
   122  		before: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/Data,111]
   123  		after: DeleteMultiWithoutTx #2, len(keys)=1, keys=[/Data,111]
   124  		dsmiddleware/dsmemcache.DeleteMulti: incoming len=1
   125  		dsmiddleware/dsmemcache.GetMulti: incoming len=1
   126  		dsmiddleware/dsmemcache.GetMulti: hit=0 miss=1
   127  	`)
   128  
   129  	if v := strings.Join(logs, "\n") + "\n"; v != expected {
   130  		t.Errorf("unexpected: %v", v)
   131  	}
   132  }
   133  
   134  func TestMemcache_BasicWithoutExpire(t *testing.T) {
   135  	ctx, client, cleanUp := testutils.SetupCloudDatastore(t)
   136  	defer cleanUp()
   137  
   138  	var logs []string
   139  	logf := func(ctx context.Context, format string, args ...interface{}) {
   140  		t.Logf(format, args...)
   141  		logs = append(logs, fmt.Sprintf(format, args...))
   142  	}
   143  
   144  	// setup. strategies are first in - first apply.
   145  
   146  	bLog := dslog.NewLogger("before: ", logf)
   147  	client.AppendMiddleware(bLog)
   148  	defer func() {
   149  		// stop logging before cleanUp func called.
   150  		client.RemoveMiddleware(bLog)
   151  	}()
   152  
   153  	memcacheClient := memcache.New(os.Getenv("MEMCACHE_ADDR"))
   154  	ch := New(
   155  		memcacheClient,
   156  		WithExpireDuration(0),
   157  		WithLogger(logf),
   158  	)
   159  	client.AppendMiddleware(ch)
   160  	defer func() {
   161  		if err := memcacheClient.FlushAll(); err != nil {
   162  			t.Fatal(err)
   163  		}
   164  		// stop logging before cleanUp func called.
   165  		client.RemoveMiddleware(ch)
   166  	}()
   167  
   168  	aLog := dslog.NewLogger("after: ", logf)
   169  	client.AppendMiddleware(aLog)
   170  	defer func() {
   171  		// stop logging before cleanUp func called.
   172  		client.RemoveMiddleware(aLog)
   173  	}()
   174  
   175  	// exec.
   176  
   177  	type Data struct {
   178  		Name string
   179  	}
   180  
   181  	// Put. add to cache.
   182  	key := client.IDKey("Data", 111, nil)
   183  	objBefore := &Data{Name: "Data"}
   184  	if _, err := client.Put(ctx, key, objBefore); err != nil {
   185  		t.Fatal(err)
   186  	}
   187  
   188  	hit, err := inCache(ctx, ch, key)
   189  	if err != nil {
   190  		t.Fatal(err)
   191  	} else if v := hit; !v {
   192  		t.Fatalf("unexpected: %v", v)
   193  	}
   194  
   195  	// Get. from cache.
   196  	objAfter := &Data{}
   197  	err = client.Get(ctx, key, objAfter)
   198  	if err != nil {
   199  		t.Fatal(err)
   200  	}
   201  
   202  	// Delete.
   203  	err = client.Delete(ctx, key)
   204  	if err != nil {
   205  		t.Fatal(err)
   206  	}
   207  
   208  	hit, err = inCache(ctx, ch, key)
   209  	if err != nil {
   210  		t.Fatal(err)
   211  	} else if v := hit; v {
   212  		t.Fatalf("unexpected: %v", v)
   213  	}
   214  
   215  	expected := heredoc.Doc(`
   216  		before: PutMultiWithoutTx #1, len(keys)=1, keys=[/Data,111]
   217  		after: PutMultiWithoutTx #1, len(keys)=1, keys=[/Data,111]
   218  		after: PutMultiWithoutTx #1, keys=[/Data,111]
   219  		dsmiddleware/dsmemcache.SetMulti: incoming len=1
   220  		before: PutMultiWithoutTx #1, keys=[/Data,111]
   221  		dsmiddleware/dsmemcache.GetMulti: incoming len=1
   222  		dsmiddleware/dsmemcache.GetMulti: hit=1 miss=0
   223  		before: GetMultiWithoutTx #2, len(keys)=1, keys=[/Data,111]
   224  		dsmiddleware/dsmemcache.GetMulti: incoming len=1
   225  		dsmiddleware/dsmemcache.GetMulti: hit=1 miss=0
   226  		before: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/Data,111]
   227  		after: DeleteMultiWithoutTx #2, len(keys)=1, keys=[/Data,111]
   228  		dsmiddleware/dsmemcache.DeleteMulti: incoming len=1
   229  		dsmiddleware/dsmemcache.GetMulti: incoming len=1
   230  		dsmiddleware/dsmemcache.GetMulti: hit=0 miss=1
   231  	`)
   232  
   233  	if v := strings.Join(logs, "\n") + "\n"; v != expected {
   234  		t.Errorf("unexpected: %v", v)
   235  	}
   236  }
   237  
   238  func TestMemcache_MultiError(t *testing.T) {
   239  	ctx, client, cleanUp := testutils.SetupCloudDatastore(t)
   240  	defer cleanUp()
   241  
   242  	var logs []string
   243  	logf := func(ctx context.Context, format string, args ...interface{}) {
   244  		t.Logf(format, args...)
   245  		logs = append(logs, fmt.Sprintf(format, args...))
   246  	}
   247  
   248  	// setup. strategies are first in - first apply.
   249  	bLog := dslog.NewLogger("before: ", logf)
   250  	client.AppendMiddleware(bLog)
   251  	defer func() {
   252  		// stop logging before cleanUp func called.
   253  		client.RemoveMiddleware(bLog)
   254  	}()
   255  
   256  	memcacheClient := memcache.New(os.Getenv("MEMCACHE_ADDR"))
   257  	ch := New(
   258  		memcacheClient,
   259  		WithLogger(logf),
   260  	)
   261  	client.AppendMiddleware(ch)
   262  	defer func() {
   263  		if err := memcacheClient.FlushAll(); err != nil {
   264  			t.Fatal(err)
   265  		}
   266  		// stop logging before cleanUp func called.
   267  		client.RemoveMiddleware(ch)
   268  	}()
   269  
   270  	aLog := dslog.NewLogger("after: ", logf)
   271  	client.AppendMiddleware(aLog)
   272  	defer func() {
   273  		// stop logging before cleanUp func called.
   274  		client.RemoveMiddleware(aLog)
   275  	}()
   276  
   277  	// exec.
   278  
   279  	type Data struct {
   280  		Name string
   281  	}
   282  
   283  	const size = 10
   284  
   285  	keys := make([]datastore.Key, 0, size)
   286  	list := make([]*Data, 0, size)
   287  	for i := 1; i <= size; i++ {
   288  		keys = append(keys, client.IDKey("Data", int64(i), nil))
   289  		list = append(list, &Data{
   290  			Name: fmt.Sprintf("#%d", i),
   291  		})
   292  	}
   293  
   294  	_, err := client.PutMulti(ctx, keys, list)
   295  	if err != nil {
   296  		t.Fatal(err)
   297  	}
   298  
   299  	for _, key := range keys {
   300  		if key.ID()%2 == 0 {
   301  			// delete cache id=2, 4, 6, 8, 10
   302  			err := ch.DeleteMulti(ctx, []datastore.Key{key})
   303  			if err != nil {
   304  				t.Fatal(err)
   305  			}
   306  		}
   307  		if key.ID()%3 == 0 {
   308  			client.RemoveMiddleware(ch)
   309  			err := client.Delete(ctx, key)
   310  			if err != nil {
   311  				t.Fatal(err)
   312  			}
   313  
   314  			client.RemoveMiddleware(aLog)
   315  			client.AppendMiddleware(ch)
   316  			client.AppendMiddleware(aLog)
   317  		}
   318  	}
   319  
   320  	list = make([]*Data, size)
   321  	err = client.GetMulti(ctx, keys, list)
   322  	merr, ok := err.(datastore.MultiError)
   323  	if !ok {
   324  		t.Fatal(err)
   325  	}
   326  
   327  	if v := len(merr); v != size {
   328  		t.Fatalf("unexpected: %v", v)
   329  	}
   330  	for idx, err := range merr {
   331  		key := keys[idx]
   332  		if key.ID()%2 == 0 && key.ID()%3 == 0 {
   333  			// not exists on cache & datastore both
   334  			if err != datastore.ErrNoSuchEntity {
   335  				t.Error(err)
   336  			}
   337  		} else {
   338  			if v := list[idx].Name; v != fmt.Sprintf("#%d", idx+1) {
   339  				t.Errorf("unexpected: %v", v)
   340  			}
   341  		}
   342  	}
   343  
   344  	expected := heredoc.Doc(`
   345  		before: PutMultiWithoutTx #1, len(keys)=10, keys=[/Data,1, /Data,2, /Data,3, /Data,4, /Data,5, /Data,6, /Data,7, /Data,8, /Data,9, /Data,10]
   346  		after: PutMultiWithoutTx #1, len(keys)=10, keys=[/Data,1, /Data,2, /Data,3, /Data,4, /Data,5, /Data,6, /Data,7, /Data,8, /Data,9, /Data,10]
   347  		after: PutMultiWithoutTx #1, keys=[/Data,1, /Data,2, /Data,3, /Data,4, /Data,5, /Data,6, /Data,7, /Data,8, /Data,9, /Data,10]
   348  		dsmiddleware/dsmemcache.SetMulti: incoming len=10
   349  		before: PutMultiWithoutTx #1, keys=[/Data,1, /Data,2, /Data,3, /Data,4, /Data,5, /Data,6, /Data,7, /Data,8, /Data,9, /Data,10]
   350  		dsmiddleware/dsmemcache.DeleteMulti: incoming len=1
   351  		before: DeleteMultiWithoutTx #2, len(keys)=1, keys=[/Data,3]
   352  		after: DeleteMultiWithoutTx #2, len(keys)=1, keys=[/Data,3]
   353  		dsmiddleware/dsmemcache.DeleteMulti: incoming len=1
   354  		dsmiddleware/dsmemcache.DeleteMulti: incoming len=1
   355  		before: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/Data,6]
   356  		after: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/Data,6]
   357  		dsmiddleware/dsmemcache.DeleteMulti: incoming len=1
   358  		before: DeleteMultiWithoutTx #4, len(keys)=1, keys=[/Data,9]
   359  		after: DeleteMultiWithoutTx #4, len(keys)=1, keys=[/Data,9]
   360  		dsmiddleware/dsmemcache.DeleteMulti: incoming len=1
   361  		before: GetMultiWithoutTx #5, len(keys)=10, keys=[/Data,1, /Data,2, /Data,3, /Data,4, /Data,5, /Data,6, /Data,7, /Data,8, /Data,9, /Data,10]
   362  		dsmiddleware/dsmemcache.GetMulti: incoming len=10
   363  		dsmiddleware/dsmemcache.GetMulti: hit=5 miss=5
   364  		after: GetMultiWithoutTx #5, len(keys)=5, keys=[/Data,2, /Data,4, /Data,6, /Data,8, /Data,10]
   365  		after: GetMultiWithoutTx #5, err=datastore: no such entity
   366  		dsmiddleware/dsmemcache.SetMulti: incoming len=4
   367  		before: GetMultiWithoutTx #5, err=datastore: no such entity
   368  	`)
   369  
   370  	if v := strings.Join(logs, "\n") + "\n"; v != expected {
   371  		t.Errorf("unexpected: %v", v)
   372  	}
   373  }