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

     1  package localcache
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"regexp"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/MakeNowJust/heredoc/v2"
    12  	"go.mercari.io/datastore"
    13  	"go.mercari.io/datastore/dsmiddleware/dslog"
    14  	"go.mercari.io/datastore/internal/testutils"
    15  	"google.golang.org/api/iterator"
    16  )
    17  
    18  func TestLocalCache_Basic(t *testing.T) {
    19  	ctx, client, cleanUp := testutils.SetupCloudDatastore(t)
    20  	defer cleanUp()
    21  
    22  	var logs []string
    23  	logf := func(ctx context.Context, format string, args ...interface{}) {
    24  		t.Logf(format, args...)
    25  		logs = append(logs, fmt.Sprintf(format, args...))
    26  	}
    27  
    28  	// setup. strategies are first in - first apply.
    29  
    30  	bLog := dslog.NewLogger("before: ", logf)
    31  	client.AppendMiddleware(bLog)
    32  	defer func() {
    33  		// stop logging before cleanUp func called.
    34  		client.RemoveMiddleware(bLog)
    35  	}()
    36  
    37  	ch := New()
    38  	client.AppendMiddleware(ch)
    39  	defer func() {
    40  		// stop logging before cleanUp func called.
    41  		client.RemoveMiddleware(ch)
    42  	}()
    43  
    44  	aLog := dslog.NewLogger("after: ", logf)
    45  	client.AppendMiddleware(aLog)
    46  	defer func() {
    47  		// stop logging before cleanUp func called.
    48  		client.RemoveMiddleware(aLog)
    49  	}()
    50  
    51  	// exec.
    52  
    53  	type Data struct {
    54  		Name string
    55  	}
    56  
    57  	// Put. add to cache.
    58  	key := client.IDKey("Data", 111, nil)
    59  	objBefore := &Data{Name: "Data"}
    60  	_, err := client.Put(ctx, key, objBefore)
    61  	if err != nil {
    62  		t.Fatal(err)
    63  	}
    64  
    65  	if v := ch.HasCache(key); !v {
    66  		t.Fatalf("unexpected: %v", v)
    67  	}
    68  
    69  	// Get. from cache.
    70  	objAfter := &Data{}
    71  	err = client.Get(ctx, key, objAfter)
    72  	if err != nil {
    73  		t.Fatal(err)
    74  	}
    75  
    76  	// Delete.
    77  	err = client.Delete(ctx, key)
    78  	if err != nil {
    79  		t.Fatal(err)
    80  	}
    81  
    82  	if v := ch.HasCache(key); v {
    83  		t.Fatalf("unexpected: %v", v)
    84  	}
    85  
    86  	expected := heredoc.Doc(`
    87  		before: PutMultiWithoutTx #1, len(keys)=1, keys=[/Data,111]
    88  		after: PutMultiWithoutTx #1, len(keys)=1, keys=[/Data,111]
    89  		after: PutMultiWithoutTx #1, keys=[/Data,111]
    90  		before: PutMultiWithoutTx #1, keys=[/Data,111]
    91  		before: GetMultiWithoutTx #2, len(keys)=1, keys=[/Data,111]
    92  		before: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/Data,111]
    93  		after: DeleteMultiWithoutTx #2, len(keys)=1, keys=[/Data,111]
    94  	`)
    95  
    96  	if v := strings.Join(logs, "\n") + "\n"; v != expected {
    97  		t.Errorf("unexpected: %v", v)
    98  	}
    99  }
   100  
   101  func TestLocalCache_WithIncludeKinds(t *testing.T) {
   102  	ctx, client, cleanUp := testutils.SetupCloudDatastore(t)
   103  	defer cleanUp()
   104  
   105  	var logs []string
   106  	logf := func(ctx context.Context, format string, args ...interface{}) {
   107  		t.Logf(format, args...)
   108  		logs = append(logs, fmt.Sprintf(format, args...))
   109  	}
   110  
   111  	// setup. strategies are first in - first apply.
   112  
   113  	bLog := dslog.NewLogger("before: ", logf)
   114  	client.AppendMiddleware(bLog)
   115  	defer func() {
   116  		// stop logging before cleanUp func called.
   117  		client.RemoveMiddleware(bLog)
   118  	}()
   119  
   120  	ch := New(
   121  		WithLogger(logf),
   122  		WithIncludeKinds("DataA"),
   123  	)
   124  	client.AppendMiddleware(ch)
   125  	defer func() {
   126  		// stop logging before cleanUp func called.
   127  		client.RemoveMiddleware(ch)
   128  	}()
   129  
   130  	aLog := dslog.NewLogger("after: ", logf)
   131  	client.AppendMiddleware(aLog)
   132  	defer func() {
   133  		// stop logging before cleanUp func called.
   134  		client.RemoveMiddleware(aLog)
   135  	}()
   136  
   137  	// exec.
   138  
   139  	type Data struct {
   140  		Name string
   141  	}
   142  
   143  	{ // Put. cache target.
   144  		key := client.IDKey("DataA", 111, nil)
   145  		objBefore := &Data{Name: "A"}
   146  		_, err := client.Put(ctx, key, objBefore)
   147  		if err != nil {
   148  			t.Fatal(err)
   149  		}
   150  
   151  		obj := &Data{}
   152  		err = client.Get(ctx, key, obj)
   153  		if err != nil {
   154  			t.Fatal(err)
   155  		}
   156  		if v := obj.Name; v != "A" {
   157  			t.Errorf("unexpected: %v", v)
   158  		}
   159  
   160  		err = client.Delete(ctx, key)
   161  		if err != nil {
   162  			t.Fatal(err)
   163  		}
   164  	}
   165  	{ // Put. cache ignored.
   166  		key := client.IDKey("DataB", 111, nil)
   167  		objBefore := &Data{Name: "B"}
   168  		_, err := client.Put(ctx, key, objBefore)
   169  		if err != nil {
   170  			t.Fatal(err)
   171  		}
   172  
   173  		obj := &Data{}
   174  		err = client.Get(ctx, key, obj)
   175  		if err != nil {
   176  			t.Fatal(err)
   177  		}
   178  		if v := obj.Name; v != "B" {
   179  			t.Errorf("unexpected: %v", v)
   180  		}
   181  
   182  		err = client.Delete(ctx, key)
   183  		if err != nil {
   184  			t.Fatal(err)
   185  		}
   186  	}
   187  	{ // Put. cache target & ignored.
   188  		keyInc := client.IDKey("DataA", 111, nil)
   189  		keyExc := client.IDKey("DataB", 111, nil)
   190  
   191  		list := []*Data{{Name: "A"}, {Name: "B"}}
   192  		_, err := client.PutMulti(ctx, []datastore.Key{keyInc, keyExc}, list)
   193  		if err != nil {
   194  			t.Fatal(err)
   195  		}
   196  
   197  		list = make([]*Data, 2)
   198  		err = client.GetMulti(ctx, []datastore.Key{keyInc, keyExc}, list)
   199  		if err != nil {
   200  			t.Fatal(err)
   201  		}
   202  		if v := len(list); v != 2 {
   203  			t.Fatalf("unexpected: %v", v)
   204  		}
   205  		if v := list[0].Name; v != "A" {
   206  			t.Errorf("unexpected: %v", v)
   207  		}
   208  		if v := list[1].Name; v != "B" {
   209  			t.Errorf("unexpected: %v", v)
   210  		}
   211  
   212  		err = client.DeleteMulti(ctx, []datastore.Key{keyInc, keyExc})
   213  		if err != nil {
   214  			t.Fatal(err)
   215  		}
   216  	}
   217  	{ // Put. partially hit
   218  		keyIncA := client.IDKey("DataA", 111, nil)
   219  		keyIncB := client.IDKey("DataA", 222, nil)
   220  		keyExcA := client.IDKey("DataB", 111, nil)
   221  		keyExcB := client.IDKey("DataB", 222, nil)
   222  
   223  		list := []*Data{{Name: "A1"}, {Name: "A2"}, {Name: "B1"}, {Name: "B2"}}
   224  		_, err := client.PutMulti(ctx, []datastore.Key{keyIncA, keyIncB, keyExcA, keyExcB}, list)
   225  		if err != nil {
   226  			t.Fatal(err)
   227  		}
   228  
   229  		ch.DeleteCache(ctx, keyIncB)
   230  		ch.DeleteCache(ctx, keyExcB)
   231  
   232  		list = make([]*Data, 4)
   233  		err = client.GetMulti(ctx, []datastore.Key{keyIncA, keyIncB, keyExcA, keyExcB}, list)
   234  		if err != nil {
   235  			t.Fatal(err)
   236  		}
   237  		if v := len(list); v != 4 {
   238  			t.Fatalf("unexpected: %v", v)
   239  		}
   240  		if v := list[0].Name; v != "A1" {
   241  			t.Errorf("unexpected: %v", v)
   242  		}
   243  		if v := list[1].Name; v != "A2" {
   244  			t.Errorf("unexpected: %v", v)
   245  		}
   246  		if v := list[2].Name; v != "B1" {
   247  			t.Errorf("unexpected: %v", v)
   248  		}
   249  		if v := list[3].Name; v != "B2" {
   250  			t.Errorf("unexpected: %v", v)
   251  		}
   252  
   253  		err = client.DeleteMulti(ctx, []datastore.Key{keyIncA, keyIncB, keyExcA, keyExcB})
   254  		if err != nil {
   255  			t.Fatal(err)
   256  		}
   257  	}
   258  
   259  	expected := heredoc.Doc(`
   260  		before: PutMultiWithoutTx #1, len(keys)=1, keys=[/DataA,111]
   261  		after: PutMultiWithoutTx #1, len(keys)=1, keys=[/DataA,111]
   262  		after: PutMultiWithoutTx #1, keys=[/DataA,111]
   263  		dsmiddleware/localcache.SetMulti: len=1
   264  		dsmiddleware/localcache.SetMulti: idx=0 key=/DataA,111 len(ps)=1
   265  		before: PutMultiWithoutTx #1, keys=[/DataA,111]
   266  		before: GetMultiWithoutTx #2, len(keys)=1, keys=[/DataA,111]
   267  		dsmiddleware/localcache.GetMulti: len=1
   268  		dsmiddleware/localcache.GetMulti: idx=0 key=/DataA,111
   269  		dsmiddleware/localcache.GetMulti: idx=0, hit key=/DataA,111 len(ps)=1
   270  		before: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/DataA,111]
   271  		after: DeleteMultiWithoutTx #2, len(keys)=1, keys=[/DataA,111]
   272  		dsmiddleware/localcache.DeleteMulti: len=1
   273  		dsmiddleware/localcache.DeleteMulti: idx=0 key=/DataA,111
   274  		before: PutMultiWithoutTx #4, len(keys)=1, keys=[/DataB,111]
   275  		after: PutMultiWithoutTx #3, len(keys)=1, keys=[/DataB,111]
   276  		after: PutMultiWithoutTx #3, keys=[/DataB,111]
   277  		before: PutMultiWithoutTx #4, keys=[/DataB,111]
   278  		before: GetMultiWithoutTx #5, len(keys)=1, keys=[/DataB,111]
   279  		after: GetMultiWithoutTx #4, len(keys)=1, keys=[/DataB,111]
   280  		before: DeleteMultiWithoutTx #6, len(keys)=1, keys=[/DataB,111]
   281  		after: DeleteMultiWithoutTx #5, len(keys)=1, keys=[/DataB,111]
   282  		before: PutMultiWithoutTx #7, len(keys)=2, keys=[/DataA,111, /DataB,111]
   283  		after: PutMultiWithoutTx #6, len(keys)=2, keys=[/DataA,111, /DataB,111]
   284  		after: PutMultiWithoutTx #6, keys=[/DataA,111, /DataB,111]
   285  		dsmiddleware/localcache.SetMulti: len=1
   286  		dsmiddleware/localcache.SetMulti: idx=0 key=/DataA,111 len(ps)=1
   287  		before: PutMultiWithoutTx #7, keys=[/DataA,111, /DataB,111]
   288  		before: GetMultiWithoutTx #8, len(keys)=2, keys=[/DataA,111, /DataB,111]
   289  		dsmiddleware/localcache.GetMulti: len=1
   290  		dsmiddleware/localcache.GetMulti: idx=0 key=/DataA,111
   291  		dsmiddleware/localcache.GetMulti: idx=0, hit key=/DataA,111 len(ps)=1
   292  		after: GetMultiWithoutTx #7, len(keys)=1, keys=[/DataB,111]
   293  		before: DeleteMultiWithoutTx #9, len(keys)=2, keys=[/DataA,111, /DataB,111]
   294  		after: DeleteMultiWithoutTx #8, len(keys)=2, keys=[/DataA,111, /DataB,111]
   295  		dsmiddleware/localcache.DeleteMulti: len=1
   296  		dsmiddleware/localcache.DeleteMulti: idx=0 key=/DataA,111
   297  		before: PutMultiWithoutTx #10, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   298  		after: PutMultiWithoutTx #9, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   299  		after: PutMultiWithoutTx #9, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   300  		dsmiddleware/localcache.SetMulti: len=2
   301  		dsmiddleware/localcache.SetMulti: idx=0 key=/DataA,111 len(ps)=1
   302  		dsmiddleware/localcache.SetMulti: idx=1 key=/DataA,222 len(ps)=1
   303  		before: PutMultiWithoutTx #10, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   304  		dsmiddleware/localcache.DeleteCache: key=/DataA,222
   305  		dsmiddleware/localcache.DeleteCache: key=/DataB,222
   306  		before: GetMultiWithoutTx #11, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   307  		dsmiddleware/localcache.GetMulti: len=2
   308  		dsmiddleware/localcache.GetMulti: idx=0 key=/DataA,111
   309  		dsmiddleware/localcache.GetMulti: idx=1 key=/DataA,222
   310  		dsmiddleware/localcache.GetMulti: idx=0, hit key=/DataA,111 len(ps)=1
   311  		dsmiddleware/localcache.GetMulti: idx=1, missed key=/DataA,222
   312  		after: GetMultiWithoutTx #10, len(keys)=3, keys=[/DataA,222, /DataB,111, /DataB,222]
   313  		dsmiddleware/localcache.SetMulti: len=1
   314  		dsmiddleware/localcache.SetMulti: idx=0 key=/DataA,222 len(ps)=1
   315  		before: DeleteMultiWithoutTx #12, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   316  		after: DeleteMultiWithoutTx #11, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   317  		dsmiddleware/localcache.DeleteMulti: len=2
   318  		dsmiddleware/localcache.DeleteMulti: idx=0 key=/DataA,111
   319  		dsmiddleware/localcache.DeleteMulti: idx=1 key=/DataA,222
   320  	`)
   321  
   322  	if v := strings.Join(logs, "\n") + "\n"; v != expected {
   323  		t.Errorf("unexpected: %v", v)
   324  	}
   325  }
   326  
   327  func TestLocalCache_WithExcludeKinds(t *testing.T) {
   328  	ctx, client, cleanUp := testutils.SetupCloudDatastore(t)
   329  	defer cleanUp()
   330  
   331  	var logs []string
   332  	logf := func(ctx context.Context, format string, args ...interface{}) {
   333  		t.Logf(format, args...)
   334  		logs = append(logs, fmt.Sprintf(format, args...))
   335  	}
   336  
   337  	// setup. strategies are first in - first apply.
   338  
   339  	bLog := dslog.NewLogger("before: ", logf)
   340  	client.AppendMiddleware(bLog)
   341  	defer func() {
   342  		// stop logging before cleanUp func called.
   343  		client.RemoveMiddleware(bLog)
   344  	}()
   345  
   346  	ch := New(
   347  		WithLogger(logf),
   348  		WithExcludeKinds("DataA"),
   349  	)
   350  	client.AppendMiddleware(ch)
   351  	defer func() {
   352  		// stop logging before cleanUp func called.
   353  		client.RemoveMiddleware(ch)
   354  	}()
   355  
   356  	aLog := dslog.NewLogger("after: ", logf)
   357  	client.AppendMiddleware(aLog)
   358  	defer func() {
   359  		// stop logging before cleanUp func called.
   360  		client.RemoveMiddleware(aLog)
   361  	}()
   362  
   363  	// exec.
   364  
   365  	type Data struct {
   366  		Name string
   367  	}
   368  
   369  	{ // Put. cache target.
   370  		key := client.IDKey("DataA", 111, nil)
   371  		objBefore := &Data{Name: "A"}
   372  		_, err := client.Put(ctx, key, objBefore)
   373  		if err != nil {
   374  			t.Fatal(err)
   375  		}
   376  
   377  		obj := &Data{}
   378  		err = client.Get(ctx, key, obj)
   379  		if err != nil {
   380  			t.Fatal(err)
   381  		}
   382  		if v := obj.Name; v != "A" {
   383  			t.Errorf("unexpected: %v", v)
   384  		}
   385  
   386  		err = client.Delete(ctx, key)
   387  		if err != nil {
   388  			t.Fatal(err)
   389  		}
   390  	}
   391  	{ // Put. cache ignored.
   392  		key := client.IDKey("DataB", 111, nil)
   393  		objBefore := &Data{Name: "B"}
   394  		_, err := client.Put(ctx, key, objBefore)
   395  		if err != nil {
   396  			t.Fatal(err)
   397  		}
   398  
   399  		obj := &Data{}
   400  		err = client.Get(ctx, key, obj)
   401  		if err != nil {
   402  			t.Fatal(err)
   403  		}
   404  		if v := obj.Name; v != "B" {
   405  			t.Errorf("unexpected: %v", v)
   406  		}
   407  
   408  		err = client.Delete(ctx, key)
   409  		if err != nil {
   410  			t.Fatal(err)
   411  		}
   412  	}
   413  	{ // Put. cache target & ignored.
   414  		keyInc := client.IDKey("DataA", 111, nil)
   415  		keyExc := client.IDKey("DataB", 111, nil)
   416  
   417  		list := []*Data{{Name: "A"}, {Name: "B"}}
   418  		_, err := client.PutMulti(ctx, []datastore.Key{keyInc, keyExc}, list)
   419  		if err != nil {
   420  			t.Fatal(err)
   421  		}
   422  
   423  		list = make([]*Data, 2)
   424  		err = client.GetMulti(ctx, []datastore.Key{keyInc, keyExc}, list)
   425  		if err != nil {
   426  			t.Fatal(err)
   427  		}
   428  		if v := len(list); v != 2 {
   429  			t.Fatalf("unexpected: %v", v)
   430  		}
   431  		if v := list[0].Name; v != "A" {
   432  			t.Errorf("unexpected: %v", v)
   433  		}
   434  		if v := list[1].Name; v != "B" {
   435  			t.Errorf("unexpected: %v", v)
   436  		}
   437  
   438  		err = client.DeleteMulti(ctx, []datastore.Key{keyInc, keyExc})
   439  		if err != nil {
   440  			t.Fatal(err)
   441  		}
   442  	}
   443  	{ // Put. partially hit
   444  		keyIncA := client.IDKey("DataA", 111, nil)
   445  		keyIncB := client.IDKey("DataA", 222, nil)
   446  		keyExcA := client.IDKey("DataB", 111, nil)
   447  		keyExcB := client.IDKey("DataB", 222, nil)
   448  
   449  		list := []*Data{{Name: "A1"}, {Name: "A2"}, {Name: "B1"}, {Name: "B2"}}
   450  		_, err := client.PutMulti(ctx, []datastore.Key{keyIncA, keyIncB, keyExcA, keyExcB}, list)
   451  		if err != nil {
   452  			t.Fatal(err)
   453  		}
   454  
   455  		ch.DeleteCache(ctx, keyIncB)
   456  		ch.DeleteCache(ctx, keyExcB)
   457  
   458  		list = make([]*Data, 4)
   459  		err = client.GetMulti(ctx, []datastore.Key{keyIncA, keyIncB, keyExcA, keyExcB}, list)
   460  		if err != nil {
   461  			t.Fatal(err)
   462  		}
   463  		if v := len(list); v != 4 {
   464  			t.Fatalf("unexpected: %v", v)
   465  		}
   466  		if v := list[0].Name; v != "A1" {
   467  			t.Errorf("unexpected: %v", v)
   468  		}
   469  		if v := list[1].Name; v != "A2" {
   470  			t.Errorf("unexpected: %v", v)
   471  		}
   472  		if v := list[2].Name; v != "B1" {
   473  			t.Errorf("unexpected: %v", v)
   474  		}
   475  		if v := list[3].Name; v != "B2" {
   476  			t.Errorf("unexpected: %v", v)
   477  		}
   478  
   479  		err = client.DeleteMulti(ctx, []datastore.Key{keyIncA, keyIncB, keyExcA, keyExcB})
   480  		if err != nil {
   481  			t.Fatal(err)
   482  		}
   483  	}
   484  
   485  	expected := heredoc.Doc(`
   486  		before: PutMultiWithoutTx #1, len(keys)=1, keys=[/DataA,111]
   487  		after: PutMultiWithoutTx #1, len(keys)=1, keys=[/DataA,111]
   488  		after: PutMultiWithoutTx #1, keys=[/DataA,111]
   489  		before: PutMultiWithoutTx #1, keys=[/DataA,111]
   490  		before: GetMultiWithoutTx #2, len(keys)=1, keys=[/DataA,111]
   491  		after: GetMultiWithoutTx #2, len(keys)=1, keys=[/DataA,111]
   492  		before: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/DataA,111]
   493  		after: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/DataA,111]
   494  		before: PutMultiWithoutTx #4, len(keys)=1, keys=[/DataB,111]
   495  		after: PutMultiWithoutTx #4, len(keys)=1, keys=[/DataB,111]
   496  		after: PutMultiWithoutTx #4, keys=[/DataB,111]
   497  		dsmiddleware/localcache.SetMulti: len=1
   498  		dsmiddleware/localcache.SetMulti: idx=0 key=/DataB,111 len(ps)=1
   499  		before: PutMultiWithoutTx #4, keys=[/DataB,111]
   500  		before: GetMultiWithoutTx #5, len(keys)=1, keys=[/DataB,111]
   501  		dsmiddleware/localcache.GetMulti: len=1
   502  		dsmiddleware/localcache.GetMulti: idx=0 key=/DataB,111
   503  		dsmiddleware/localcache.GetMulti: idx=0, hit key=/DataB,111 len(ps)=1
   504  		before: DeleteMultiWithoutTx #6, len(keys)=1, keys=[/DataB,111]
   505  		after: DeleteMultiWithoutTx #5, len(keys)=1, keys=[/DataB,111]
   506  		dsmiddleware/localcache.DeleteMulti: len=1
   507  		dsmiddleware/localcache.DeleteMulti: idx=0 key=/DataB,111
   508  		before: PutMultiWithoutTx #7, len(keys)=2, keys=[/DataA,111, /DataB,111]
   509  		after: PutMultiWithoutTx #6, len(keys)=2, keys=[/DataA,111, /DataB,111]
   510  		after: PutMultiWithoutTx #6, keys=[/DataA,111, /DataB,111]
   511  		dsmiddleware/localcache.SetMulti: len=1
   512  		dsmiddleware/localcache.SetMulti: idx=0 key=/DataB,111 len(ps)=1
   513  		before: PutMultiWithoutTx #7, keys=[/DataA,111, /DataB,111]
   514  		before: GetMultiWithoutTx #8, len(keys)=2, keys=[/DataA,111, /DataB,111]
   515  		dsmiddleware/localcache.GetMulti: len=1
   516  		dsmiddleware/localcache.GetMulti: idx=0 key=/DataB,111
   517  		dsmiddleware/localcache.GetMulti: idx=0, hit key=/DataB,111 len(ps)=1
   518  		after: GetMultiWithoutTx #7, len(keys)=1, keys=[/DataA,111]
   519  		before: DeleteMultiWithoutTx #9, len(keys)=2, keys=[/DataA,111, /DataB,111]
   520  		after: DeleteMultiWithoutTx #8, len(keys)=2, keys=[/DataA,111, /DataB,111]
   521  		dsmiddleware/localcache.DeleteMulti: len=1
   522  		dsmiddleware/localcache.DeleteMulti: idx=0 key=/DataB,111
   523  		before: PutMultiWithoutTx #10, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   524  		after: PutMultiWithoutTx #9, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   525  		after: PutMultiWithoutTx #9, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   526  		dsmiddleware/localcache.SetMulti: len=2
   527  		dsmiddleware/localcache.SetMulti: idx=0 key=/DataB,111 len(ps)=1
   528  		dsmiddleware/localcache.SetMulti: idx=1 key=/DataB,222 len(ps)=1
   529  		before: PutMultiWithoutTx #10, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   530  		dsmiddleware/localcache.DeleteCache: key=/DataA,222
   531  		dsmiddleware/localcache.DeleteCache: key=/DataB,222
   532  		before: GetMultiWithoutTx #11, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   533  		dsmiddleware/localcache.GetMulti: len=2
   534  		dsmiddleware/localcache.GetMulti: idx=0 key=/DataB,111
   535  		dsmiddleware/localcache.GetMulti: idx=1 key=/DataB,222
   536  		dsmiddleware/localcache.GetMulti: idx=0, hit key=/DataB,111 len(ps)=1
   537  		dsmiddleware/localcache.GetMulti: idx=1, missed key=/DataB,222
   538  		after: GetMultiWithoutTx #10, len(keys)=3, keys=[/DataA,111, /DataA,222, /DataB,222]
   539  		dsmiddleware/localcache.SetMulti: len=1
   540  		dsmiddleware/localcache.SetMulti: idx=0 key=/DataB,222 len(ps)=1
   541  		before: DeleteMultiWithoutTx #12, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   542  		after: DeleteMultiWithoutTx #11, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   543  		dsmiddleware/localcache.DeleteMulti: len=2
   544  		dsmiddleware/localcache.DeleteMulti: idx=0 key=/DataB,111
   545  		dsmiddleware/localcache.DeleteMulti: idx=1 key=/DataB,222
   546  	`)
   547  
   548  	if v := strings.Join(logs, "\n") + "\n"; v != expected {
   549  		t.Errorf("unexpected: %v", v)
   550  	}
   551  }
   552  
   553  func TestLocalCache_WithKeyFilter(t *testing.T) {
   554  	ctx, client, cleanUp := testutils.SetupCloudDatastore(t)
   555  	defer cleanUp()
   556  
   557  	var logs []string
   558  	logf := func(ctx context.Context, format string, args ...interface{}) {
   559  		t.Logf(format, args...)
   560  		logs = append(logs, fmt.Sprintf(format, args...))
   561  	}
   562  
   563  	// setup. strategies are first in - first apply.
   564  
   565  	bLog := dslog.NewLogger("before: ", logf)
   566  	client.AppendMiddleware(bLog)
   567  	defer func() {
   568  		// stop logging before cleanUp func called.
   569  		client.RemoveMiddleware(bLog)
   570  	}()
   571  
   572  	ch := New(
   573  		WithLogger(logf),
   574  		WithKeyFilter(func(ctx context.Context, key datastore.Key) bool {
   575  			return key.ID() != 111
   576  		}),
   577  	)
   578  	client.AppendMiddleware(ch)
   579  	defer func() {
   580  		// stop logging before cleanUp func called.
   581  		client.RemoveMiddleware(ch)
   582  	}()
   583  
   584  	aLog := dslog.NewLogger("after: ", logf)
   585  	client.AppendMiddleware(aLog)
   586  	defer func() {
   587  		// stop logging before cleanUp func called.
   588  		client.RemoveMiddleware(aLog)
   589  	}()
   590  
   591  	// exec.
   592  
   593  	type Data struct {
   594  		Name string
   595  	}
   596  
   597  	{ // Put. cache target.
   598  		key := client.IDKey("DataA", 222, nil)
   599  		objBefore := &Data{Name: "A"}
   600  		_, err := client.Put(ctx, key, objBefore)
   601  		if err != nil {
   602  			t.Fatal(err)
   603  		}
   604  
   605  		obj := &Data{}
   606  		err = client.Get(ctx, key, obj)
   607  		if err != nil {
   608  			t.Fatal(err)
   609  		}
   610  		if v := obj.Name; v != "A" {
   611  			t.Errorf("unexpected: %v", v)
   612  		}
   613  
   614  		err = client.Delete(ctx, key)
   615  		if err != nil {
   616  			t.Fatal(err)
   617  		}
   618  	}
   619  	{ // Put. cache ignored.
   620  		key := client.IDKey("DataB", 111, nil)
   621  		objBefore := &Data{Name: "B"}
   622  		_, err := client.Put(ctx, key, objBefore)
   623  		if err != nil {
   624  			t.Fatal(err)
   625  		}
   626  
   627  		obj := &Data{}
   628  		err = client.Get(ctx, key, obj)
   629  		if err != nil {
   630  			t.Fatal(err)
   631  		}
   632  		if v := obj.Name; v != "B" {
   633  			t.Errorf("unexpected: %v", v)
   634  		}
   635  
   636  		err = client.Delete(ctx, key)
   637  		if err != nil {
   638  			t.Fatal(err)
   639  		}
   640  	}
   641  	{ // Put. cache target & ignored.
   642  		keyIgnore := client.IDKey("DataA", 111, nil)
   643  		keyTarget := client.IDKey("DataB", 222, nil)
   644  
   645  		list := []*Data{{Name: "A"}, {Name: "B"}}
   646  		_, err := client.PutMulti(ctx, []datastore.Key{keyIgnore, keyTarget}, list)
   647  		if err != nil {
   648  			t.Fatal(err)
   649  		}
   650  
   651  		list = make([]*Data, 2)
   652  		err = client.GetMulti(ctx, []datastore.Key{keyIgnore, keyTarget}, list)
   653  		if err != nil {
   654  			t.Fatal(err)
   655  		}
   656  		if v := len(list); v != 2 {
   657  			t.Fatalf("unexpected: %v", v)
   658  		}
   659  		if v := list[0].Name; v != "A" {
   660  			t.Errorf("unexpected: %v", v)
   661  		}
   662  		if v := list[1].Name; v != "B" {
   663  			t.Errorf("unexpected: %v", v)
   664  		}
   665  
   666  		err = client.DeleteMulti(ctx, []datastore.Key{keyIgnore, keyTarget})
   667  		if err != nil {
   668  			t.Fatal(err)
   669  		}
   670  	}
   671  	{ // Put. partially hit
   672  		keyIgnoreA := client.IDKey("DataA", 111, nil)
   673  		keyIgnoreB := client.IDKey("DataB", 111, nil)
   674  		keyTargetA := client.IDKey("DataA", 222, nil)
   675  		keyTargetB := client.IDKey("DataB", 222, nil)
   676  
   677  		list := []*Data{{Name: "A1"}, {Name: "A2"}, {Name: "B1"}, {Name: "B2"}}
   678  		_, err := client.PutMulti(ctx, []datastore.Key{keyIgnoreA, keyTargetA, keyIgnoreB, keyTargetB}, list)
   679  		if err != nil {
   680  			t.Fatal(err)
   681  		}
   682  
   683  		ch.DeleteCache(ctx, keyIgnoreA)
   684  		ch.DeleteCache(ctx, keyTargetA)
   685  
   686  		list = make([]*Data, 4)
   687  		err = client.GetMulti(ctx, []datastore.Key{keyIgnoreA, keyTargetA, keyIgnoreB, keyTargetB}, list)
   688  		if err != nil {
   689  			t.Fatal(err)
   690  		}
   691  		if v := len(list); v != 4 {
   692  			t.Fatalf("unexpected: %v", v)
   693  		}
   694  		if v := list[0].Name; v != "A1" {
   695  			t.Errorf("unexpected: %v", v)
   696  		}
   697  		if v := list[1].Name; v != "A2" {
   698  			t.Errorf("unexpected: %v", v)
   699  		}
   700  		if v := list[2].Name; v != "B1" {
   701  			t.Errorf("unexpected: %v", v)
   702  		}
   703  		if v := list[3].Name; v != "B2" {
   704  			t.Errorf("unexpected: %v", v)
   705  		}
   706  
   707  		err = client.DeleteMulti(ctx, []datastore.Key{keyIgnoreA, keyTargetA, keyIgnoreB, keyTargetB})
   708  		if err != nil {
   709  			t.Fatal(err)
   710  		}
   711  	}
   712  
   713  	expected := heredoc.Doc(`
   714  		before: PutMultiWithoutTx #1, len(keys)=1, keys=[/DataA,222]
   715  		after: PutMultiWithoutTx #1, len(keys)=1, keys=[/DataA,222]
   716  		after: PutMultiWithoutTx #1, keys=[/DataA,222]
   717  		dsmiddleware/localcache.SetMulti: len=1
   718  		dsmiddleware/localcache.SetMulti: idx=0 key=/DataA,222 len(ps)=1
   719  		before: PutMultiWithoutTx #1, keys=[/DataA,222]
   720  		before: GetMultiWithoutTx #2, len(keys)=1, keys=[/DataA,222]
   721  		dsmiddleware/localcache.GetMulti: len=1
   722  		dsmiddleware/localcache.GetMulti: idx=0 key=/DataA,222
   723  		dsmiddleware/localcache.GetMulti: idx=0, hit key=/DataA,222 len(ps)=1
   724  		before: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/DataA,222]
   725  		after: DeleteMultiWithoutTx #2, len(keys)=1, keys=[/DataA,222]
   726  		dsmiddleware/localcache.DeleteMulti: len=1
   727  		dsmiddleware/localcache.DeleteMulti: idx=0 key=/DataA,222
   728  		before: PutMultiWithoutTx #4, len(keys)=1, keys=[/DataB,111]
   729  		after: PutMultiWithoutTx #3, len(keys)=1, keys=[/DataB,111]
   730  		after: PutMultiWithoutTx #3, keys=[/DataB,111]
   731  		before: PutMultiWithoutTx #4, keys=[/DataB,111]
   732  		before: GetMultiWithoutTx #5, len(keys)=1, keys=[/DataB,111]
   733  		after: GetMultiWithoutTx #4, len(keys)=1, keys=[/DataB,111]
   734  		before: DeleteMultiWithoutTx #6, len(keys)=1, keys=[/DataB,111]
   735  		after: DeleteMultiWithoutTx #5, len(keys)=1, keys=[/DataB,111]
   736  		before: PutMultiWithoutTx #7, len(keys)=2, keys=[/DataA,111, /DataB,222]
   737  		after: PutMultiWithoutTx #6, len(keys)=2, keys=[/DataA,111, /DataB,222]
   738  		after: PutMultiWithoutTx #6, keys=[/DataA,111, /DataB,222]
   739  		dsmiddleware/localcache.SetMulti: len=1
   740  		dsmiddleware/localcache.SetMulti: idx=0 key=/DataB,222 len(ps)=1
   741  		before: PutMultiWithoutTx #7, keys=[/DataA,111, /DataB,222]
   742  		before: GetMultiWithoutTx #8, len(keys)=2, keys=[/DataA,111, /DataB,222]
   743  		dsmiddleware/localcache.GetMulti: len=1
   744  		dsmiddleware/localcache.GetMulti: idx=0 key=/DataB,222
   745  		dsmiddleware/localcache.GetMulti: idx=0, hit key=/DataB,222 len(ps)=1
   746  		after: GetMultiWithoutTx #7, len(keys)=1, keys=[/DataA,111]
   747  		before: DeleteMultiWithoutTx #9, len(keys)=2, keys=[/DataA,111, /DataB,222]
   748  		after: DeleteMultiWithoutTx #8, len(keys)=2, keys=[/DataA,111, /DataB,222]
   749  		dsmiddleware/localcache.DeleteMulti: len=1
   750  		dsmiddleware/localcache.DeleteMulti: idx=0 key=/DataB,222
   751  		before: PutMultiWithoutTx #10, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   752  		after: PutMultiWithoutTx #9, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   753  		after: PutMultiWithoutTx #9, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   754  		dsmiddleware/localcache.SetMulti: len=2
   755  		dsmiddleware/localcache.SetMulti: idx=0 key=/DataA,222 len(ps)=1
   756  		dsmiddleware/localcache.SetMulti: idx=1 key=/DataB,222 len(ps)=1
   757  		before: PutMultiWithoutTx #10, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   758  		dsmiddleware/localcache.DeleteCache: key=/DataA,111
   759  		dsmiddleware/localcache.DeleteCache: key=/DataA,222
   760  		before: GetMultiWithoutTx #11, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   761  		dsmiddleware/localcache.GetMulti: len=2
   762  		dsmiddleware/localcache.GetMulti: idx=0 key=/DataA,222
   763  		dsmiddleware/localcache.GetMulti: idx=1 key=/DataB,222
   764  		dsmiddleware/localcache.GetMulti: idx=0, missed key=/DataA,222
   765  		dsmiddleware/localcache.GetMulti: idx=1, hit key=/DataB,222 len(ps)=1
   766  		after: GetMultiWithoutTx #10, len(keys)=3, keys=[/DataA,111, /DataA,222, /DataB,111]
   767  		dsmiddleware/localcache.SetMulti: len=1
   768  		dsmiddleware/localcache.SetMulti: idx=0 key=/DataA,222 len(ps)=1
   769  		before: DeleteMultiWithoutTx #12, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   770  		after: DeleteMultiWithoutTx #11, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222]
   771  		dsmiddleware/localcache.DeleteMulti: len=2
   772  		dsmiddleware/localcache.DeleteMulti: idx=0 key=/DataA,222
   773  		dsmiddleware/localcache.DeleteMulti: idx=1 key=/DataB,222
   774  	`)
   775  
   776  	if v := strings.Join(logs, "\n") + "\n"; v != expected {
   777  		t.Errorf("unexpected: %v", v)
   778  	}
   779  }
   780  
   781  func TestLocalCache_FlushLocalCache(t *testing.T) {
   782  	ctx, client, cleanUp := testutils.SetupCloudDatastore(t)
   783  	defer cleanUp()
   784  
   785  	ch := New()
   786  	client.AppendMiddleware(ch)
   787  	defer func() {
   788  		// stop logging before cleanUp func called.
   789  		client.RemoveMiddleware(ch)
   790  	}()
   791  
   792  	type Data struct {
   793  		Name string
   794  	}
   795  
   796  	// Put. add to cache.
   797  	key := client.IDKey("Data", 111, nil)
   798  	objBefore := &Data{Name: "Data"}
   799  	_, err := client.Put(ctx, key, objBefore)
   800  	if err != nil {
   801  		t.Fatal(err)
   802  	}
   803  
   804  	if v := ch.HasCache(key); !v {
   805  		t.Fatalf("unexpected: %v", v)
   806  	}
   807  
   808  	ch.FlushLocalCache()
   809  
   810  	if v := ch.HasCache(key); v {
   811  		t.Fatalf("unexpected: %v", v)
   812  	}
   813  }
   814  
   815  func TestLocalCache_Query(t *testing.T) {
   816  	ctx, client, cleanUp := testutils.SetupCloudDatastore(t)
   817  	defer cleanUp()
   818  
   819  	var logs []string
   820  	logf := func(ctx context.Context, format string, args ...interface{}) {
   821  		t.Logf(format, args...)
   822  		logs = append(logs, fmt.Sprintf(format, args...))
   823  	}
   824  
   825  	// setup. strategies are first in - first apply.
   826  
   827  	bLog := dslog.NewLogger("before: ", logf)
   828  	client.AppendMiddleware(bLog)
   829  	defer func() {
   830  		// stop logging before cleanUp func called.
   831  		client.RemoveMiddleware(bLog)
   832  	}()
   833  
   834  	ch := New()
   835  	client.AppendMiddleware(ch)
   836  	defer func() {
   837  		// stop logging before cleanUp func called.
   838  		client.RemoveMiddleware(ch)
   839  	}()
   840  
   841  	aLog := dslog.NewLogger("after: ", logf)
   842  	client.AppendMiddleware(aLog)
   843  	defer func() {
   844  		// stop logging before cleanUp func called.
   845  		client.RemoveMiddleware(aLog)
   846  	}()
   847  
   848  	// exec.
   849  
   850  	type Data struct {
   851  		Name string
   852  	}
   853  
   854  	const size = 3
   855  
   856  	keys := make([]datastore.Key, size)
   857  	list := make([]*Data, size)
   858  	for i := 0; i < size; i++ {
   859  		keys[i] = client.NameKey("Data", fmt.Sprintf("#%d", i+1), nil)
   860  		list[i] = &Data{
   861  			Name: fmt.Sprintf("#%d", i+1),
   862  		}
   863  	}
   864  	_, err := client.PutMulti(ctx, keys, list)
   865  	if err != nil {
   866  		t.Fatal(err)
   867  	}
   868  
   869  	q := client.NewQuery("Data").Order("-Name")
   870  
   871  	// Run
   872  	iter := client.Run(ctx, q)
   873  
   874  	// Next
   875  	cnt := 0
   876  	for {
   877  		obj := &Data{}
   878  		key, err := iter.Next(obj)
   879  		if err == iterator.Done {
   880  			break
   881  		} else if err != nil {
   882  			t.Fatal(err)
   883  		}
   884  		if v := obj.Name; v == "" || v != key.Name() {
   885  			t.Errorf("unexpected: %v", cnt)
   886  		}
   887  		cnt++
   888  	}
   889  	if cnt != size {
   890  		t.Errorf("unexpected: %v", cnt)
   891  	}
   892  
   893  	// GetAll
   894  	list = nil
   895  	_, err = client.GetAll(ctx, q, &list)
   896  	if err != nil {
   897  		t.Fatal(err)
   898  	}
   899  
   900  	expected := heredoc.Doc(`
   901  		before: PutMultiWithoutTx #1, len(keys)=3, keys=[/Data,#1, /Data,#2, /Data,#3]
   902  		after: PutMultiWithoutTx #1, len(keys)=3, keys=[/Data,#1, /Data,#2, /Data,#3]
   903  		after: PutMultiWithoutTx #1, keys=[/Data,#1, /Data,#2, /Data,#3]
   904  		before: PutMultiWithoutTx #1, keys=[/Data,#1, /Data,#2, /Data,#3]
   905  		before: Run #2, q=v1:Data&or=-Name
   906  		after: Run #2, q=v1:Data&or=-Name
   907  		before: Next #3, q=v1:Data&or=-Name
   908  		after: Next #3, q=v1:Data&or=-Name
   909  		after: Next #3, key=/Data,#3
   910  		before: Next #3, key=/Data,#3
   911  		before: Next #4, q=v1:Data&or=-Name
   912  		after: Next #4, q=v1:Data&or=-Name
   913  		after: Next #4, key=/Data,#2
   914  		before: Next #4, key=/Data,#2
   915  		before: Next #5, q=v1:Data&or=-Name
   916  		after: Next #5, q=v1:Data&or=-Name
   917  		after: Next #5, key=/Data,#1
   918  		before: Next #5, key=/Data,#1
   919  		before: Next #6, q=v1:Data&or=-Name
   920  		after: Next #6, q=v1:Data&or=-Name
   921  		after: Next #6, err=no more items in iterator
   922  		before: Next #6, err=no more items in iterator
   923  		before: GetAll #7, q=v1:Data&or=-Name
   924  		after: GetAll #7, q=v1:Data&or=-Name
   925  		after: GetAll #7, len(keys)=3, keys=[/Data,#3, /Data,#2, /Data,#1]
   926  		before: GetAll #7, len(keys)=3, keys=[/Data,#3, /Data,#2, /Data,#1]
   927  	`)
   928  
   929  	if v := strings.Join(logs, "\n") + "\n"; v != expected {
   930  		t.Errorf("unexpected: %v", v)
   931  	}
   932  }
   933  
   934  func TestLocalCache_Transaction(t *testing.T) {
   935  	ctx, client, cleanUp := testutils.SetupCloudDatastore(t)
   936  	defer cleanUp()
   937  
   938  	var logs []string
   939  	logf := func(ctx context.Context, format string, args ...interface{}) {
   940  		t.Logf(format, args...)
   941  		logs = append(logs, fmt.Sprintf(format, args...))
   942  	}
   943  
   944  	// setup. strategies are first in - first apply.
   945  
   946  	bLog := dslog.NewLogger("before: ", logf)
   947  	client.AppendMiddleware(bLog)
   948  	defer func() {
   949  		// stop logging before cleanUp func called.
   950  		client.RemoveMiddleware(bLog)
   951  	}()
   952  
   953  	ch := New()
   954  	client.AppendMiddleware(ch)
   955  	defer func() {
   956  		// stop logging before cleanUp func called.
   957  		client.RemoveMiddleware(ch)
   958  	}()
   959  
   960  	aLog := dslog.NewLogger("after: ", logf)
   961  	client.AppendMiddleware(aLog)
   962  	defer func() {
   963  		// stop logging before cleanUp func called.
   964  		client.RemoveMiddleware(aLog)
   965  	}()
   966  
   967  	// exec.
   968  
   969  	type Data struct {
   970  		Name string
   971  	}
   972  
   973  	key := client.NameKey("Data", "a", nil)
   974  
   975  	// put to cache
   976  	_, err := client.Put(ctx, key, &Data{Name: "Before"})
   977  	if err != nil {
   978  		t.Fatal(err)
   979  	}
   980  	if v := ch.HasCache(key); !v {
   981  		t.Fatalf("unexpected: %v", v)
   982  	}
   983  
   984  	{ // Rollback
   985  		tx, err := client.NewTransaction(ctx)
   986  		if err != nil {
   987  			t.Fatal(err)
   988  		}
   989  
   990  		// don't put to cache before commit
   991  		key2 := client.NameKey("Data", "b", nil)
   992  		_, err = tx.Put(key2, &Data{Name: "After"})
   993  		if err != nil {
   994  			t.Fatal(err)
   995  		}
   996  		if v := ch.HasCache(key2); v {
   997  			t.Fatalf("unexpected: %v", v)
   998  		}
   999  
  1000  		obj := &Data{}
  1001  		err = tx.Get(key, obj)
  1002  		if err != nil {
  1003  			t.Fatal(err)
  1004  		}
  1005  
  1006  		// don't delete from cache before commit
  1007  		err = tx.Delete(key)
  1008  		if err != nil {
  1009  			t.Fatal(err)
  1010  		}
  1011  		if v := ch.HasCache(key); !v {
  1012  			t.Fatalf("unexpected: %v", v)
  1013  		}
  1014  
  1015  		// rollback.
  1016  		err = tx.Rollback()
  1017  		if err != nil {
  1018  			t.Fatal(err)
  1019  		}
  1020  		if v := ch.CacheLen(); v != 1 {
  1021  			t.Fatalf("unexpected: %v", v)
  1022  		}
  1023  	}
  1024  
  1025  	{ // Commit
  1026  		tx, err := client.NewTransaction(ctx)
  1027  		if err != nil {
  1028  			t.Fatal(err)
  1029  		}
  1030  
  1031  		// don't put to cache before commit
  1032  		key2 := client.IncompleteKey("Data", nil)
  1033  		pKey, err := tx.Put(key2, &Data{Name: "After"})
  1034  		if err != nil {
  1035  			t.Fatal(err)
  1036  		}
  1037  		if v := ch.CacheLen(); v != 1 {
  1038  			t.Fatalf("unexpected: %v", v)
  1039  		}
  1040  
  1041  		obj := &Data{}
  1042  		err = tx.Get(key, obj)
  1043  		if err != nil {
  1044  			t.Fatal(err)
  1045  		}
  1046  
  1047  		// don't delete from cache before commit
  1048  		err = tx.Delete(key)
  1049  		if err != nil {
  1050  			t.Fatal(err)
  1051  		}
  1052  		if v := ch.HasCache(key); !v {
  1053  			t.Fatalf("unexpected: %v", v)
  1054  		}
  1055  
  1056  		// commit.
  1057  		commit, err := tx.Commit()
  1058  		if err != nil {
  1059  			t.Fatal(err)
  1060  		}
  1061  
  1062  		key3 := commit.Key(pKey)
  1063  		if v := key3.Name(); v != key2.Name() {
  1064  			t.Errorf("unexpected: %v", v)
  1065  		}
  1066  		// committed, but don't put to cache in tx.
  1067  		if v := ch.HasCache(key3); v {
  1068  			t.Fatalf("unexpected: %v", v)
  1069  		}
  1070  
  1071  		if v := ch.CacheLen(); v != 0 {
  1072  			for _, keyStr := range ch.CacheKeys() {
  1073  				key, err := client.DecodeKey(keyStr)
  1074  				if err != nil {
  1075  					t.Fatal(err)
  1076  				}
  1077  				t.Log(key.String())
  1078  			}
  1079  			t.Fatalf("unexpected: %v", v)
  1080  		}
  1081  	}
  1082  
  1083  	var expected *regexp.Regexp
  1084  	{
  1085  		expectedPattern := heredoc.Doc(`
  1086  			before: PutMultiWithoutTx #1, len(keys)=1, keys=[/Data,a]
  1087  			after: PutMultiWithoutTx #1, len(keys)=1, keys=[/Data,a]
  1088  			after: PutMultiWithoutTx #1, keys=[/Data,a]
  1089  			before: PutMultiWithoutTx #1, keys=[/Data,a]
  1090  			before: PutMultiWithTx #2, len(keys)=1, keys=[/Data,b]
  1091  			after: PutMultiWithTx #2, len(keys)=1, keys=[/Data,b]
  1092  			before: GetMultiWithTx #3, len(keys)=1, keys=[/Data,a]
  1093  			after: GetMultiWithTx #3, len(keys)=1, keys=[/Data,a]
  1094  			before: DeleteMultiWithTx #4, len(keys)=1, keys=[/Data,a]
  1095  			after: DeleteMultiWithTx #4, len(keys)=1, keys=[/Data,a]
  1096  			before: PostRollback #5
  1097  			after: PostRollback #5
  1098  			before: PutMultiWithTx #6, len(keys)=1, keys=[/Data,0]
  1099  			after: PutMultiWithTx #6, len(keys)=1, keys=[/Data,0]
  1100  			before: GetMultiWithTx #7, len(keys)=1, keys=[/Data,a]
  1101  			after: GetMultiWithTx #7, len(keys)=1, keys=[/Data,a]
  1102  			before: DeleteMultiWithTx #8, len(keys)=1, keys=[/Data,a]
  1103  			after: DeleteMultiWithTx #8, len(keys)=1, keys=[/Data,a]
  1104  			before: PostCommit #9 Put keys=[/Data,@####@]
  1105  			after: PostCommit #9 Put keys=[/Data,@####@]
  1106  		`)
  1107  		ss := strings.Split(expectedPattern, "@####@")
  1108  		var buf bytes.Buffer
  1109  		for idx, s := range ss {
  1110  			buf.WriteString(regexp.QuoteMeta(s))
  1111  			if idx != (len(ss) - 1) {
  1112  				buf.WriteString("[0-9]+")
  1113  			}
  1114  		}
  1115  		expected = regexp.MustCompile(buf.String())
  1116  	}
  1117  
  1118  	if v := strings.Join(logs, "\n") + "\n"; !expected.MatchString(v) {
  1119  		t.Errorf("unexpected: %v", v)
  1120  	}
  1121  }
  1122  
  1123  func TestLocalCache_MultiError(t *testing.T) {
  1124  	ctx, client, cleanUp := testutils.SetupCloudDatastore(t)
  1125  	defer cleanUp()
  1126  
  1127  	var logs []string
  1128  	logf := func(ctx context.Context, format string, args ...interface{}) {
  1129  		t.Logf(format, args...)
  1130  		logs = append(logs, fmt.Sprintf(format, args...))
  1131  	}
  1132  
  1133  	// setup. strategies are first in - first apply.
  1134  
  1135  	bLog := dslog.NewLogger("before: ", logf)
  1136  	client.AppendMiddleware(bLog)
  1137  	defer func() {
  1138  		// stop logging before cleanUp func called.
  1139  		client.RemoveMiddleware(bLog)
  1140  	}()
  1141  
  1142  	ch := New(
  1143  		WithLogger(logf),
  1144  	)
  1145  	client.AppendMiddleware(ch)
  1146  	defer func() {
  1147  		// stop logging before cleanUp func called.
  1148  		client.RemoveMiddleware(ch)
  1149  	}()
  1150  
  1151  	aLog := dslog.NewLogger("after: ", logf)
  1152  	client.AppendMiddleware(aLog)
  1153  	defer func() {
  1154  		// stop logging before cleanUp func called.
  1155  		client.RemoveMiddleware(aLog)
  1156  	}()
  1157  
  1158  	// exec.
  1159  
  1160  	type Data struct {
  1161  		Name string
  1162  	}
  1163  
  1164  	const size = 10
  1165  
  1166  	keys := make([]datastore.Key, 0, size)
  1167  	list := make([]*Data, 0, size)
  1168  	for i := 1; i <= size; i++ {
  1169  		keys = append(keys, client.IDKey("Data", int64(i), nil))
  1170  		list = append(list, &Data{
  1171  			Name: fmt.Sprintf("#%d", i),
  1172  		})
  1173  	}
  1174  
  1175  	_, err := client.PutMulti(ctx, keys, list)
  1176  	if err != nil {
  1177  		t.Fatal(err)
  1178  	}
  1179  
  1180  	for _, key := range keys {
  1181  		if key.ID()%2 == 0 {
  1182  			// delete cache id=2, 4, 6, 8, 10
  1183  			ch.DeleteCache(ctx, key)
  1184  		}
  1185  		if key.ID()%3 == 0 {
  1186  			// Delete entity where out of aememcache scope
  1187  			// delete entity id=3, 6, 9
  1188  			client.RemoveMiddleware(ch)
  1189  			err := client.Delete(ctx, key)
  1190  			if err != nil {
  1191  				t.Fatal(err)
  1192  			}
  1193  			client.RemoveMiddleware(aLog)
  1194  			client.AppendMiddleware(ch)
  1195  			client.AppendMiddleware(aLog)
  1196  		}
  1197  	}
  1198  
  1199  	list = make([]*Data, size)
  1200  	err = client.GetMulti(ctx, keys, list)
  1201  	merr, ok := err.(datastore.MultiError)
  1202  	if !ok {
  1203  		t.Fatal(err)
  1204  	}
  1205  
  1206  	if v := len(merr); v != size {
  1207  		t.Fatalf("unexpected: %v", v)
  1208  	}
  1209  	for idx, err := range merr {
  1210  		key := keys[idx]
  1211  		if key.ID()%2 == 0 && key.ID()%3 == 0 {
  1212  			// not exists on memcache & datastore both
  1213  			if err != datastore.ErrNoSuchEntity {
  1214  				t.Error(err)
  1215  			}
  1216  		} else {
  1217  			if v := list[idx].Name; v != fmt.Sprintf("#%d", idx+1) {
  1218  				t.Errorf("unexpected: %v", v)
  1219  			}
  1220  		}
  1221  	}
  1222  
  1223  	expected := heredoc.Doc(`
  1224  		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]
  1225  		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]
  1226  		after: PutMultiWithoutTx #1, keys=[/Data,1, /Data,2, /Data,3, /Data,4, /Data,5, /Data,6, /Data,7, /Data,8, /Data,9, /Data,10]
  1227  		dsmiddleware/localcache.SetMulti: len=10
  1228  		dsmiddleware/localcache.SetMulti: idx=0 key=/Data,1 len(ps)=1
  1229  		dsmiddleware/localcache.SetMulti: idx=1 key=/Data,2 len(ps)=1
  1230  		dsmiddleware/localcache.SetMulti: idx=2 key=/Data,3 len(ps)=1
  1231  		dsmiddleware/localcache.SetMulti: idx=3 key=/Data,4 len(ps)=1
  1232  		dsmiddleware/localcache.SetMulti: idx=4 key=/Data,5 len(ps)=1
  1233  		dsmiddleware/localcache.SetMulti: idx=5 key=/Data,6 len(ps)=1
  1234  		dsmiddleware/localcache.SetMulti: idx=6 key=/Data,7 len(ps)=1
  1235  		dsmiddleware/localcache.SetMulti: idx=7 key=/Data,8 len(ps)=1
  1236  		dsmiddleware/localcache.SetMulti: idx=8 key=/Data,9 len(ps)=1
  1237  		dsmiddleware/localcache.SetMulti: idx=9 key=/Data,10 len(ps)=1
  1238  		before: PutMultiWithoutTx #1, keys=[/Data,1, /Data,2, /Data,3, /Data,4, /Data,5, /Data,6, /Data,7, /Data,8, /Data,9, /Data,10]
  1239  		dsmiddleware/localcache.DeleteCache: key=/Data,2
  1240  		before: DeleteMultiWithoutTx #2, len(keys)=1, keys=[/Data,3]
  1241  		after: DeleteMultiWithoutTx #2, len(keys)=1, keys=[/Data,3]
  1242  		dsmiddleware/localcache.DeleteCache: key=/Data,4
  1243  		dsmiddleware/localcache.DeleteCache: key=/Data,6
  1244  		before: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/Data,6]
  1245  		after: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/Data,6]
  1246  		dsmiddleware/localcache.DeleteCache: key=/Data,8
  1247  		before: DeleteMultiWithoutTx #4, len(keys)=1, keys=[/Data,9]
  1248  		after: DeleteMultiWithoutTx #4, len(keys)=1, keys=[/Data,9]
  1249  		dsmiddleware/localcache.DeleteCache: key=/Data,10
  1250  		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]
  1251  		dsmiddleware/localcache.GetMulti: len=10
  1252  		dsmiddleware/localcache.GetMulti: idx=0 key=/Data,1
  1253  		dsmiddleware/localcache.GetMulti: idx=1 key=/Data,2
  1254  		dsmiddleware/localcache.GetMulti: idx=2 key=/Data,3
  1255  		dsmiddleware/localcache.GetMulti: idx=3 key=/Data,4
  1256  		dsmiddleware/localcache.GetMulti: idx=4 key=/Data,5
  1257  		dsmiddleware/localcache.GetMulti: idx=5 key=/Data,6
  1258  		dsmiddleware/localcache.GetMulti: idx=6 key=/Data,7
  1259  		dsmiddleware/localcache.GetMulti: idx=7 key=/Data,8
  1260  		dsmiddleware/localcache.GetMulti: idx=8 key=/Data,9
  1261  		dsmiddleware/localcache.GetMulti: idx=9 key=/Data,10
  1262  		dsmiddleware/localcache.GetMulti: idx=0, hit key=/Data,1 len(ps)=1
  1263  		dsmiddleware/localcache.GetMulti: idx=1, missed key=/Data,2
  1264  		dsmiddleware/localcache.GetMulti: idx=2, hit key=/Data,3 len(ps)=1
  1265  		dsmiddleware/localcache.GetMulti: idx=3, missed key=/Data,4
  1266  		dsmiddleware/localcache.GetMulti: idx=4, hit key=/Data,5 len(ps)=1
  1267  		dsmiddleware/localcache.GetMulti: idx=5, missed key=/Data,6
  1268  		dsmiddleware/localcache.GetMulti: idx=6, hit key=/Data,7 len(ps)=1
  1269  		dsmiddleware/localcache.GetMulti: idx=7, missed key=/Data,8
  1270  		dsmiddleware/localcache.GetMulti: idx=8, hit key=/Data,9 len(ps)=1
  1271  		dsmiddleware/localcache.GetMulti: idx=9, missed key=/Data,10
  1272  		after: GetMultiWithoutTx #5, len(keys)=5, keys=[/Data,2, /Data,4, /Data,6, /Data,8, /Data,10]
  1273  		after: GetMultiWithoutTx #5, err=datastore: no such entity
  1274  		dsmiddleware/localcache.SetMulti: len=4
  1275  		dsmiddleware/localcache.SetMulti: idx=0 key=/Data,2 len(ps)=1
  1276  		dsmiddleware/localcache.SetMulti: idx=1 key=/Data,4 len(ps)=1
  1277  		dsmiddleware/localcache.SetMulti: idx=2 key=/Data,8 len(ps)=1
  1278  		dsmiddleware/localcache.SetMulti: idx=3 key=/Data,10 len(ps)=1
  1279  		before: GetMultiWithoutTx #5, err=datastore: no such entity
  1280  	`)
  1281  
  1282  	if v := strings.Join(logs, "\n") + "\n"; v != expected {
  1283  		t.Errorf("unexpected: %v", v)
  1284  	}
  1285  }