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

     1  package splitop
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/MakeNowJust/heredoc/v2"
    10  	"go.mercari.io/datastore"
    11  	"go.mercari.io/datastore/dsmiddleware/dslog"
    12  	"go.mercari.io/datastore/internal/testutils"
    13  )
    14  
    15  func TestSplitOp_Basic(t *testing.T) {
    16  	ctx, client, cleanUp := testutils.SetupCloudDatastore(t)
    17  	defer cleanUp()
    18  
    19  	var logs []string
    20  	logf := func(ctx context.Context, format string, args ...interface{}) {
    21  		t.Logf(format, args...)
    22  		logs = append(logs, fmt.Sprintf(format, args...))
    23  	}
    24  
    25  	// setup. strategies are first in - first apply.
    26  
    27  	bLog := dslog.NewLogger("before: ", logf)
    28  	client.AppendMiddleware(bLog)
    29  	defer func() {
    30  		// stop logging before cleanUp func called.
    31  		client.RemoveMiddleware(bLog)
    32  	}()
    33  
    34  	ch := New(
    35  		WithLogger(logf),
    36  		WithGetSplitThreshold(3),
    37  		WithPutSplitThreshold(2),
    38  	)
    39  	client.AppendMiddleware(ch)
    40  	defer func() {
    41  		// stop logging before cleanUp func called.
    42  		client.RemoveMiddleware(ch)
    43  	}()
    44  
    45  	aLog := dslog.NewLogger("after: ", logf)
    46  	client.AppendMiddleware(aLog)
    47  	defer func() {
    48  		// stop logging before cleanUp func called.
    49  		client.RemoveMiddleware(aLog)
    50  	}()
    51  
    52  	// exec.
    53  
    54  	type Data struct {
    55  		Name string
    56  	}
    57  
    58  	// Put
    59  	var keys []datastore.Key
    60  	var entities []*Data
    61  	for i := 1; i <= 5; i++ {
    62  		keys = append(keys, client.IDKey("Data", int64(i), nil))
    63  		entities = append(entities, &Data{Name: fmt.Sprintf("Data%d", i)})
    64  	}
    65  	_, err := client.PutMulti(ctx, keys, entities)
    66  	if err != nil {
    67  		t.Fatal(err)
    68  	}
    69  
    70  	// Get
    71  	result := make([]*Data, len(keys))
    72  	err = client.GetMulti(ctx, keys, result)
    73  	if err != nil {
    74  		t.Fatal(err)
    75  	}
    76  
    77  	for idx, obj := range result {
    78  		if v := obj.Name; v != fmt.Sprintf("Data%d", keys[idx].ID()) {
    79  			t.Errorf("unexpected: %v", v)
    80  		}
    81  	}
    82  
    83  	expected := heredoc.Doc(`
    84  		before: PutMultiWithoutTx #1, len(keys)=5, keys=[/Data,1, /Data,2, /Data,3, /Data,4, /Data,5]
    85  		put 5 keys
    86  		put [0, 2) range keys
    87  		after: PutMultiWithoutTx #1, len(keys)=2, keys=[/Data,1, /Data,2]
    88  		after: PutMultiWithoutTx #1, keys=[/Data,1, /Data,2]
    89  		put [2, 4) range keys
    90  		after: PutMultiWithoutTx #2, len(keys)=2, keys=[/Data,3, /Data,4]
    91  		after: PutMultiWithoutTx #2, keys=[/Data,3, /Data,4]
    92  		put [4, 5) range keys
    93  		after: PutMultiWithoutTx #3, len(keys)=1, keys=[/Data,5]
    94  		after: PutMultiWithoutTx #3, keys=[/Data,5]
    95  		before: PutMultiWithoutTx #1, keys=[/Data,1, /Data,2, /Data,3, /Data,4, /Data,5]
    96  		before: GetMultiWithoutTx #2, len(keys)=5, keys=[/Data,1, /Data,2, /Data,3, /Data,4, /Data,5]
    97  		get 5 keys
    98  		get [0, 3) range keys
    99  		after: GetMultiWithoutTx #4, len(keys)=3, keys=[/Data,1, /Data,2, /Data,3]
   100  		get [3, 5) range keys
   101  		after: GetMultiWithoutTx #5, len(keys)=2, keys=[/Data,4, /Data,5]
   102  	`)
   103  
   104  	if v := strings.Join(logs, "\n") + "\n"; v != expected {
   105  		t.Errorf("unexpected: %v", v)
   106  	}
   107  }
   108  
   109  func TestSplitOp_HasNoSuchEntity(t *testing.T) {
   110  	ctx, client, cleanUp := testutils.SetupCloudDatastore(t)
   111  	defer cleanUp()
   112  
   113  	var logs []string
   114  	logf := func(ctx context.Context, format string, args ...interface{}) {
   115  		t.Logf(format, args...)
   116  		logs = append(logs, fmt.Sprintf(format, args...))
   117  	}
   118  
   119  	// setup. strategies are first in - first apply.
   120  
   121  	bLog := dslog.NewLogger("before: ", logf)
   122  	client.AppendMiddleware(bLog)
   123  	defer func() {
   124  		// stop logging before cleanUp func called.
   125  		client.RemoveMiddleware(bLog)
   126  	}()
   127  
   128  	ch := New(
   129  		WithLogger(logf),
   130  		WithGetSplitThreshold(3),
   131  		WithPutSplitThreshold(2),
   132  	)
   133  	client.AppendMiddleware(ch)
   134  	defer func() {
   135  		// stop logging before cleanUp func called.
   136  		client.RemoveMiddleware(ch)
   137  	}()
   138  
   139  	aLog := dslog.NewLogger("after: ", logf)
   140  	client.AppendMiddleware(aLog)
   141  	defer func() {
   142  		// stop logging before cleanUp func called.
   143  		client.RemoveMiddleware(aLog)
   144  	}()
   145  
   146  	// exec.
   147  
   148  	type Data struct {
   149  		Name string
   150  	}
   151  
   152  	// Put
   153  	var keys []datastore.Key
   154  	{
   155  		var putKeys []datastore.Key
   156  		var entities []*Data
   157  		for i := 1; i <= 5; i++ {
   158  			key := client.IDKey("Data", int64(i), nil)
   159  			keys = append(keys, key)
   160  
   161  			switch key.ID() {
   162  			case 1, 3, 5:
   163  				putKeys = append(putKeys, key)
   164  				entities = append(entities, &Data{Name: fmt.Sprintf("Data%d", key.ID())})
   165  			}
   166  		}
   167  		_, err := client.PutMulti(ctx, putKeys, entities)
   168  		if err != nil {
   169  			t.Fatal(err)
   170  		}
   171  	}
   172  
   173  	// Get
   174  	result := make([]*Data, len(keys))
   175  	err := client.GetMulti(ctx, keys, result)
   176  	if mErr, ok := err.(datastore.MultiError); !ok {
   177  		t.Fatal(err)
   178  	} else {
   179  		for idx, err := range mErr {
   180  			id := keys[idx].ID()
   181  			switch id {
   182  			case 1, 3, 5:
   183  				if err != nil {
   184  					t.Errorf("unexpected error content: %d, %v", id, err)
   185  				}
   186  			default:
   187  				if err != datastore.ErrNoSuchEntity {
   188  					t.Errorf("unexpected error content: %d, %v", id, err)
   189  				}
   190  			}
   191  		}
   192  	}
   193  
   194  	for idx, obj := range result {
   195  		if obj == nil {
   196  			continue
   197  		}
   198  		if v := obj.Name; v != fmt.Sprintf("Data%d", keys[idx].ID()) {
   199  			t.Errorf("unexpected: %v", v)
   200  		}
   201  	}
   202  
   203  	expected := heredoc.Doc(`
   204  		before: PutMultiWithoutTx #1, len(keys)=3, keys=[/Data,1, /Data,3, /Data,5]
   205  		put 3 keys
   206  		put [0, 2) range keys
   207  		after: PutMultiWithoutTx #1, len(keys)=2, keys=[/Data,1, /Data,3]
   208  		after: PutMultiWithoutTx #1, keys=[/Data,1, /Data,3]
   209  		put [2, 3) range keys
   210  		after: PutMultiWithoutTx #2, len(keys)=1, keys=[/Data,5]
   211  		after: PutMultiWithoutTx #2, keys=[/Data,5]
   212  		before: PutMultiWithoutTx #1, keys=[/Data,1, /Data,3, /Data,5]
   213  		before: GetMultiWithoutTx #2, len(keys)=5, keys=[/Data,1, /Data,2, /Data,3, /Data,4, /Data,5]
   214  		get 5 keys
   215  		get [0, 3) range keys
   216  		after: GetMultiWithoutTx #3, len(keys)=3, keys=[/Data,1, /Data,2, /Data,3]
   217  		after: GetMultiWithoutTx #3, err=datastore: no such entity
   218  		get [3, 5) range keys
   219  		after: GetMultiWithoutTx #4, len(keys)=2, keys=[/Data,4, /Data,5]
   220  		after: GetMultiWithoutTx #4, err=datastore: no such entity
   221  		before: GetMultiWithoutTx #2, err=datastore: no such entity (and 1 other error)
   222  	`)
   223  
   224  	if v := strings.Join(logs, "\n") + "\n"; v != expected {
   225  		t.Errorf("unexpected: %v", v)
   226  	}
   227  }