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 }