vitess.io/vitess@v0.16.2/go/vt/schemamanager/tablet_executor_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  	"strings"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/stretchr/testify/assert"
    26  
    27  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    28  	"vitess.io/vitess/go/vt/topo/memorytopo"
    29  
    30  	"vitess.io/vitess/go/vt/logutil"
    31  	"vitess.io/vitess/go/vt/mysqlctl/tmutils"
    32  	tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata"
    33  	"vitess.io/vitess/go/vt/schema"
    34  	"vitess.io/vitess/go/vt/sqlparser"
    35  )
    36  
    37  var (
    38  	testWaitReplicasTimeout = 10 * time.Second
    39  )
    40  
    41  func TestTabletExecutorOpen(t *testing.T) {
    42  	executor := newFakeExecutor(t)
    43  	ctx := context.Background()
    44  
    45  	if err := executor.Open(ctx, "test_keyspace"); err != nil {
    46  		t.Fatalf("executor.Open should succeed")
    47  	}
    48  
    49  	defer executor.Close()
    50  
    51  	if err := executor.Open(ctx, "test_keyspace"); err != nil {
    52  		t.Fatalf("open an opened executor should also succeed")
    53  	}
    54  }
    55  
    56  func TestTabletExecutorOpenWithEmptyPrimaryAlias(t *testing.T) {
    57  	ctx := context.Background()
    58  	ts := memorytopo.NewServer("test_cell")
    59  	tablet := &topodatapb.Tablet{
    60  		Alias: &topodatapb.TabletAlias{
    61  			Cell: "test_cell",
    62  			Uid:  1,
    63  		},
    64  		Keyspace: "test_keyspace",
    65  		Shard:    "0",
    66  		Type:     topodatapb.TabletType_REPLICA,
    67  	}
    68  	// This will create the Keyspace, Shard and Tablet record.
    69  	// Since this is a replica tablet, the Shard will have no primary.
    70  	if err := ts.InitTablet(ctx, tablet, false /*allowPrimaryOverride*/, true /*createShardAndKeyspace*/, false /*allowUpdate*/); err != nil {
    71  		t.Fatalf("InitTablet failed: %v", err)
    72  	}
    73  	executor := NewTabletExecutor("TestTabletExecutorOpenWithEmptyPrimaryAlias", ts, newFakeTabletManagerClient(), logutil.NewConsoleLogger(), testWaitReplicasTimeout)
    74  	if err := executor.Open(ctx, "test_keyspace"); err == nil || !strings.Contains(err.Error(), "does not have a primary") {
    75  		t.Fatalf("executor.Open() = '%v', want error", err)
    76  	}
    77  	executor.Close()
    78  }
    79  
    80  func TestTabletExecutorValidate(t *testing.T) {
    81  	fakeTmc := newFakeTabletManagerClient()
    82  
    83  	fakeTmc.AddSchemaDefinition("vt_test_keyspace", &tabletmanagerdatapb.SchemaDefinition{
    84  		DatabaseSchema: "CREATE DATABASE `{{.DatabaseName}}` /*!40100 DEFAULT CHARACTER SET utf8 */",
    85  		TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
    86  			{
    87  				Name:   "test_table",
    88  				Schema: "table schema",
    89  				Type:   tmutils.TableBaseTable,
    90  			},
    91  			{
    92  				Name:     "test_table_03",
    93  				Schema:   "table schema",
    94  				Type:     tmutils.TableBaseTable,
    95  				RowCount: 200000,
    96  			},
    97  			{
    98  				Name:     "test_table_04",
    99  				Schema:   "table schema",
   100  				Type:     tmutils.TableBaseTable,
   101  				RowCount: 3000000,
   102  			},
   103  		},
   104  	})
   105  
   106  	executor := NewTabletExecutor("TestTabletExecutorValidate", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout)
   107  	ctx := context.Background()
   108  
   109  	sqls := []string{
   110  		"ALTER TABLE test_table ADD COLUMN new_id bigint(20)",
   111  		"CREATE TABLE test_table_02 (pk int)",
   112  		"ALTER DATABASE db_name DEFAULT CHARACTER SET = utf8mb4",
   113  		"ALTER SCHEMA db_name CHARACTER SET = utf8mb4",
   114  	}
   115  
   116  	if err := executor.Validate(ctx, sqls); err == nil {
   117  		t.Fatalf("validate should fail because executor is closed")
   118  	}
   119  
   120  	executor.Open(ctx, "test_keyspace")
   121  	defer executor.Close()
   122  
   123  	// schema changes with DMLs should fail
   124  	if err := executor.Validate(ctx, []string{
   125  		"INSERT INTO test_table VALUES(1)"}); err == nil {
   126  		t.Fatalf("schema changes are for DDLs")
   127  	}
   128  
   129  	// validates valid ddls
   130  	if err := executor.Validate(ctx, sqls); err != nil {
   131  		t.Fatalf("executor.Validate should succeed, but got error: %v", err)
   132  	}
   133  
   134  	// alter a table with more than 100,000 rows
   135  	if err := executor.Validate(ctx, []string{
   136  		"ALTER TABLE test_table_03 ADD COLUMN new_id bigint(20)",
   137  	}); err == nil {
   138  		t.Fatalf("executor.Validate should fail, alter a table more than 100,000 rows")
   139  	}
   140  
   141  	if err := executor.Validate(ctx, []string{
   142  		"TRUNCATE TABLE test_table_04",
   143  	}); err != nil {
   144  		t.Fatalf("executor.Validate should succeed, drop a table with more than 2,000,000 rows is allowed")
   145  	}
   146  
   147  	if err := executor.Validate(ctx, []string{
   148  		"DROP TABLE test_table_04",
   149  	}); err != nil {
   150  		t.Fatalf("executor.Validate should succeed, drop a table with more than 2,000,000 rows is allowed")
   151  	}
   152  
   153  	executor.AllowBigSchemaChange()
   154  	// alter a table with more than 100,000 rows
   155  	if err := executor.Validate(ctx, []string{
   156  		"ALTER TABLE test_table_03 ADD COLUMN new_id bigint(20)",
   157  	}); err != nil {
   158  		t.Fatalf("executor.Validate should succeed, big schema change is disabled")
   159  	}
   160  
   161  	executor.DisallowBigSchemaChange()
   162  	if err := executor.Validate(ctx, []string{
   163  		"ALTER TABLE test_table_03 ADD COLUMN new_id bigint(20)",
   164  	}); err == nil {
   165  		t.Fatalf("executor.Validate should fail, alter a table more than 100,000 rows")
   166  	}
   167  }
   168  
   169  func TestTabletExecutorDML(t *testing.T) {
   170  	fakeTmc := newFakeTabletManagerClient()
   171  
   172  	fakeTmc.AddSchemaDefinition("vt_test_keyspace", &tabletmanagerdatapb.SchemaDefinition{
   173  		DatabaseSchema: "CREATE DATABASE `{{.DatabaseName}}` /*!40100 DEFAULT CHARACTER SET utf8 */",
   174  		TableDefinitions: []*tabletmanagerdatapb.TableDefinition{
   175  			{
   176  				Name:   "test_table",
   177  				Schema: "table schema",
   178  				Type:   tmutils.TableBaseTable,
   179  			},
   180  			{
   181  				Name:     "test_table_03",
   182  				Schema:   "table schema",
   183  				Type:     tmutils.TableBaseTable,
   184  				RowCount: 200000,
   185  			},
   186  			{
   187  				Name:     "test_table_04",
   188  				Schema:   "table schema",
   189  				Type:     tmutils.TableBaseTable,
   190  				RowCount: 3000000,
   191  			},
   192  		},
   193  	})
   194  
   195  	executor := NewTabletExecutor("TestTabletExecutorDML", newFakeTopo(t), fakeTmc, logutil.NewConsoleLogger(), testWaitReplicasTimeout)
   196  	ctx := context.Background()
   197  
   198  	executor.Open(ctx, "unsharded_keyspace")
   199  	defer executor.Close()
   200  
   201  	// schema changes with DMLs should fail
   202  	if err := executor.Validate(ctx, []string{
   203  		"INSERT INTO test_table VALUES(1)"}); err != nil {
   204  		t.Fatalf("executor.Validate should succeed, for DML to unsharded keyspace")
   205  	}
   206  }
   207  
   208  func TestTabletExecutorExecute(t *testing.T) {
   209  	executor := newFakeExecutor(t)
   210  	ctx := context.Background()
   211  
   212  	sqls := []string{"DROP TABLE unknown_table"}
   213  
   214  	result := executor.Execute(ctx, sqls)
   215  	if result.ExecutorErr == "" {
   216  		t.Fatalf("execute should fail, call execute.Open first")
   217  	}
   218  }
   219  
   220  func TestIsOnlineSchemaDDL(t *testing.T) {
   221  	tt := []struct {
   222  		query       string
   223  		ddlStrategy string
   224  		isOnlineDDL bool
   225  		strategy    schema.DDLStrategy
   226  		options     string
   227  	}{
   228  		{
   229  			query:       "CREATE TABLE t(id int)",
   230  			isOnlineDDL: false,
   231  		},
   232  		{
   233  			query:       "CREATE TABLE t(id int)",
   234  			ddlStrategy: "gh-ost",
   235  			isOnlineDDL: true,
   236  			strategy:    schema.DDLStrategyGhost,
   237  		},
   238  		{
   239  			query:       "ALTER TABLE t ADD COLUMN i INT",
   240  			ddlStrategy: "online",
   241  			isOnlineDDL: true,
   242  			strategy:    schema.DDLStrategyOnline,
   243  		},
   244  		{
   245  			query:       "ALTER TABLE t ADD COLUMN i INT",
   246  			ddlStrategy: "vitess",
   247  			isOnlineDDL: true,
   248  			strategy:    schema.DDLStrategyVitess,
   249  		},
   250  		{
   251  			query:       "ALTER TABLE t ADD COLUMN i INT",
   252  			ddlStrategy: "",
   253  			isOnlineDDL: false,
   254  		},
   255  		{
   256  			query:       "ALTER TABLE t ADD COLUMN i INT",
   257  			ddlStrategy: "gh-ost",
   258  			isOnlineDDL: true,
   259  			strategy:    schema.DDLStrategyGhost,
   260  		},
   261  		{
   262  			query:       "ALTER TABLE t ADD COLUMN i INT",
   263  			ddlStrategy: "gh-ost --max-load=Threads_running=100",
   264  			isOnlineDDL: true,
   265  			strategy:    schema.DDLStrategyGhost,
   266  			options:     "--max-load=Threads_running=100",
   267  		},
   268  		{
   269  			query:       "TRUNCATE TABLE t",
   270  			ddlStrategy: "online",
   271  			isOnlineDDL: false,
   272  		},
   273  		{
   274  			query:       "TRUNCATE TABLE t",
   275  			ddlStrategy: "gh-ost",
   276  			isOnlineDDL: false,
   277  		},
   278  		{
   279  			query:       "RENAME TABLE t to t2",
   280  			ddlStrategy: "gh-ost",
   281  			isOnlineDDL: false,
   282  		},
   283  	}
   284  
   285  	for _, ts := range tt {
   286  		e := &TabletExecutor{}
   287  		err := e.SetDDLStrategy(ts.ddlStrategy)
   288  		assert.NoError(t, err)
   289  
   290  		stmt, err := sqlparser.Parse(ts.query)
   291  		assert.NoError(t, err)
   292  
   293  		ddlStmt, ok := stmt.(sqlparser.DDLStatement)
   294  		assert.True(t, ok)
   295  
   296  		isOnlineDDL := e.isOnlineSchemaDDL(ddlStmt)
   297  		assert.Equal(t, ts.isOnlineDDL, isOnlineDDL)
   298  		if isOnlineDDL {
   299  			assert.Equal(t, ts.strategy, e.ddlStrategySetting.Strategy)
   300  			assert.Equal(t, ts.options, e.ddlStrategySetting.Options)
   301  		}
   302  	}
   303  }