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