vitess.io/vitess@v0.16.2/go/vt/schemamanager/schemamanager_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 schemamanager
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"strings"
    24  	"testing"
    25  
    26  	"vitess.io/vitess/go/vt/logutil"
    27  	"vitess.io/vitess/go/vt/mysqlctl/tmutils"
    28  	"vitess.io/vitess/go/vt/topo"
    29  	"vitess.io/vitess/go/vt/topo/memorytopo"
    30  	"vitess.io/vitess/go/vt/topo/topoproto"
    31  	"vitess.io/vitess/go/vt/vttablet/faketmclient"
    32  	"vitess.io/vitess/go/vt/vttablet/tmclient"
    33  	"vitess.io/vitess/go/vt/vttablet/tmclienttest"
    34  
    35  	querypb "vitess.io/vitess/go/vt/proto/query"
    36  	tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata"
    37  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    38  
    39  	// import the gRPC client implementation for tablet manager
    40  	_ "vitess.io/vitess/go/vt/vttablet/grpctmclient"
    41  )
    42  
    43  var (
    44  	errControllerOpen = errors.New("Open Fail")
    45  	errControllerRead = errors.New("Read Fail")
    46  )
    47  
    48  func init() {
    49  	// enforce we will use the right protocol (gRPC) (note the
    50  	// client is unused, but it is initialized, so it needs to exist)
    51  	tmclienttest.SetProtocol("go.vt.schemamanager", "grpc")
    52  }
    53  
    54  func TestSchemaManagerControllerOpenFail(t *testing.T) {
    55  	controller := newFakeController(
    56  		[]string{"select * from test_db"}, true, false, false)
    57  	ctx := context.Background()
    58  
    59  	_, err := Run(ctx, controller, newFakeExecutor(t))
    60  	if err != errControllerOpen {
    61  		t.Fatalf("controller.Open fail, should get error: %v, but get error: %v",
    62  			errControllerOpen, err)
    63  	}
    64  }
    65  
    66  func TestSchemaManagerControllerReadFail(t *testing.T) {
    67  	controller := newFakeController(
    68  		[]string{"select * from test_db"}, false, true, false)
    69  	ctx := context.Background()
    70  	_, err := Run(ctx, controller, newFakeExecutor(t))
    71  	if err != errControllerRead {
    72  		t.Fatalf("controller.Read fail, should get error: %v, but get error: %v",
    73  			errControllerRead, err)
    74  	}
    75  	if !controller.onReadFailTriggered {
    76  		t.Fatalf("OnReadFail should be called")
    77  	}
    78  }
    79  
    80  func TestSchemaManagerValidationFail(t *testing.T) {
    81  	controller := newFakeController(
    82  		[]string{"invalid sql"}, false, false, false)
    83  	ctx := context.Background()
    84  
    85  	_, err := Run(ctx, controller, newFakeExecutor(t))
    86  	if err == nil || !strings.Contains(err.Error(), "failed to parse sql") {
    87  		t.Fatalf("run schema change should fail due to executor.Validate fail, but got: %v", err)
    88  	}
    89  }
    90  
    91  func TestSchemaManagerExecutorOpenFail(t *testing.T) {
    92  	controller := newFakeController(
    93  		[]string{"create table test_table (pk int);"}, false, false, false)
    94  	controller.SetKeyspace("unknown_keyspace")
    95  	executor := NewTabletExecutor("TestSchemaManagerExecutorOpenFail", newFakeTopo(t), newFakeTabletManagerClient(), logutil.NewConsoleLogger(), testWaitReplicasTimeout)
    96  	ctx := context.Background()
    97  
    98  	_, err := Run(ctx, controller, executor)
    99  	if err == nil || !strings.Contains(err.Error(), "unknown_keyspace") {
   100  		t.Fatalf("run schema change should fail due to executor.Open fail, but got: %v", err)
   101  	}
   102  }
   103  
   104  func TestSchemaManagerExecutorExecuteFail(t *testing.T) {
   105  	controller := newFakeController(
   106  		[]string{"create table test_table (pk int);"}, false, false, false)
   107  	executor := NewTabletExecutor("TestSchemaManagerExecutorExecuteFail", newFakeTopo(t), newFakeTabletManagerClient(), logutil.NewConsoleLogger(), testWaitReplicasTimeout)
   108  	ctx := context.Background()
   109  
   110  	_, err := Run(ctx, controller, executor)
   111  	if err == nil || !strings.Contains(err.Error(), "unknown database: vt_test_keyspace") {
   112  		t.Fatalf("run schema change should fail due to executor.Execute fail, but got: %v", err)
   113  	}
   114  }
   115  
   116  func TestSchemaManagerRun(t *testing.T) {
   117  	sql := "create table test_table (pk int)"
   118  	controller := newFakeController(
   119  		[]string{sql}, false, false, false)
   120  	fakeTmc := newFakeTabletManagerClient()
   121  	fakeTmc.AddSchemaChange(sql, &tabletmanagerdatapb.SchemaChangeResult{
   122  		BeforeSchema: &tabletmanagerdatapb.SchemaDefinition{},
   123  		AfterSchema: &tabletmanagerdatapb.SchemaDefinition{
   124  			DatabaseSchema: "CREATE DATABASE `{{.DatabaseName}}` /*!40100 DEFAULT CHARACTER SET utf8 */",
   125  			TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
   126  				{
   127  					Name:   "test_table",
   128  					Schema: sql,
   129  					Type:   tmutils.TableBaseTable,
   130  				},
   131  			},
   132  		},
   133  	})
   134  
   135  	fakeTmc.AddSchemaDefinition("vt_test_keyspace", &tabletmanagerdatapb.SchemaDefinition{})
   136  	executor := NewTabletExecutor("TestSchemaManagerRun", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout)
   137  
   138  	ctx := context.Background()
   139  	resp, err := Run(ctx, controller, executor)
   140  
   141  	if len(resp.UUIDs) > 0 {
   142  		t.Fatalf("response should contain an empty list of UUIDs, found %v", len(resp.UUIDs))
   143  	}
   144  
   145  	if err != nil {
   146  		t.Fatalf("schema change should success but get error: %v", err)
   147  	}
   148  	if !controller.onReadSuccessTriggered {
   149  		t.Fatalf("OnReadSuccess should be called")
   150  	}
   151  	if controller.onReadFailTriggered {
   152  		t.Fatalf("OnReadFail should not be called")
   153  	}
   154  	if !controller.onValidationSuccessTriggered {
   155  		t.Fatalf("OnValidateSuccess should be called")
   156  	}
   157  	if controller.onValidationFailTriggered {
   158  		t.Fatalf("OnValidationFail should not be called")
   159  	}
   160  	if !controller.onExecutorCompleteTriggered {
   161  		t.Fatalf("OnExecutorComplete should be called")
   162  	}
   163  }
   164  
   165  func TestSchemaManagerExecutorFail(t *testing.T) {
   166  	sql := "create table test_table (pk int)"
   167  	controller := newFakeController([]string{sql}, false, false, false)
   168  	fakeTmc := newFakeTabletManagerClient()
   169  	fakeTmc.AddSchemaChange(sql, &tabletmanagerdatapb.SchemaChangeResult{
   170  		BeforeSchema: &tabletmanagerdatapb.SchemaDefinition{},
   171  		AfterSchema: &tabletmanagerdatapb.SchemaDefinition{
   172  			DatabaseSchema: "CREATE DATABASE `{{.DatabaseName}}` /*!40100 DEFAULT CHARACTER SET utf8 */",
   173  			TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
   174  				{
   175  					Name:   "test_table",
   176  					Schema: sql,
   177  					Type:   tmutils.TableBaseTable,
   178  				},
   179  			},
   180  		},
   181  	})
   182  
   183  	fakeTmc.AddSchemaDefinition("vt_test_keyspace", &tabletmanagerdatapb.SchemaDefinition{})
   184  	fakeTmc.EnableExecuteFetchAsDbaError = true
   185  	executor := NewTabletExecutor("TestSchemaManagerExecutorFail", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout)
   186  
   187  	ctx := context.Background()
   188  	resp, err := Run(ctx, controller, executor)
   189  	if len(resp.UUIDs) > 0 {
   190  		t.Fatalf("response should contain an empty list of UUIDs, found %v", len(resp.UUIDs))
   191  	}
   192  
   193  	if err == nil || !strings.Contains(err.Error(), "schema change failed") {
   194  		t.Fatalf("schema change should fail, but got err: %v", err)
   195  	}
   196  }
   197  
   198  func TestSchemaManagerRegisterControllerFactory(t *testing.T) {
   199  	sql := "create table test_table (pk int)"
   200  	RegisterControllerFactory(
   201  		"test_controller",
   202  		func(params map[string]string) (Controller, error) {
   203  			return newFakeController([]string{sql}, false, false, false), nil
   204  
   205  		})
   206  
   207  	_, err := GetControllerFactory("unknown")
   208  	if err == nil || !strings.Contains(err.Error(), "there is no data sourcer factory") {
   209  		t.Fatalf("controller factory is not registered, GetControllerFactory should return an error, but got: %v", err)
   210  	}
   211  	_, err = GetControllerFactory("test_controller")
   212  	if err != nil {
   213  		t.Fatalf("GetControllerFactory should succeed, but get an error: %v", err)
   214  	}
   215  	func() {
   216  		defer func() {
   217  			err := recover()
   218  			if err == nil {
   219  				t.Fatalf("RegisterControllerFactory should fail, it registers a registered ControllerFactory")
   220  			}
   221  		}()
   222  		RegisterControllerFactory(
   223  			"test_controller",
   224  			func(params map[string]string) (Controller, error) {
   225  				return newFakeController([]string{sql}, false, false, false), nil
   226  
   227  			})
   228  	}()
   229  }
   230  
   231  func newFakeExecutor(t *testing.T) *TabletExecutor {
   232  	return NewTabletExecutor("newFakeExecutor", newFakeTopo(t), newFakeTabletManagerClient(), logutil.NewConsoleLogger(), testWaitReplicasTimeout)
   233  }
   234  
   235  func newFakeTabletManagerClient() *fakeTabletManagerClient {
   236  	return &fakeTabletManagerClient{
   237  		TabletManagerClient: faketmclient.NewFakeTabletManagerClient(),
   238  		preflightSchemas:    make(map[string]*tabletmanagerdatapb.SchemaChangeResult),
   239  		schemaDefinitions:   make(map[string]*tabletmanagerdatapb.SchemaDefinition),
   240  	}
   241  }
   242  
   243  type fakeTabletManagerClient struct {
   244  	tmclient.TabletManagerClient
   245  	EnableExecuteFetchAsDbaError bool
   246  	preflightSchemas             map[string]*tabletmanagerdatapb.SchemaChangeResult
   247  	schemaDefinitions            map[string]*tabletmanagerdatapb.SchemaDefinition
   248  }
   249  
   250  func (client *fakeTabletManagerClient) AddSchemaChange(sql string, schemaResult *tabletmanagerdatapb.SchemaChangeResult) {
   251  	client.preflightSchemas[sql] = schemaResult
   252  }
   253  
   254  func (client *fakeTabletManagerClient) AddSchemaDefinition(
   255  	dbName string, schemaDefinition *tabletmanagerdatapb.SchemaDefinition) {
   256  	client.schemaDefinitions[dbName] = schemaDefinition
   257  }
   258  
   259  func (client *fakeTabletManagerClient) PreflightSchema(ctx context.Context, tablet *topodatapb.Tablet, changes []string) ([]*tabletmanagerdatapb.SchemaChangeResult, error) {
   260  	var result []*tabletmanagerdatapb.SchemaChangeResult
   261  	for _, change := range changes {
   262  		scr, ok := client.preflightSchemas[change]
   263  		if ok {
   264  			result = append(result, scr)
   265  		} else {
   266  			result = append(result, &tabletmanagerdatapb.SchemaChangeResult{})
   267  		}
   268  	}
   269  	return result, nil
   270  }
   271  
   272  func (client *fakeTabletManagerClient) GetSchema(ctx context.Context, tablet *topodatapb.Tablet, request *tabletmanagerdatapb.GetSchemaRequest) (*tabletmanagerdatapb.SchemaDefinition, error) {
   273  	result, ok := client.schemaDefinitions[topoproto.TabletDbName(tablet)]
   274  	if !ok {
   275  		return nil, fmt.Errorf("unknown database: %s", topoproto.TabletDbName(tablet))
   276  	}
   277  	return result, nil
   278  }
   279  
   280  func (client *fakeTabletManagerClient) ExecuteFetchAsDba(ctx context.Context, tablet *topodatapb.Tablet, usePool bool, req *tabletmanagerdatapb.ExecuteFetchAsDbaRequest) (*querypb.QueryResult, error) {
   281  	if client.EnableExecuteFetchAsDbaError {
   282  		return nil, fmt.Errorf("ExecuteFetchAsDba occur an unknown error")
   283  	}
   284  	return client.TabletManagerClient.ExecuteFetchAsDba(ctx, tablet, usePool, req)
   285  }
   286  
   287  // newFakeTopo returns a topo with:
   288  // - a keyspace named 'test_keyspace'.
   289  // - 3 shards named '1', '2', '3'.
   290  // - A primary tablet for each shard.
   291  func newFakeTopo(t *testing.T) *topo.Server {
   292  	ts := memorytopo.NewServer("test_cell")
   293  	ctx := context.Background()
   294  	if err := ts.CreateKeyspace(ctx, "test_keyspace", &topodatapb.Keyspace{}); err != nil {
   295  		t.Fatalf("CreateKeyspace failed: %v", err)
   296  	}
   297  	for i, shard := range []string{"0", "1", "2"} {
   298  		if err := ts.CreateShard(ctx, "test_keyspace", shard); err != nil {
   299  			t.Fatalf("CreateShard(%v) failed: %v", shard, err)
   300  		}
   301  		tablet := &topodatapb.Tablet{
   302  			Alias: &topodatapb.TabletAlias{
   303  				Cell: "test_cell",
   304  				Uid:  uint32(i + 1),
   305  			},
   306  			Keyspace: "test_keyspace",
   307  			Shard:    shard,
   308  		}
   309  		if err := ts.CreateTablet(ctx, tablet); err != nil {
   310  			t.Fatalf("CreateTablet failed: %v", err)
   311  		}
   312  		if _, err := ts.UpdateShardFields(ctx, "test_keyspace", shard, func(si *topo.ShardInfo) error {
   313  			si.Shard.PrimaryAlias = tablet.Alias
   314  			return nil
   315  		}); err != nil {
   316  			t.Fatalf("UpdateShardFields failed: %v", err)
   317  		}
   318  	}
   319  	if err := ts.CreateKeyspace(ctx, "unsharded_keyspace", &topodatapb.Keyspace{}); err != nil {
   320  		t.Fatalf("CreateKeyspace failed: %v", err)
   321  	}
   322  	if err := ts.CreateShard(ctx, "unsharded_keyspace", "0"); err != nil {
   323  		t.Fatalf("CreateShard(%v) failed: %v", "0", err)
   324  	}
   325  	tablet := &topodatapb.Tablet{
   326  		Alias: &topodatapb.TabletAlias{
   327  			Cell: "test_cell",
   328  			Uid:  uint32(4),
   329  		},
   330  		Keyspace: "test_keyspace",
   331  		Shard:    "0",
   332  	}
   333  	if err := ts.CreateTablet(ctx, tablet); err != nil {
   334  		t.Fatalf("CreateTablet failed: %v", err)
   335  	}
   336  	if _, err := ts.UpdateShardFields(ctx, "unsharded_keyspace", "0", func(si *topo.ShardInfo) error {
   337  		si.Shard.PrimaryAlias = tablet.Alias
   338  		return nil
   339  	}); err != nil {
   340  		t.Fatalf("UpdateShardFields failed: %v", err)
   341  	}
   342  	return ts
   343  }
   344  
   345  type fakeController struct {
   346  	sqls                         []string
   347  	keyspace                     string
   348  	openFail                     bool
   349  	readFail                     bool
   350  	closeFail                    bool
   351  	onReadSuccessTriggered       bool
   352  	onReadFailTriggered          bool
   353  	onValidationSuccessTriggered bool
   354  	onValidationFailTriggered    bool
   355  	onExecutorCompleteTriggered  bool
   356  }
   357  
   358  func newFakeController(
   359  	sqls []string, openFail bool, readFail bool, closeFail bool) *fakeController {
   360  	return &fakeController{
   361  		sqls:      sqls,
   362  		keyspace:  "test_keyspace",
   363  		openFail:  openFail,
   364  		readFail:  readFail,
   365  		closeFail: closeFail,
   366  	}
   367  }
   368  
   369  func (controller *fakeController) SetKeyspace(keyspace string) {
   370  	controller.keyspace = keyspace
   371  }
   372  
   373  func (controller *fakeController) Open(ctx context.Context) error {
   374  	if controller.openFail {
   375  		return errControllerOpen
   376  	}
   377  	return nil
   378  }
   379  
   380  func (controller *fakeController) Read(ctx context.Context) ([]string, error) {
   381  	if controller.readFail {
   382  		return nil, errControllerRead
   383  	}
   384  	return controller.sqls, nil
   385  }
   386  
   387  func (controller *fakeController) Close() {
   388  }
   389  
   390  func (controller *fakeController) Keyspace() string {
   391  	return controller.keyspace
   392  }
   393  
   394  func (controller *fakeController) OnReadSuccess(ctx context.Context) error {
   395  	controller.onReadSuccessTriggered = true
   396  	return nil
   397  }
   398  
   399  func (controller *fakeController) OnReadFail(ctx context.Context, err error) error {
   400  	controller.onReadFailTriggered = true
   401  	return err
   402  }
   403  
   404  func (controller *fakeController) OnValidationSuccess(ctx context.Context) error {
   405  	controller.onValidationSuccessTriggered = true
   406  	return nil
   407  }
   408  
   409  func (controller *fakeController) OnValidationFail(ctx context.Context, err error) error {
   410  	controller.onValidationFailTriggered = true
   411  	return err
   412  }
   413  
   414  func (controller *fakeController) OnExecutorComplete(ctx context.Context, result *ExecuteResult) error {
   415  	controller.onExecutorCompleteTriggered = true
   416  	return nil
   417  }
   418  
   419  var _ Controller = (*fakeController)(nil)