vitess.io/vitess@v0.16.2/go/test/endtoend/sharded/sharded_keyspace_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 sharded
    18  
    19  import (
    20  	"flag"
    21  	"fmt"
    22  	"os"
    23  	"os/exec"
    24  	"testing"
    25  
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/require"
    28  
    29  	"vitess.io/vitess/go/test/endtoend/cluster"
    30  	"vitess.io/vitess/go/vt/log"
    31  )
    32  
    33  var (
    34  	clusterInstance *cluster.LocalProcessCluster
    35  	hostname        = "localhost"
    36  	keyspaceName    = "ks"
    37  	cell            = "zone1"
    38  	sqlSchema       = `
    39  		create table vt_select_test (
    40  		id bigint not null,
    41  		msg varchar(64),
    42  		primary key (id)
    43  		) Engine=InnoDB
    44  		`
    45  	sqlSchemaReverse = `
    46  		create table vt_select_test (
    47  		msg varchar(64),
    48  		id bigint not null,
    49  		primary key (id)
    50  		) Engine=InnoDB
    51  		`
    52  	vSchema = `
    53  		{
    54  		  "sharded": true,
    55  		  "vindexes": {
    56  			"hash_index": {
    57  			  "type": "hash"
    58  			}
    59  		  },
    60  		  "tables": {
    61  			"vt_select_test": {
    62  			   "column_vindexes": [
    63  				{
    64  				  "column": "id",
    65  				  "name": "hash_index"
    66  				}
    67  			  ] 
    68  			}
    69  		  }
    70  		}
    71  	`
    72  )
    73  
    74  func TestMain(m *testing.M) {
    75  	defer cluster.PanicHandler(nil)
    76  	flag.Parse()
    77  
    78  	exitcode, err := func() (int, error) {
    79  		clusterInstance = cluster.NewCluster(cell, hostname)
    80  		defer clusterInstance.Teardown()
    81  
    82  		// Start topo server
    83  		if err := clusterInstance.StartTopo(); err != nil {
    84  			return 1, err
    85  		}
    86  		if err := clusterInstance.VtctlProcess.CreateKeyspace(keyspaceName); err != nil {
    87  			return 1, err
    88  		}
    89  
    90  		initCluster([]string{"-80", "80-"}, 2)
    91  
    92  		return m.Run(), nil
    93  	}()
    94  	if err != nil {
    95  		fmt.Printf("%v\n", err)
    96  		os.Exit(1)
    97  	} else {
    98  		os.Exit(exitcode)
    99  	}
   100  
   101  }
   102  
   103  func TestShardedKeyspace(t *testing.T) {
   104  	defer cluster.PanicHandler(t)
   105  	shard1 := clusterInstance.Keyspaces[0].Shards[0]
   106  	shard2 := clusterInstance.Keyspaces[0].Shards[1]
   107  
   108  	shard1Primary := shard1.Vttablets[0]
   109  	shard2Primary := shard2.Vttablets[0]
   110  	err := clusterInstance.VtctlclientProcess.InitializeShard(keyspaceName, shard1.Name, cell, shard1Primary.TabletUID)
   111  	require.Nil(t, err)
   112  	err = clusterInstance.VtctlclientProcess.InitializeShard(keyspaceName, shard2.Name, cell, shard2Primary.TabletUID)
   113  	require.Nil(t, err)
   114  
   115  	err = clusterInstance.StartVTOrc(keyspaceName)
   116  	require.NoError(t, err)
   117  
   118  	// apply the schema on the first shard through vtctl, so all tablets
   119  	// are the same.
   120  	//apply the schema on the second shard.
   121  	_, err = shard1Primary.VttabletProcess.QueryTablet(sqlSchema, keyspaceName, true)
   122  	require.Nil(t, err)
   123  
   124  	_, err = shard2Primary.VttabletProcess.QueryTablet(sqlSchemaReverse, keyspaceName, true)
   125  	require.Nil(t, err)
   126  
   127  	if err = clusterInstance.VtctlclientProcess.ApplyVSchema(keyspaceName, vSchema); err != nil {
   128  		log.Error(err.Error())
   129  		return
   130  	}
   131  
   132  	reloadSchemas(t,
   133  		shard1Primary.Alias,
   134  		shard1.Vttablets[1].Alias,
   135  		shard2Primary.Alias,
   136  		shard2.Vttablets[1].Alias)
   137  
   138  	_ = clusterInstance.VtctlclientProcess.ExecuteCommand("SetReadWrite", shard1Primary.Alias)
   139  	_ = clusterInstance.VtctlclientProcess.ExecuteCommand("SetReadWrite", shard2Primary.Alias)
   140  
   141  	_, _ = shard1Primary.VttabletProcess.QueryTablet("insert into vt_select_test (id, msg) values (1, 'test 1')", keyspaceName, true)
   142  	_, _ = shard2Primary.VttabletProcess.QueryTablet("insert into vt_select_test (id, msg) values (10, 'test 10')", keyspaceName, true)
   143  
   144  	err = clusterInstance.VtctlclientProcess.ExecuteCommand("Validate", "--", "--ping-tablets")
   145  	require.Nil(t, err)
   146  
   147  	rows, err := shard1Primary.VttabletProcess.QueryTablet("select id, msg from vt_select_test order by id", keyspaceName, true)
   148  	require.Nil(t, err)
   149  	assert.Equal(t, `[[INT64(1) VARCHAR("test 1")]]`, fmt.Sprintf("%v", rows.Rows))
   150  
   151  	err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidateSchemaShard", fmt.Sprintf("%s/%s", keyspaceName, shard1.Name))
   152  	require.Nil(t, err)
   153  
   154  	err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidateSchemaShard", fmt.Sprintf("%s/%s", keyspaceName, shard1.Name))
   155  	require.Nil(t, err)
   156  
   157  	output, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("ValidateSchemaKeyspace", keyspaceName)
   158  	require.Error(t, err)
   159  	// We should assert that there is a schema difference and that both the shard primaries are involved in it.
   160  	// However, we cannot assert in which order the two primaries will occur since the underlying function does not guarantee that
   161  	// We could have an output here like `schemas differ ... shard1Primary ... differs from: shard2Primary ...` or `schemas differ ... shard2Primary ... differs from: shard1Primary ...`
   162  	assert.Contains(t, output, "schemas differ on table vt_select_test:")
   163  	assert.Contains(t, output, shard1Primary.Alias+": CREATE TABLE")
   164  	assert.Contains(t, output, shard2Primary.Alias+": CREATE TABLE")
   165  
   166  	err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidateVersionShard", fmt.Sprintf("%s/%s", keyspaceName, shard1.Name))
   167  	require.Nil(t, err)
   168  	err = clusterInstance.VtctlclientProcess.ExecuteCommand("GetPermissions", shard1.Vttablets[1].Alias)
   169  	require.Nil(t, err)
   170  	err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidatePermissionsShard", fmt.Sprintf("%s/%s", keyspaceName, shard1.Name))
   171  	require.Nil(t, err)
   172  	err = clusterInstance.VtctlclientProcess.ExecuteCommand("ValidatePermissionsKeyspace", keyspaceName)
   173  	require.Nil(t, err)
   174  
   175  	rows, err = shard1Primary.VttabletProcess.QueryTablet("select id, msg from vt_select_test order by id", keyspaceName, true)
   176  	require.Nil(t, err)
   177  	assert.Equal(t, `[[INT64(1) VARCHAR("test 1")]]`, fmt.Sprintf("%v", rows.Rows))
   178  
   179  	rows, err = shard2Primary.VttabletProcess.QueryTablet("select id, msg from vt_select_test order by id", keyspaceName, true)
   180  	require.Nil(t, err)
   181  	assert.Equal(t, `[[INT64(10) VARCHAR("test 10")]]`, fmt.Sprintf("%v", rows.Rows))
   182  }
   183  
   184  func reloadSchemas(t *testing.T, aliases ...string) {
   185  	for _, alias := range aliases {
   186  		if err := clusterInstance.VtctlclientProcess.ExecuteCommand("ReloadSchema", alias); err != nil {
   187  			assert.Fail(t, "Unable to reload schema")
   188  		}
   189  
   190  	}
   191  }
   192  
   193  func initCluster(shardNames []string, totalTabletsRequired int) {
   194  	keyspace := cluster.Keyspace{
   195  		Name: keyspaceName,
   196  	}
   197  	for _, shardName := range shardNames {
   198  		shard := &cluster.Shard{
   199  			Name: shardName,
   200  		}
   201  
   202  		var mysqlCtlProcessList []*exec.Cmd
   203  
   204  		for i := 0; i < totalTabletsRequired; i++ {
   205  			// instantiate vttablet object with reserved ports
   206  			tabletUID := clusterInstance.GetAndReserveTabletUID()
   207  			tablet := &cluster.Vttablet{
   208  				TabletUID: tabletUID,
   209  				HTTPPort:  clusterInstance.GetAndReservePort(),
   210  				GrpcPort:  clusterInstance.GetAndReservePort(),
   211  				MySQLPort: clusterInstance.GetAndReservePort(),
   212  				Alias:     fmt.Sprintf("%s-%010d", clusterInstance.Cell, tabletUID),
   213  			}
   214  			if i == 0 { // Make the first one as primary
   215  				tablet.Type = "primary"
   216  			}
   217  			// Start Mysqlctl process
   218  			tablet.MysqlctlProcess = *cluster.MysqlCtlProcessInstance(tablet.TabletUID, tablet.MySQLPort, clusterInstance.TmpDirectory)
   219  			proc, err := tablet.MysqlctlProcess.StartProcess()
   220  			if err != nil {
   221  				return
   222  			}
   223  			mysqlCtlProcessList = append(mysqlCtlProcessList, proc)
   224  
   225  			// start vttablet process
   226  			tablet.VttabletProcess = cluster.VttabletProcessInstance(
   227  				tablet.HTTPPort,
   228  				tablet.GrpcPort,
   229  				tablet.TabletUID,
   230  				clusterInstance.Cell,
   231  				shardName,
   232  				keyspaceName,
   233  				clusterInstance.VtctldProcess.Port,
   234  				tablet.Type,
   235  				clusterInstance.TopoProcess.Port,
   236  				clusterInstance.Hostname,
   237  				clusterInstance.TmpDirectory,
   238  				clusterInstance.VtTabletExtraArgs,
   239  				clusterInstance.DefaultCharset)
   240  			tablet.Alias = tablet.VttabletProcess.TabletPath
   241  
   242  			shard.Vttablets = append(shard.Vttablets, tablet)
   243  		}
   244  		for _, proc := range mysqlCtlProcessList {
   245  			if err := proc.Wait(); err != nil {
   246  				return
   247  			}
   248  		}
   249  
   250  		for _, tablet := range shard.Vttablets {
   251  			log.Info(fmt.Sprintf("Starting vttablet for tablet uid %d, grpc port %d", tablet.TabletUID, tablet.GrpcPort))
   252  
   253  			if err := tablet.VttabletProcess.Setup(); err != nil {
   254  				log.Error(err.Error())
   255  				return
   256  			}
   257  		}
   258  
   259  		keyspace.Shards = append(keyspace.Shards, *shard)
   260  	}
   261  	clusterInstance.Keyspaces = append(clusterInstance.Keyspaces, keyspace)
   262  }