vitess.io/vitess@v0.16.2/go/vt/srvtopo/resolver_test.go (about)

     1  /*
     2  Copyright 2019 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package srvtopo
    18  
    19  import (
    20  	"testing"
    21  
    22  	"github.com/stretchr/testify/require"
    23  
    24  	"context"
    25  
    26  	"vitess.io/vitess/go/sqltypes"
    27  	"vitess.io/vitess/go/vt/key"
    28  	"vitess.io/vitess/go/vt/logutil"
    29  	"vitess.io/vitess/go/vt/topo/memorytopo"
    30  	"vitess.io/vitess/go/vt/topotools"
    31  	"vitess.io/vitess/go/vt/vttablet/tabletconntest"
    32  
    33  	querypb "vitess.io/vitess/go/vt/proto/query"
    34  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    35  )
    36  
    37  func initResolver(t *testing.T, name string) *Resolver {
    38  	ctx := context.Background()
    39  	cell := "cell1"
    40  	ts := memorytopo.NewServer(cell)
    41  	rs := NewResilientServer(ts, name)
    42  
    43  	// Create sharded keyspace and shards.
    44  	if err := ts.CreateKeyspace(ctx, "sks", &topodatapb.Keyspace{}); err != nil {
    45  		t.Fatalf("CreateKeyspace(sks) failed: %v", err)
    46  	}
    47  	shardKrArray, err := key.ParseShardingSpec("-20-40-60-80-a0-c0-e0-")
    48  	if err != nil {
    49  		t.Fatalf("key.ParseShardingSpec failed: %v", err)
    50  	}
    51  	for _, kr := range shardKrArray {
    52  		shard := key.KeyRangeString(kr)
    53  		if err := ts.CreateShard(ctx, "sks", shard); err != nil {
    54  			t.Fatalf("CreateShard(\"%v\") failed: %v", shard, err)
    55  		}
    56  	}
    57  
    58  	// Create unsharded keyspace and shard.
    59  	if err := ts.CreateKeyspace(ctx, "uks", &topodatapb.Keyspace{}); err != nil {
    60  		t.Fatalf("CreateKeyspace(uks) failed: %v", err)
    61  	}
    62  	if err := ts.CreateShard(ctx, "uks", "0"); err != nil {
    63  		t.Fatalf("CreateShard(0) failed: %v", err)
    64  	}
    65  
    66  	// And rebuild both.
    67  	for _, keyspace := range []string{"sks", "uks"} {
    68  		if err := topotools.RebuildKeyspace(ctx, logutil.NewConsoleLogger(), ts, keyspace, []string{cell}, false); err != nil {
    69  			t.Fatalf("RebuildKeyspace(%v) failed: %v", keyspace, err)
    70  		}
    71  	}
    72  
    73  	// Create snapshot keyspace and shard.
    74  	err = ts.CreateKeyspace(ctx, "rks", &topodatapb.Keyspace{KeyspaceType: topodatapb.KeyspaceType_SNAPSHOT})
    75  	require.NoError(t, err, "CreateKeyspace(rks) failed: %v")
    76  	err = ts.CreateShard(ctx, "rks", "-80")
    77  	require.NoError(t, err, "CreateShard(-80) failed: %v")
    78  
    79  	// Rebuild should error because allowPartial is false and shard does not cover full keyrange
    80  	err = topotools.RebuildKeyspace(ctx, logutil.NewConsoleLogger(), ts, "rks", []string{cell}, false)
    81  	require.Error(t, err, "RebuildKeyspace(rks) failed")
    82  	require.EqualError(t, err, "keyspace partition for PRIMARY in cell cell1 does not end with max key")
    83  
    84  	// Rebuild should succeed with allowPartial true
    85  	err = topotools.RebuildKeyspace(ctx, logutil.NewConsoleLogger(), ts, "rks", []string{cell}, true)
    86  	require.NoError(t, err, "RebuildKeyspace(rks) failed")
    87  
    88  	// Create missing shard
    89  	err = ts.CreateShard(ctx, "rks", "80-")
    90  	require.NoError(t, err, "CreateShard(80-) failed: %v")
    91  
    92  	// Rebuild should now succeed even with allowPartial false
    93  	err = topotools.RebuildKeyspace(ctx, logutil.NewConsoleLogger(), ts, "rks", []string{cell}, false)
    94  	require.NoError(t, err, "RebuildKeyspace(rks) failed")
    95  
    96  	return NewResolver(rs, &tabletconntest.FakeQueryService{}, cell)
    97  }
    98  
    99  func TestResolveDestinations(t *testing.T) {
   100  	resolver := initResolver(t, "TestResolveDestinations")
   101  
   102  	id1 := &querypb.Value{
   103  		Type:  sqltypes.VarChar,
   104  		Value: []byte("1"),
   105  	}
   106  	id2 := &querypb.Value{
   107  		Type:  sqltypes.VarChar,
   108  		Value: []byte("2"),
   109  	}
   110  
   111  	kr2040 := &topodatapb.KeyRange{
   112  		Start: []byte{0x20},
   113  		End:   []byte{0x40},
   114  	}
   115  	kr80a0 := &topodatapb.KeyRange{
   116  		Start: []byte{0x80},
   117  		End:   []byte{0xa0},
   118  	}
   119  	kr2830 := &topodatapb.KeyRange{
   120  		Start: []byte{0x28},
   121  		End:   []byte{0x30},
   122  	}
   123  
   124  	var testCases = []struct {
   125  		name           string
   126  		keyspace       string
   127  		ids            []*querypb.Value
   128  		destinations   []key.Destination
   129  		errString      string
   130  		expectedShards []string
   131  		expectedValues [][]*querypb.Value
   132  	}{
   133  		{
   134  			name:     "unsharded keyspace, regular shard, no ids",
   135  			keyspace: "uks",
   136  			destinations: []key.Destination{
   137  				key.DestinationShard("0"),
   138  			},
   139  			expectedShards: []string{"0"},
   140  		},
   141  		{
   142  			name:     "unsharded keyspace, regular shard, with ids",
   143  			keyspace: "uks",
   144  			ids:      []*querypb.Value{id1, id2},
   145  			destinations: []key.Destination{
   146  				key.DestinationShard("0"),
   147  				key.DestinationShard("0"),
   148  			},
   149  			expectedShards: []string{"0"},
   150  			expectedValues: [][]*querypb.Value{
   151  				{id1, id2},
   152  			},
   153  		},
   154  		{
   155  			name:     "sharded keyspace, keyrange destinations, with ids",
   156  			keyspace: "sks",
   157  			ids:      []*querypb.Value{id1, id2},
   158  			destinations: []key.Destination{
   159  				key.DestinationExactKeyRange{KeyRange: kr2040},
   160  				key.DestinationExactKeyRange{KeyRange: kr80a0},
   161  			},
   162  			expectedShards: []string{"20-40", "80-a0"},
   163  			expectedValues: [][]*querypb.Value{
   164  				{id1},
   165  				{id2},
   166  			},
   167  		},
   168  		{
   169  			name:     "sharded keyspace, keyspace id destinations, with ids",
   170  			keyspace: "sks",
   171  			ids:      []*querypb.Value{id1, id2},
   172  			destinations: []key.Destination{
   173  				key.DestinationKeyspaceID{0x28},
   174  				key.DestinationKeyspaceID{0x78, 0x23},
   175  			},
   176  			expectedShards: []string{"20-40", "60-80"},
   177  			expectedValues: [][]*querypb.Value{
   178  				{id1},
   179  				{id2},
   180  			},
   181  		},
   182  		{
   183  			name:     "sharded keyspace, multi keyspace id destinations, with ids",
   184  			keyspace: "sks",
   185  			ids:      []*querypb.Value{id1, id2},
   186  			destinations: []key.Destination{
   187  				key.DestinationKeyspaceIDs{
   188  					{0x28},
   189  					{0x47},
   190  				},
   191  				key.DestinationKeyspaceIDs{
   192  					{0x78},
   193  					{0x23},
   194  				},
   195  			},
   196  			expectedShards: []string{"20-40", "40-60", "60-80"},
   197  			expectedValues: [][]*querypb.Value{
   198  				{id1, id2},
   199  				{id1},
   200  				{id2},
   201  			},
   202  		},
   203  		{
   204  			name:     "using non-mapping keyranges should fail",
   205  			keyspace: "sks",
   206  			destinations: []key.Destination{
   207  				key.DestinationExactKeyRange{
   208  					KeyRange: kr2830,
   209  				},
   210  			},
   211  			errString: "keyrange 28-30 does not exactly match shards",
   212  		},
   213  	}
   214  	for _, testCase := range testCases {
   215  		ctx := context.Background()
   216  		rss, values, err := resolver.ResolveDestinations(ctx, testCase.keyspace, topodatapb.TabletType_REPLICA, testCase.ids, testCase.destinations)
   217  		if err != nil {
   218  			if testCase.errString == "" {
   219  				t.Errorf("%v: expected success but got error: %v", testCase.name, err)
   220  			} else {
   221  				if err.Error() != testCase.errString {
   222  					t.Errorf("%v: expected error '%v' but got error: %v", testCase.name, testCase.errString, err)
   223  				}
   224  			}
   225  			continue
   226  		}
   227  
   228  		if testCase.errString != "" {
   229  			t.Errorf("%v: expected error '%v' but got success", testCase.name, testCase.errString)
   230  			continue
   231  		}
   232  
   233  		// Check the ResolvedShard are correct.
   234  		if len(rss) != len(testCase.expectedShards) {
   235  			t.Errorf("%v: expected %v ResolvedShard, but got: %v", testCase.name, len(testCase.expectedShards), rss)
   236  			continue
   237  		}
   238  		badShards := false
   239  		for i, rs := range rss {
   240  			if rs.Target.Shard != testCase.expectedShards[i] {
   241  				t.Errorf("%v: expected rss[%v] to be '%v', but got: %v", testCase.name, i, testCase.expectedShards[i], rs.Target.Shard)
   242  				badShards = true
   243  			}
   244  		}
   245  		if badShards {
   246  			continue
   247  		}
   248  
   249  		// Check the values are correct, if we passed some in.
   250  		if testCase.ids == nil {
   251  			continue
   252  		}
   253  		if len(values) != len(rss) {
   254  			t.Errorf("%v: len(values) != len(rss): %v != %v", testCase.name, len(values), len(rss))
   255  		}
   256  		if !ValuesEqual(values, testCase.expectedValues) {
   257  			t.Errorf("%v: values != testCase.expectedValues: got values=%v", testCase.name, values)
   258  		}
   259  	}
   260  }