vitess.io/vitess@v0.16.2/go/test/endtoend/vtcombo/vttest_sample_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 vtcombo 18 19 import ( 20 "context" 21 "database/sql" 22 "encoding/json" 23 "flag" 24 "fmt" 25 "io" 26 "net/http" 27 "os" 28 "os/exec" 29 "strconv" 30 "strings" 31 "testing" 32 33 "github.com/go-sql-driver/mysql" 34 "github.com/stretchr/testify/assert" 35 "github.com/stretchr/testify/require" 36 37 "vitess.io/vitess/go/vt/log" 38 "vitess.io/vitess/go/vt/vtgate/vtgateconn" 39 "vitess.io/vitess/go/vt/vttest" 40 41 querypb "vitess.io/vitess/go/vt/proto/query" 42 vttestpb "vitess.io/vitess/go/vt/proto/vttest" 43 ) 44 45 var ( 46 localCluster *vttest.LocalCluster 47 grpcAddress string 48 vtctldAddr string 49 mysqlAddress string 50 ks1 = "test_keyspace" 51 redirected = "redirected" 52 jsonTopo = ` 53 { 54 "keyspaces": [ 55 { 56 "name": "test_keyspace", 57 "shards": [{"name": "-80"}, {"name": "80-"}], 58 "rdonlyCount": 1, 59 "replicaCount": 2 60 }, 61 { 62 "name": "redirected", 63 "servedFrom": "test_keyspace" 64 }, 65 { 66 "name": "routed", 67 "shards": [{"name": "0"}] 68 } 69 ], 70 "routing_rules": { 71 "rules": [{ 72 "from_table": "routed.routed_table", 73 "to_tables": [ 74 "routed.test_table" 75 ] 76 }] 77 } 78 }` 79 ) 80 81 func TestMain(m *testing.M) { 82 flag.Parse() 83 84 exitcode, err := func() (int, error) { 85 var topology vttestpb.VTTestTopology 86 87 data := vttest.JSONTopoData(&topology) 88 err := data.Set(jsonTopo) 89 if err != nil { 90 return 1, err 91 } 92 93 var cfg vttest.Config 94 cfg.Topology = &topology 95 cfg.SchemaDir = os.Getenv("VTROOT") + "/test/vttest_schema" 96 cfg.DefaultSchemaDir = os.Getenv("VTROOT") + "/test/vttest_schema/default" 97 cfg.PersistentMode = true 98 99 localCluster = &vttest.LocalCluster{ 100 Config: cfg, 101 } 102 103 err = localCluster.Setup() 104 defer localCluster.TearDown() 105 if err != nil { 106 return 1, err 107 } 108 109 grpcAddress = fmt.Sprintf("localhost:%d", localCluster.Env.PortForProtocol("vtcombo", "grpc")) 110 mysqlAddress = fmt.Sprintf("localhost:%d", localCluster.Env.PortForProtocol("vtcombo_mysql_port", "")) 111 vtctldAddr = fmt.Sprintf("localhost:%d", localCluster.Env.PortForProtocol("vtcombo", "port")) 112 113 return m.Run(), nil 114 }() 115 if err != nil { 116 log.Errorf("top level error: %v\n", err) 117 os.Exit(1) 118 } else { 119 os.Exit(exitcode) 120 } 121 } 122 123 func TestStandalone(t *testing.T) { 124 // validate debug vars 125 resp, err := http.Get(fmt.Sprintf("http://%s/debug/vars", vtctldAddr)) 126 require.NoError(t, err) 127 defer resp.Body.Close() 128 require.Equal(t, 200, resp.StatusCode) 129 resultMap := make(map[string]any) 130 respByte, _ := io.ReadAll(resp.Body) 131 err = json.Unmarshal(respByte, &resultMap) 132 require.NoError(t, err) 133 cmd := resultMap["cmdline"] 134 require.NotNil(t, cmd, "cmdline is not available in debug vars") 135 tmp, _ := cmd.([]any) 136 require.Contains(t, tmp[0], "vtcombo") 137 138 ctx := context.Background() 139 conn, err := vtgateconn.Dial(ctx, grpcAddress) 140 require.NoError(t, err) 141 defer conn.Close() 142 143 cfg := mysql.NewConfig() 144 cfg.Net = "tcp" 145 cfg.Addr = mysqlAddress 146 cfg.DBName = "routed@primary" 147 db, err := sql.Open("mysql", cfg.FormatDSN()) 148 require.NoError(t, err) 149 defer db.Close() 150 151 idStart, rowCount := 1000, 500 152 insertManyRows(ctx, t, conn, idStart, rowCount) 153 assertInsertedRowsExist(ctx, t, conn, idStart, rowCount) 154 assertRouting(ctx, t, db) 155 assertCanInsertRow(ctx, t, conn) 156 assertTabletsPresent(t) 157 158 err = localCluster.TearDown() 159 require.NoError(t, err) 160 err = localCluster.Setup() 161 require.NoError(t, err) 162 163 assertInsertedRowsExist(ctx, t, conn, idStart, rowCount) 164 assertTabletsPresent(t) 165 assertTransactionalityAndRollbackObeyed(ctx, t, conn, idStart) 166 } 167 168 func assertInsertedRowsExist(ctx context.Context, t *testing.T, conn *vtgateconn.VTGateConn, idStart, rowCount int) { 169 cur := conn.Session(ks1+":-80@rdonly", nil) 170 bindVariables := map[string]*querypb.BindVariable{ 171 "id_start": {Type: querypb.Type_UINT64, Value: []byte(strconv.FormatInt(int64(idStart), 10))}, 172 } 173 res, err := cur.Execute(ctx, "select * from test_table where id >= :id_start", bindVariables) 174 require.NoError(t, err) 175 176 assert.Equal(t, rowCount, len(res.Rows)) 177 178 cur = conn.Session(redirected+":-80@replica", nil) 179 bindVariables = map[string]*querypb.BindVariable{ 180 "id_start": {Type: querypb.Type_UINT64, Value: []byte(strconv.FormatInt(int64(idStart), 10))}, 181 } 182 res, err = cur.Execute(ctx, "select * from test_table where id = :id_start", bindVariables) 183 require.NoError(t, err) 184 require.Equal(t, 1, len(res.Rows)) 185 assert.Equal(t, "VARCHAR(\"test1000\")", res.Rows[0][1].String()) 186 } 187 188 func assertRouting(ctx context.Context, t *testing.T, db *sql.DB) { 189 // insert into test table 190 _, err := db.ExecContext(ctx, `insert into test_table (id, msg, keyspace_id) values (?, ?, ?)`, 1, "message", 1) 191 require.NoError(t, err) 192 193 // read from routed table 194 row := db.QueryRowContext(ctx, "SELECT COUNT(*) FROM routed_table") 195 require.NoError(t, row.Err()) 196 var count uint64 197 require.NoError(t, row.Scan(&count)) 198 require.NotZero(t, count) 199 } 200 201 func assertCanInsertRow(ctx context.Context, t *testing.T, conn *vtgateconn.VTGateConn) { 202 cur := conn.Session(ks1+":80-@primary", nil) 203 _, err := cur.Execute(ctx, "begin", nil) 204 require.NoError(t, err) 205 206 i := 0x810000000000000 207 bindVariables := map[string]*querypb.BindVariable{ 208 "id": {Type: querypb.Type_UINT64, Value: []byte(strconv.FormatInt(int64(i), 10))}, 209 "msg": {Type: querypb.Type_VARCHAR, Value: []byte("test" + strconv.FormatInt(int64(i), 10))}, 210 "keyspace_id": {Type: querypb.Type_UINT64, Value: []byte(strconv.FormatInt(int64(i), 10))}, 211 } 212 query := "insert into test_table (id, msg, keyspace_id) values (:id, :msg, :keyspace_id)" 213 _, err = cur.Execute(ctx, query, bindVariables) 214 require.NoError(t, err) 215 216 _, err = cur.Execute(ctx, "commit", nil) 217 require.NoError(t, err) 218 } 219 220 func insertManyRows(ctx context.Context, t *testing.T, conn *vtgateconn.VTGateConn, idStart, rowCount int) { 221 cur := conn.Session(ks1+":-80@primary", nil) 222 223 query := "insert into test_table (id, msg, keyspace_id) values (:id, :msg, :keyspace_id)" 224 _, err := cur.Execute(ctx, "begin", nil) 225 require.NoError(t, err) 226 227 for i := idStart; i < idStart+rowCount; i++ { 228 bindVariables := map[string]*querypb.BindVariable{ 229 "id": {Type: querypb.Type_UINT64, Value: []byte(strconv.FormatInt(int64(i), 10))}, 230 "msg": {Type: querypb.Type_VARCHAR, Value: []byte("test" + strconv.FormatInt(int64(i), 10))}, 231 "keyspace_id": {Type: querypb.Type_UINT64, Value: []byte(strconv.FormatInt(int64(i), 10))}, 232 } 233 _, err = cur.Execute(ctx, query, bindVariables) 234 require.NoError(t, err) 235 } 236 237 _, err = cur.Execute(ctx, "commit", nil) 238 require.NoError(t, err) 239 } 240 241 func assertTabletsPresent(t *testing.T) { 242 tmpCmd := exec.Command("vtctlclient", "--vtctl_client_protocol", "grpc", "--server", grpcAddress, "--stderrthreshold", "0", "ListAllTablets", "--", "test") 243 244 log.Infof("Running vtctlclient with command: %v", tmpCmd.Args) 245 246 output, err := tmpCmd.CombinedOutput() 247 require.NoError(t, err) 248 249 numPrimary, numReplica, numRdonly, numDash80, num80Dash, numRouted := 0, 0, 0, 0, 0, 0 250 lines := strings.Split(string(output), "\n") 251 252 for _, line := range lines { 253 if !strings.HasPrefix(line, "test-") { 254 continue 255 } 256 parts := strings.Split(line, " ") 257 if parts[1] == "routed" { 258 numRouted++ 259 continue 260 } 261 262 assert.Equal(t, "test_keyspace", parts[1]) 263 264 switch parts[3] { 265 case "primary": 266 numPrimary++ 267 case "replica": 268 numReplica++ 269 case "rdonly": 270 numRdonly++ 271 default: 272 t.Logf("invalid tablet type %s", parts[3]) 273 } 274 275 switch parts[2] { 276 case "-80": 277 numDash80++ 278 case "80-": 279 num80Dash++ 280 default: 281 t.Logf("invalid shard %s", parts[2]) 282 } 283 284 } 285 286 assert.Equal(t, 2, numPrimary) 287 assert.Equal(t, 2, numReplica) 288 assert.Equal(t, 2, numRdonly) 289 assert.Equal(t, 3, numDash80) 290 assert.Equal(t, 3, num80Dash) 291 assert.NotZero(t, numRouted) 292 } 293 294 func assertTransactionalityAndRollbackObeyed(ctx context.Context, t *testing.T, conn *vtgateconn.VTGateConn, idStart int) { 295 cur := conn.Session(ks1+":80-@primary", &querypb.ExecuteOptions{}) 296 297 i := idStart + 1 298 msg := "test" 299 bindVariables := map[string]*querypb.BindVariable{ 300 "id": {Type: querypb.Type_UINT64, Value: []byte(strconv.FormatInt(int64(i), 10))}, 301 "msg": {Type: querypb.Type_VARCHAR, Value: []byte(msg)}, 302 "keyspace_id": {Type: querypb.Type_UINT64, Value: []byte(strconv.FormatInt(int64(i), 10))}, 303 } 304 query := "insert into test_table (id, msg, keyspace_id) values (:id, :msg, :keyspace_id)" 305 _, err := cur.Execute(ctx, query, bindVariables) 306 require.NoError(t, err) 307 308 bindVariables = map[string]*querypb.BindVariable{ 309 "msg": {Type: querypb.Type_VARCHAR, Value: []byte(msg)}, 310 } 311 res, err := cur.Execute(ctx, "select * from test_table where msg = :msg", bindVariables) 312 require.NoError(t, err) 313 require.Equal(t, 1, len(res.Rows)) 314 315 _, err = cur.Execute(ctx, "begin", nil) 316 require.NoError(t, err) 317 318 msg2 := msg + "2" 319 bindVariables = map[string]*querypb.BindVariable{ 320 "id": {Type: querypb.Type_UINT64, Value: []byte(strconv.FormatInt(int64(i), 10))}, 321 "msg": {Type: querypb.Type_VARCHAR, Value: []byte(msg2)}, 322 } 323 query = "update test_table set msg = :msg where id = :id" 324 _, err = cur.Execute(ctx, query, bindVariables) 325 require.NoError(t, err) 326 327 _, err = cur.Execute(ctx, "rollback", nil) 328 require.NoError(t, err) 329 330 bindVariables = map[string]*querypb.BindVariable{ 331 "msg": {Type: querypb.Type_VARCHAR, Value: []byte(msg2)}, 332 } 333 res, err = cur.Execute(ctx, "select * from test_table where msg = :msg", bindVariables) 334 require.NoError(t, err) 335 require.Equal(t, 0, len(res.Rows)) 336 }