vitess.io/vitess@v0.16.2/go/vt/vtctl/schematools/reload_test.go (about)

     1  /*
     2  Copyright 2021 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 schematools
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"sync"
    23  	"testing"
    24  
    25  	"github.com/stretchr/testify/assert"
    26  
    27  	"vitess.io/vitess/go/sync2"
    28  	"vitess.io/vitess/go/vt/logutil"
    29  	"vitess.io/vitess/go/vt/topo/memorytopo"
    30  	"vitess.io/vitess/go/vt/topo/topoproto"
    31  	"vitess.io/vitess/go/vt/vtctl/grpcvtctldserver/testutil"
    32  	"vitess.io/vitess/go/vt/vttablet/tmclient"
    33  
    34  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    35  )
    36  
    37  type reloadSchemaTMC struct {
    38  	tmclient.TabletManagerClient
    39  
    40  	// tablet alias => positionStr => error
    41  	results map[string]map[string]error
    42  
    43  	m        sync.Mutex
    44  	calls    []*reloadSchemaCall
    45  	errCount int
    46  }
    47  
    48  type reloadSchemaCall struct {
    49  	Alias    *topodatapb.TabletAlias
    50  	Position string
    51  }
    52  
    53  func (tmc *reloadSchemaTMC) ReloadSchema(ctx context.Context, tablet *topodatapb.Tablet, position string) (err error) {
    54  	rpcErr := err
    55  	defer func() {
    56  		tmc.m.Lock()
    57  		defer tmc.m.Unlock()
    58  
    59  		tmc.calls = append(tmc.calls, &reloadSchemaCall{
    60  			Alias:    tablet.Alias,
    61  			Position: position,
    62  		})
    63  
    64  		if rpcErr != nil {
    65  			tmc.errCount++
    66  		}
    67  	}()
    68  
    69  	if tmc.results == nil {
    70  		return fmt.Errorf("no results set on reloadSchemaTMC")
    71  	}
    72  
    73  	key := topoproto.TabletAliasString(tablet.Alias)
    74  	posMap, ok := tmc.results[key]
    75  	if !ok {
    76  		return fmt.Errorf("no ReloadSchema fakes set for %s", key)
    77  	}
    78  
    79  	rpcErr, ok = posMap[position]
    80  	if !ok {
    81  		return fmt.Errorf("no ReloadSchema fake set for (%s, %s)", key, position)
    82  	}
    83  
    84  	return rpcErr
    85  }
    86  
    87  func TestReloadShard(t *testing.T) {
    88  	t.Parallel()
    89  	ctx := context.Background()
    90  
    91  	tests := []struct {
    92  		name    string
    93  		cells   []string
    94  		tablets []*topodatapb.Tablet
    95  		results map[string]map[string]error
    96  		req     *struct {
    97  			Keyspace       string
    98  			Shard          string
    99  			Position       string
   100  			IncludePrimary bool
   101  		}
   102  		expected *struct {
   103  			IsPartial bool
   104  			Ok        bool
   105  		}
   106  		expectedCalls    []*reloadSchemaCall
   107  		expectedErrCount int
   108  	}{
   109  		{
   110  			name:  "ok",
   111  			cells: []string{"zone1", "zone2"},
   112  			tablets: []*topodatapb.Tablet{
   113  				{
   114  					Alias: &topodatapb.TabletAlias{
   115  						Cell: "zone1",
   116  						Uid:  100,
   117  					},
   118  					Keyspace: "ks",
   119  					Shard:    "-",
   120  					Type:     topodatapb.TabletType_PRIMARY,
   121  				},
   122  				{
   123  					Alias: &topodatapb.TabletAlias{
   124  						Cell: "zone1",
   125  						Uid:  101,
   126  					},
   127  					Keyspace: "ks",
   128  					Shard:    "-",
   129  					Type:     topodatapb.TabletType_REPLICA,
   130  				},
   131  				{
   132  					Alias: &topodatapb.TabletAlias{
   133  						Cell: "zone2",
   134  						Uid:  200,
   135  					},
   136  					Keyspace: "ks",
   137  					Shard:    "-",
   138  					Type:     topodatapb.TabletType_REPLICA,
   139  				},
   140  			},
   141  			results: map[string]map[string]error{
   142  				"zone1-0000000101": {
   143  					"pos1": nil,
   144  				},
   145  				"zone2-0000000200": {
   146  					"pos1": nil,
   147  				},
   148  			},
   149  			req: &struct {
   150  				Keyspace       string
   151  				Shard          string
   152  				Position       string
   153  				IncludePrimary bool
   154  			}{
   155  				Keyspace: "ks",
   156  				Shard:    "-",
   157  				Position: "pos1",
   158  			},
   159  			expected: &struct {
   160  				IsPartial bool
   161  				Ok        bool
   162  			}{
   163  				IsPartial: false,
   164  				Ok:        true,
   165  			},
   166  			expectedCalls: []*reloadSchemaCall{
   167  				{
   168  					Alias: &topodatapb.TabletAlias{
   169  						Cell: "zone1",
   170  						Uid:  101,
   171  					},
   172  					Position: "pos1",
   173  				},
   174  				{
   175  					Alias: &topodatapb.TabletAlias{
   176  						Cell: "zone2",
   177  						Uid:  200,
   178  					},
   179  					Position: "pos1",
   180  				},
   181  			},
   182  			expectedErrCount: 0,
   183  		},
   184  		{
   185  			name:  "best effort",
   186  			cells: []string{"zone1", "zone2"},
   187  			tablets: []*topodatapb.Tablet{
   188  				{
   189  					Alias: &topodatapb.TabletAlias{
   190  						Cell: "zone1",
   191  						Uid:  100,
   192  					},
   193  					Keyspace: "ks",
   194  					Shard:    "-",
   195  					Type:     topodatapb.TabletType_PRIMARY,
   196  				},
   197  				{
   198  					Alias: &topodatapb.TabletAlias{
   199  						Cell: "zone1",
   200  						Uid:  101,
   201  					},
   202  					Keyspace: "ks",
   203  					Shard:    "-",
   204  					Type:     topodatapb.TabletType_REPLICA,
   205  				},
   206  				{
   207  					Alias: &topodatapb.TabletAlias{
   208  						Cell: "zone2",
   209  						Uid:  200,
   210  					},
   211  					Keyspace: "ks",
   212  					Shard:    "-",
   213  					Type:     topodatapb.TabletType_REPLICA,
   214  				},
   215  			},
   216  			results: map[string]map[string]error{
   217  				"zone1-0000000101": {
   218  					"pos1": nil,
   219  				},
   220  				"zone2-0000000200": {
   221  					"pos1": assert.AnError,
   222  				},
   223  			},
   224  			req: &struct {
   225  				Keyspace       string
   226  				Shard          string
   227  				Position       string
   228  				IncludePrimary bool
   229  			}{
   230  				Keyspace: "ks",
   231  				Shard:    "-",
   232  				Position: "pos1",
   233  			},
   234  			expected: &struct {
   235  				IsPartial bool
   236  				Ok        bool
   237  			}{
   238  				IsPartial: false,
   239  				Ok:        true,
   240  			},
   241  			expectedCalls: []*reloadSchemaCall{
   242  				{
   243  					Alias: &topodatapb.TabletAlias{
   244  						Cell: "zone1",
   245  						Uid:  101,
   246  					},
   247  					Position: "pos1",
   248  				},
   249  				{
   250  					Alias: &topodatapb.TabletAlias{
   251  						Cell: "zone2",
   252  						Uid:  200,
   253  					},
   254  					Position: "pos1",
   255  				},
   256  			},
   257  			expectedErrCount: 1,
   258  		},
   259  		{
   260  			name:  "include_primary",
   261  			cells: []string{"zone1"},
   262  			tablets: []*topodatapb.Tablet{
   263  				{
   264  					Alias: &topodatapb.TabletAlias{
   265  						Cell: "zone1",
   266  						Uid:  100,
   267  					},
   268  					Keyspace: "ks",
   269  					Shard:    "-",
   270  					Type:     topodatapb.TabletType_PRIMARY,
   271  				},
   272  			},
   273  			results: map[string]map[string]error{
   274  				"zone1-0000000100": {
   275  					"":              nil,
   276  					"some position": assert.AnError,
   277  				},
   278  			},
   279  			req: &struct {
   280  				Keyspace       string
   281  				Shard          string
   282  				Position       string
   283  				IncludePrimary bool
   284  			}{
   285  				Keyspace:       "ks",
   286  				Shard:          "-",
   287  				Position:       "some position",
   288  				IncludePrimary: true,
   289  			},
   290  			expected: &struct {
   291  				IsPartial bool
   292  				Ok        bool
   293  			}{
   294  				IsPartial: false,
   295  				Ok:        true,
   296  			},
   297  			expectedCalls: []*reloadSchemaCall{
   298  				{
   299  					Alias: &topodatapb.TabletAlias{
   300  						Cell: "zone1",
   301  						Uid:  100,
   302  					},
   303  					Position: "",
   304  				},
   305  			},
   306  		},
   307  		{
   308  			name: "fail to load tabletmap",
   309  			req: &struct {
   310  				Keyspace       string
   311  				Shard          string
   312  				Position       string
   313  				IncludePrimary bool
   314  			}{
   315  				Keyspace: "doesnotexist",
   316  				Shard:    "-",
   317  			},
   318  			expected: &struct {
   319  				IsPartial bool
   320  				Ok        bool
   321  			}{
   322  				IsPartial: false,
   323  				Ok:        false,
   324  			},
   325  		},
   326  	}
   327  
   328  	for _, tt := range tests {
   329  		tt := tt
   330  		t.Run(tt.name, func(t *testing.T) {
   331  			t.Parallel()
   332  
   333  			ts := memorytopo.NewServer(tt.cells...)
   334  			testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{
   335  				AlsoSetShardPrimary: true,
   336  			}, tt.tablets...)
   337  			tmc := &reloadSchemaTMC{
   338  				results: tt.results,
   339  			}
   340  
   341  			isPartial, ok := ReloadShard(ctx, ts, tmc, logutil.NewMemoryLogger(), tt.req.Keyspace, tt.req.Shard, tt.req.Position, sync2.NewSemaphore(1, 0), tt.req.IncludePrimary)
   342  			assert.Equal(t, tt.expected.IsPartial, isPartial, "incorrect value for isPartial")
   343  			assert.Equal(t, tt.expected.Ok, ok, "incorrect value for ok")
   344  
   345  			tmc.m.Lock()
   346  			defer tmc.m.Unlock()
   347  
   348  			assert.ElementsMatch(t, tt.expectedCalls, tmc.calls)
   349  			assert.Equal(t, tt.expectedErrCount, tmc.errCount, "rpc error count incorrect")
   350  		})
   351  	}
   352  }