vitess.io/vitess@v0.16.2/go/test/endtoend/onlineddl/scheduler/onlineddl_scheduler_test.go (about) 1 /* 2 Copyright 2021 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 scheduler 18 19 import ( 20 "context" 21 "errors" 22 "flag" 23 "fmt" 24 "io" 25 "math/rand" 26 "os" 27 "path" 28 "strings" 29 "sync" 30 "testing" 31 "time" 32 33 "vitess.io/vitess/go/mysql" 34 "vitess.io/vitess/go/textutil" 35 "vitess.io/vitess/go/vt/log" 36 "vitess.io/vitess/go/vt/schema" 37 "vitess.io/vitess/go/vt/sqlparser" 38 39 "vitess.io/vitess/go/test/endtoend/cluster" 40 "vitess.io/vitess/go/test/endtoend/onlineddl" 41 42 "github.com/stretchr/testify/assert" 43 "github.com/stretchr/testify/require" 44 ) 45 46 const ( 47 anyErrorIndicator = "<any-error-of-any-kind>" 48 ) 49 50 type testOnlineDDLStatementParams struct { 51 ddlStatement string 52 ddlStrategy string 53 executeStrategy string 54 expectHint string 55 expectError string 56 skipWait bool 57 migrationContext string 58 } 59 60 type testRevertMigrationParams struct { 61 revertUUID string 62 executeStrategy string 63 ddlStrategy string 64 migrationContext string 65 expectError string 66 skipWait bool 67 } 68 69 var ( 70 clusterInstance *cluster.LocalProcessCluster 71 shards []cluster.Shard 72 vtParams mysql.ConnParams 73 74 normalWaitTime = 20 * time.Second 75 extendedWaitTime = 60 * time.Second 76 ensureStateNotChangedTime = 5 * time.Second 77 78 hostname = "localhost" 79 keyspaceName = "ks" 80 cell = "zone1" 81 schemaChangeDirectory = "" 82 overrideVtctlParams *cluster.VtctlClientParams 83 ) 84 85 type WriteMetrics struct { 86 mu sync.Mutex 87 insertsAttempts, insertsFailures, insertsNoops, inserts int64 88 updatesAttempts, updatesFailures, updatesNoops, updates int64 89 deletesAttempts, deletesFailures, deletesNoops, deletes int64 90 } 91 92 func (w *WriteMetrics) Clear() { 93 w.mu.Lock() 94 defer w.mu.Unlock() 95 96 w.inserts = 0 97 w.updates = 0 98 w.deletes = 0 99 100 w.insertsAttempts = 0 101 w.insertsFailures = 0 102 w.insertsNoops = 0 103 104 w.updatesAttempts = 0 105 w.updatesFailures = 0 106 w.updatesNoops = 0 107 108 w.deletesAttempts = 0 109 w.deletesFailures = 0 110 w.deletesNoops = 0 111 } 112 113 func (w *WriteMetrics) String() string { 114 return fmt.Sprintf(`WriteMetrics: inserts-deletes=%d, updates-deletes=%d, 115 insertsAttempts=%d, insertsFailures=%d, insertsNoops=%d, inserts=%d, 116 updatesAttempts=%d, updatesFailures=%d, updatesNoops=%d, updates=%d, 117 deletesAttempts=%d, deletesFailures=%d, deletesNoops=%d, deletes=%d, 118 `, 119 w.inserts-w.deletes, w.updates-w.deletes, 120 w.insertsAttempts, w.insertsFailures, w.insertsNoops, w.inserts, 121 w.updatesAttempts, w.updatesFailures, w.updatesNoops, w.updates, 122 w.deletesAttempts, w.deletesFailures, w.deletesNoops, w.deletes, 123 ) 124 } 125 126 func parseTableName(t *testing.T, sql string) (tableName string) { 127 // ddlStatement could possibly be composed of multiple DDL statements 128 tokenizer := sqlparser.NewStringTokenizer(sql) 129 for { 130 stmt, err := sqlparser.ParseNextStrictDDL(tokenizer) 131 if err != nil && errors.Is(err, io.EOF) { 132 break 133 } 134 require.NoErrorf(t, err, "parsing sql: [%v]", sql) 135 ddlStmt, ok := stmt.(sqlparser.DDLStatement) 136 require.True(t, ok) 137 tableName = ddlStmt.GetTable().Name.String() 138 if tableName == "" { 139 tbls := ddlStmt.AffectedTables() 140 require.NotEmpty(t, tbls) 141 tableName = tbls[0].Name.String() 142 } 143 require.NotEmptyf(t, tableName, "could not parse table name from SQL: %s", sqlparser.String(ddlStmt)) 144 } 145 require.NotEmptyf(t, tableName, "could not parse table name from SQL: %s", sql) 146 return tableName 147 } 148 149 // testOnlineDDLStatement runs an online DDL, ALTER statement 150 func TestParseTableName(t *testing.T) { 151 sqls := []string{ 152 `ALTER TABLE t1_test ENGINE=InnoDB`, 153 `ALTER TABLE t1_test ENGINE=InnoDB;`, 154 `DROP TABLE IF EXISTS t1_test`, 155 ` 156 ALTER TABLE stress_test ENGINE=InnoDB; 157 ALTER TABLE stress_test ENGINE=InnoDB; 158 ALTER TABLE stress_test ENGINE=InnoDB; 159 `, 160 } 161 162 for _, sql := range sqls { 163 t.Run(sql, func(t *testing.T) { 164 parseTableName(t, sql) 165 }) 166 } 167 } 168 169 func TestMain(m *testing.M) { 170 defer cluster.PanicHandler(nil) 171 flag.Parse() 172 173 exitcode, err := func() (int, error) { 174 clusterInstance = cluster.NewCluster(cell, hostname) 175 schemaChangeDirectory = path.Join("/tmp", fmt.Sprintf("schema_change_dir_%d", clusterInstance.GetAndReserveTabletUID())) 176 defer os.RemoveAll(schemaChangeDirectory) 177 defer clusterInstance.Teardown() 178 179 if _, err := os.Stat(schemaChangeDirectory); os.IsNotExist(err) { 180 _ = os.Mkdir(schemaChangeDirectory, 0700) 181 } 182 183 clusterInstance.VtctldExtraArgs = []string{ 184 "--schema_change_dir", schemaChangeDirectory, 185 "--schema_change_controller", "local", 186 "--schema_change_check_interval", "1"} 187 188 clusterInstance.VtTabletExtraArgs = []string{ 189 "--enable-lag-throttler", 190 "--throttle_threshold", "1s", 191 "--heartbeat_enable", 192 "--heartbeat_interval", "250ms", 193 "--heartbeat_on_demand_duration", "5s", 194 "--watch_replication_stream", 195 } 196 clusterInstance.VtGateExtraArgs = []string{} 197 198 if err := clusterInstance.StartTopo(); err != nil { 199 return 1, err 200 } 201 202 // Start keyspace 203 keyspace := &cluster.Keyspace{ 204 Name: keyspaceName, 205 } 206 207 // No need for replicas in this stress test 208 if err := clusterInstance.StartKeyspace(*keyspace, []string{"1"}, 0, false); err != nil { 209 return 1, err 210 } 211 212 vtgateInstance := clusterInstance.NewVtgateInstance() 213 // Start vtgate 214 if err := vtgateInstance.Setup(); err != nil { 215 return 1, err 216 } 217 // ensure it is torn down during cluster TearDown 218 clusterInstance.VtgateProcess = *vtgateInstance 219 vtParams = mysql.ConnParams{ 220 Host: clusterInstance.Hostname, 221 Port: clusterInstance.VtgateMySQLPort, 222 } 223 224 return m.Run(), nil 225 }() 226 if err != nil { 227 fmt.Printf("%v\n", err) 228 os.Exit(1) 229 } else { 230 os.Exit(exitcode) 231 } 232 233 } 234 235 func TestSchemaChange(t *testing.T) { 236 t.Run("scheduler", testScheduler) 237 t.Run("singleton", testSingleton) 238 t.Run("declarative", testDeclarative) 239 t.Run("foreign-keys", testForeignKeys) 240 t.Run("summary: validate sequential migration IDs", func(t *testing.T) { 241 onlineddl.ValidateSequentialMigrationIDs(t, &vtParams, shards) 242 }) 243 } 244 245 func testScheduler(t *testing.T) { 246 defer cluster.PanicHandler(t) 247 shards = clusterInstance.Keyspaces[0].Shards 248 require.Equal(t, 1, len(shards)) 249 250 ddlStrategy := "vitess" 251 252 createParams := func(ddlStatement string, ddlStrategy string, executeStrategy string, expectHint string, expectError string, skipWait bool) *testOnlineDDLStatementParams { 253 return &testOnlineDDLStatementParams{ 254 ddlStatement: ddlStatement, 255 ddlStrategy: ddlStrategy, 256 executeStrategy: executeStrategy, 257 expectHint: expectHint, 258 expectError: expectError, 259 skipWait: skipWait, 260 } 261 } 262 263 createRevertParams := func(revertUUID string, ddlStrategy string, executeStrategy string, expectError string, skipWait bool) *testRevertMigrationParams { 264 return &testRevertMigrationParams{ 265 revertUUID: revertUUID, 266 executeStrategy: executeStrategy, 267 ddlStrategy: ddlStrategy, 268 expectError: expectError, 269 skipWait: skipWait, 270 } 271 } 272 273 mysqlVersion := onlineddl.GetMySQLVersion(t, clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet()) 274 require.NotEmpty(t, mysqlVersion) 275 _, capableOf, _ := mysql.GetFlavor(mysqlVersion, nil) 276 277 var ( 278 t1uuid string 279 t2uuid string 280 281 t1Name = "t1_test" 282 t2Name = "t2_test" 283 createT1Statement = ` 284 CREATE TABLE t1_test ( 285 id bigint(20) not null, 286 hint_col varchar(64) not null default 'just-created', 287 PRIMARY KEY (id) 288 ) ENGINE=InnoDB 289 ` 290 createT2Statement = ` 291 CREATE TABLE t2_test ( 292 id bigint(20) not null, 293 hint_col varchar(64) not null default 'just-created', 294 PRIMARY KEY (id) 295 ) ENGINE=InnoDB 296 ` 297 createT1IfNotExistsStatement = ` 298 CREATE TABLE IF NOT EXISTS t1_test ( 299 id bigint(20) not null, 300 hint_col varchar(64) not null default 'should_not_appear', 301 PRIMARY KEY (id) 302 ) ENGINE=InnoDB 303 ` 304 trivialAlterT1Statement = ` 305 ALTER TABLE t1_test ENGINE=InnoDB; 306 ` 307 trivialAlterT2Statement = ` 308 ALTER TABLE t2_test ENGINE=InnoDB; 309 ` 310 instantAlterT1Statement = ` 311 ALTER TABLE t1_test ADD COLUMN i0 INT NOT NULL DEFAULT 0; 312 ` 313 dropT1Statement = ` 314 DROP TABLE IF EXISTS t1_test 315 ` 316 dropT3Statement = ` 317 DROP TABLE IF EXISTS t3_test 318 ` 319 dropT4Statement = ` 320 DROP TABLE IF EXISTS t4_test 321 ` 322 alterExtraColumn = ` 323 ALTER TABLE t1_test ADD COLUMN extra_column int NOT NULL DEFAULT 0 324 ` 325 createViewDependsOnExtraColumn = ` 326 CREATE VIEW t1_test_view AS SELECT id, extra_column FROM t1_test 327 ` 328 ) 329 330 testReadTimestamp := func(t *testing.T, uuid string, timestampColumn string) (timestamp string) { 331 rs := onlineddl.ReadMigrations(t, &vtParams, uuid) 332 require.NotNil(t, rs) 333 for _, row := range rs.Named().Rows { 334 timestamp = row.AsString(timestampColumn, "") 335 assert.NotEmpty(t, timestamp) 336 } 337 return timestamp 338 } 339 testTableSequentialTimes := func(t *testing.T, uuid1, uuid2 string) { 340 // expect uuid2 to start after uuid1 completes 341 t.Run("Compare t1, t2 sequential times", func(t *testing.T) { 342 endTime1 := testReadTimestamp(t, uuid1, "completed_timestamp") 343 startTime2 := testReadTimestamp(t, uuid2, "started_timestamp") 344 assert.GreaterOrEqual(t, startTime2, endTime1) 345 }) 346 } 347 testTableCompletionTimes := func(t *testing.T, uuid1, uuid2 string) { 348 // expect uuid1 to complete before uuid2 349 t.Run("Compare t1, t2 completion times", func(t *testing.T) { 350 endTime1 := testReadTimestamp(t, uuid1, "completed_timestamp") 351 endTime2 := testReadTimestamp(t, uuid2, "completed_timestamp") 352 assert.GreaterOrEqual(t, endTime2, endTime1) 353 }) 354 } 355 testAllowConcurrent := func(t *testing.T, name string, uuid string, expect int64) { 356 t.Run("verify allow_concurrent: "+name, func(t *testing.T) { 357 rs := onlineddl.ReadMigrations(t, &vtParams, uuid) 358 require.NotNil(t, rs) 359 for _, row := range rs.Named().Rows { 360 allowConcurrent := row.AsInt64("allow_concurrent", 0) 361 assert.Equal(t, expect, allowConcurrent) 362 } 363 }) 364 } 365 366 // CREATE 367 t.Run("CREATE TABLEs t1, t1", func(t *testing.T) { 368 { // The table does not exist 369 t1uuid = testOnlineDDLStatement(t, createParams(createT1Statement, ddlStrategy, "vtgate", "just-created", "", false)) 370 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete) 371 checkTable(t, t1Name, true) 372 } 373 { 374 // The table does not exist 375 t2uuid = testOnlineDDLStatement(t, createParams(createT2Statement, ddlStrategy, "vtgate", "just-created", "", false)) 376 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t2uuid, schema.OnlineDDLStatusComplete) 377 checkTable(t, t2Name, true) 378 } 379 testTableSequentialTimes(t, t1uuid, t2uuid) 380 }) 381 t.Run("Postpone launch CREATE", func(t *testing.T) { 382 t1uuid = testOnlineDDLStatement(t, createParams(createT1IfNotExistsStatement, ddlStrategy+" --postpone-launch", "vtgate", "", "", true)) // skip wait 383 time.Sleep(2 * time.Second) 384 rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid) 385 require.NotNil(t, rs) 386 for _, row := range rs.Named().Rows { 387 postponeLaunch := row.AsInt64("postpone_launch", 0) 388 assert.Equal(t, int64(1), postponeLaunch) 389 } 390 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusQueued) 391 392 t.Run("launch all shards", func(t *testing.T) { 393 onlineddl.CheckLaunchMigration(t, &vtParams, shards, t1uuid, "", true) 394 rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid) 395 require.NotNil(t, rs) 396 for _, row := range rs.Named().Rows { 397 postponeLaunch := row.AsInt64("postpone_launch", 0) 398 assert.Equal(t, int64(0), postponeLaunch) 399 } 400 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 401 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 402 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete) 403 }) 404 }) 405 t.Run("Postpone launch ALTER", func(t *testing.T) { 406 t1uuid = testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy+" --postpone-launch", "vtgate", "", "", true)) // skip wait 407 time.Sleep(2 * time.Second) 408 rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid) 409 require.NotNil(t, rs) 410 for _, row := range rs.Named().Rows { 411 postponeLaunch := row.AsInt64("postpone_launch", 0) 412 assert.Equal(t, int64(1), postponeLaunch) 413 } 414 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusQueued) 415 416 t.Run("launch irrelevant UUID", func(t *testing.T) { 417 someOtherUUID := "00000000_1111_2222_3333_444444444444" 418 onlineddl.CheckLaunchMigration(t, &vtParams, shards, someOtherUUID, "", false) 419 time.Sleep(2 * time.Second) 420 rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid) 421 require.NotNil(t, rs) 422 for _, row := range rs.Named().Rows { 423 postponeLaunch := row.AsInt64("postpone_launch", 0) 424 assert.Equal(t, int64(1), postponeLaunch) 425 } 426 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusQueued) 427 }) 428 t.Run("launch irrelevant shards", func(t *testing.T) { 429 onlineddl.CheckLaunchMigration(t, &vtParams, shards, t1uuid, "x,y,z", false) 430 time.Sleep(2 * time.Second) 431 rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid) 432 require.NotNil(t, rs) 433 for _, row := range rs.Named().Rows { 434 postponeLaunch := row.AsInt64("postpone_launch", 0) 435 assert.Equal(t, int64(1), postponeLaunch) 436 } 437 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusQueued) 438 }) 439 t.Run("launch relevant shard", func(t *testing.T) { 440 onlineddl.CheckLaunchMigration(t, &vtParams, shards, t1uuid, "x, y, 1", true) 441 rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid) 442 require.NotNil(t, rs) 443 for _, row := range rs.Named().Rows { 444 postponeLaunch := row.AsInt64("postpone_launch", 0) 445 assert.Equal(t, int64(0), postponeLaunch) 446 } 447 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 448 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 449 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete) 450 }) 451 }) 452 t.Run("ALTER both tables non-concurrent", func(t *testing.T) { 453 t1uuid = testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy, "vtgate", "", "", true)) // skip wait 454 t2uuid = testOnlineDDLStatement(t, createParams(trivialAlterT2Statement, ddlStrategy, "vtgate", "", "", true)) // skip wait 455 456 t.Run("wait for t1 complete", func(t *testing.T) { 457 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 458 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 459 }) 460 t.Run("wait for t1 complete", func(t *testing.T) { 461 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t2uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 462 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 463 }) 464 t.Run("check both complete", func(t *testing.T) { 465 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete) 466 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t2uuid, schema.OnlineDDLStatusComplete) 467 }) 468 testTableSequentialTimes(t, t1uuid, t2uuid) 469 }) 470 t.Run("ALTER both tables non-concurrent, postponed", func(t *testing.T) { 471 t1uuid = testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy+" -postpone-completion", "vtgate", "", "", true)) // skip wait 472 t2uuid = testOnlineDDLStatement(t, createParams(trivialAlterT2Statement, ddlStrategy+" -postpone-completion", "vtgate", "", "", true)) // skip wait 473 474 testAllowConcurrent(t, "t1", t1uuid, 0) 475 t.Run("expect t1 running, t2 queued", func(t *testing.T) { 476 onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusRunning) 477 // now that t1 is running, let's unblock t2. We expect it to remain queued. 478 onlineddl.CheckCompleteMigration(t, &vtParams, shards, t2uuid, true) 479 time.Sleep(ensureStateNotChangedTime) 480 // t1 should be still running! 481 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning) 482 // non-concurrent -- should be queued! 483 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t2uuid, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady) 484 }) 485 t.Run("complete t1", func(t *testing.T) { 486 // Issue a complete and wait for successful completion 487 onlineddl.CheckCompleteMigration(t, &vtParams, shards, t1uuid, true) 488 // This part may take a while, because we depend on vreplication polling 489 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 490 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 491 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete) 492 }) 493 t.Run("expect t2 to complete", func(t *testing.T) { 494 // This part may take a while, because we depend on vreplication polling 495 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t2uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 496 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 497 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t2uuid, schema.OnlineDDLStatusComplete) 498 }) 499 testTableSequentialTimes(t, t1uuid, t2uuid) 500 }) 501 502 t.Run("ALTER both tables, elligible for concurrenct", func(t *testing.T) { 503 // ALTER TABLE is allowed to run concurrently when no other ALTER is busy with copy state. Our tables are tiny so we expect to find both migrations running 504 t1uuid = testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy+" --allow-concurrent --postpone-completion", "vtgate", "", "", true)) // skip wait 505 t2uuid = testOnlineDDLStatement(t, createParams(trivialAlterT2Statement, ddlStrategy+" --allow-concurrent --postpone-completion", "vtgate", "", "", true)) // skip wait 506 507 testAllowConcurrent(t, "t1", t1uuid, 1) 508 testAllowConcurrent(t, "t2", t2uuid, 1) 509 t.Run("expect both running", func(t *testing.T) { 510 onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusRunning) 511 onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t2uuid, normalWaitTime, schema.OnlineDDLStatusRunning) 512 time.Sleep(ensureStateNotChangedTime) 513 // both should be still running! 514 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning) 515 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t2uuid, schema.OnlineDDLStatusRunning) 516 }) 517 t.Run("complete t2", func(t *testing.T) { 518 // Issue a complete and wait for successful completion 519 onlineddl.CheckCompleteMigration(t, &vtParams, shards, t2uuid, true) 520 // This part may take a while, because we depend on vreplication polling 521 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t2uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 522 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 523 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t2uuid, schema.OnlineDDLStatusComplete) 524 }) 525 t.Run("complete t1", func(t *testing.T) { 526 // t1 should still be running! 527 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning) 528 // Issue a complete and wait for successful completion 529 onlineddl.CheckCompleteMigration(t, &vtParams, shards, t1uuid, true) 530 // This part may take a while, because we depend on vreplication polling 531 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 532 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 533 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete) 534 }) 535 testTableCompletionTimes(t, t2uuid, t1uuid) 536 }) 537 t.Run("ALTER both tables, elligible for concurrenct, with throttling", func(t *testing.T) { 538 onlineddl.ThrottleAllMigrations(t, &vtParams) 539 defer onlineddl.UnthrottleAllMigrations(t, &vtParams) 540 // ALTER TABLE is allowed to run concurrently when no other ALTER is busy with copy state. Our tables are tiny so we expect to find both migrations running 541 t1uuid = testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy+" -allow-concurrent -postpone-completion", "vtgate", "", "", true)) // skip wait 542 t2uuid = testOnlineDDLStatement(t, createParams(trivialAlterT2Statement, ddlStrategy+" -allow-concurrent -postpone-completion", "vtgate", "", "", true)) // skip wait 543 544 testAllowConcurrent(t, "t1", t1uuid, 1) 545 testAllowConcurrent(t, "t2", t2uuid, 1) 546 t.Run("expect t1 running", func(t *testing.T) { 547 onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusRunning) 548 // since all migrations are throttled, t1 migration is not ready_to_complete, hence 549 // t2 should not be running 550 onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t2uuid, normalWaitTime, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady) 551 time.Sleep(ensureStateNotChangedTime) 552 // both should be still running! 553 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning) 554 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t2uuid, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady) 555 }) 556 t.Run("unthrottle, expect t2 running", func(t *testing.T) { 557 onlineddl.UnthrottleAllMigrations(t, &vtParams) 558 // t1 should now be ready_to_complete, hence t2 should start running 559 onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t2uuid, extendedWaitTime, schema.OnlineDDLStatusRunning) 560 time.Sleep(ensureStateNotChangedTime) 561 // both should be still running! 562 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning) 563 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t2uuid, schema.OnlineDDLStatusRunning) 564 }) 565 t.Run("complete t2", func(t *testing.T) { 566 // Issue a complete and wait for successful completion 567 onlineddl.CheckCompleteMigration(t, &vtParams, shards, t2uuid, true) 568 // This part may take a while, because we depend on vreplication polling 569 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t2uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 570 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 571 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t2uuid, schema.OnlineDDLStatusComplete) 572 }) 573 t.Run("complete t1", func(t *testing.T) { 574 // t1 should still be running! 575 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning) 576 // Issue a complete and wait for successful completion 577 onlineddl.CheckCompleteMigration(t, &vtParams, shards, t1uuid, true) 578 // This part may take a while, because we depend on vreplication polling 579 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 580 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 581 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete) 582 }) 583 testTableCompletionTimes(t, t2uuid, t1uuid) 584 }) 585 t.Run("REVERT both tables concurrent, postponed", func(t *testing.T) { 586 t1uuid = testRevertMigration(t, createRevertParams(t1uuid, ddlStrategy+" -allow-concurrent -postpone-completion", "vtgate", "", true)) 587 t2uuid = testRevertMigration(t, createRevertParams(t2uuid, ddlStrategy+" -allow-concurrent -postpone-completion", "vtgate", "", true)) 588 589 testAllowConcurrent(t, "t1", t1uuid, 1) 590 t.Run("expect both migrations to run", func(t *testing.T) { 591 onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusRunning) 592 onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t2uuid, normalWaitTime, schema.OnlineDDLStatusRunning) 593 }) 594 t.Run("test ready-to-complete", func(t *testing.T) { 595 rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid) 596 require.NotNil(t, rs) 597 for _, row := range rs.Named().Rows { 598 readyToComplete := row.AsInt64("ready_to_complete", 0) 599 assert.Equal(t, int64(1), readyToComplete) 600 } 601 }) 602 t.Run("complete t2", func(t *testing.T) { 603 // now that both are running, let's unblock t2. We expect it to complete. 604 // Issue a complete and wait for successful completion 605 onlineddl.CheckCompleteMigration(t, &vtParams, shards, t2uuid, true) 606 // This part may take a while, because we depend on vreplication polling 607 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t2uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 608 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 609 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t2uuid, schema.OnlineDDLStatusComplete) 610 }) 611 t.Run("complete t1", func(t *testing.T) { 612 // t1 should be still running! 613 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning) 614 // Issue a complete and wait for successful completion 615 onlineddl.CheckCompleteMigration(t, &vtParams, shards, t1uuid, true) 616 // This part may take a while, because we depend on vreplication polling 617 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 618 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 619 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete) 620 }) 621 }) 622 t.Run("concurrent REVERT vs two non-concurrent DROPs", func(t *testing.T) { 623 t1uuid = testRevertMigration(t, createRevertParams(t1uuid, ddlStrategy+" -allow-concurrent -postpone-completion", "vtgate", "", true)) 624 drop3uuid := testOnlineDDLStatement(t, createParams(dropT3Statement, ddlStrategy, "vtgate", "", "", true)) // skip wait 625 626 testAllowConcurrent(t, "t1", t1uuid, 1) 627 testAllowConcurrent(t, "drop3", drop3uuid, 0) 628 t.Run("expect t1 migration to run", func(t *testing.T) { 629 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusRunning) 630 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 631 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning) 632 }) 633 drop1uuid := testOnlineDDLStatement(t, createParams(dropT1Statement, ddlStrategy, "vtgate", "", "", true)) // skip wait 634 t.Run("drop3 complete", func(t *testing.T) { 635 // drop3 migration should not block. It can run concurrently to t1, and does not conflict 636 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, drop3uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 637 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 638 onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop3uuid, schema.OnlineDDLStatusComplete) 639 }) 640 t.Run("drop1 blocked", func(t *testing.T) { 641 // drop1 migration should block. It can run concurrently to t1, but conflicts on table name 642 onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop1uuid, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady) 643 // let's cancel it 644 onlineddl.CheckCancelMigration(t, &vtParams, shards, drop1uuid, true) 645 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, drop1uuid, normalWaitTime, schema.OnlineDDLStatusFailed, schema.OnlineDDLStatusCancelled) 646 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 647 onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop1uuid, schema.OnlineDDLStatusCancelled) 648 }) 649 t.Run("complete t1", func(t *testing.T) { 650 // t1 should be still running! 651 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning) 652 // Issue a complete and wait for successful completion 653 onlineddl.CheckCompleteMigration(t, &vtParams, shards, t1uuid, true) 654 // This part may take a while, because we depend on vreplication polling 655 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 656 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 657 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete) 658 }) 659 }) 660 t.Run("non-concurrent REVERT vs three concurrent drops", func(t *testing.T) { 661 t1uuid = testRevertMigration(t, createRevertParams(t1uuid, ddlStrategy+" -postpone-completion", "vtgate", "", true)) 662 drop3uuid := testOnlineDDLStatement(t, createParams(dropT3Statement, ddlStrategy+" -allow-concurrent", "vtgate", "", "", true)) // skip wait 663 drop4uuid := testOnlineDDLStatement(t, createParams(dropT4Statement, ddlStrategy+" -allow-concurrent -postpone-completion", "vtgate", "", "", true)) // skip wait 664 665 testAllowConcurrent(t, "drop3", drop3uuid, 1) 666 t.Run("expect t1 migration to run", func(t *testing.T) { 667 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusRunning) 668 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 669 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning) 670 }) 671 drop1uuid := testOnlineDDLStatement(t, createParams(dropT1Statement, ddlStrategy+" -allow-concurrent", "vtgate", "", "", true)) // skip wait 672 testAllowConcurrent(t, "drop1", drop1uuid, 1) 673 t.Run("t3drop complete", func(t *testing.T) { 674 // drop3 migration should not block. It can run concurrently to t1, and does not conflict 675 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, drop3uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 676 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 677 onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop3uuid, schema.OnlineDDLStatusComplete) 678 }) 679 t.Run("t1drop blocked", func(t *testing.T) { 680 // drop1 migration should block. It can run concurrently to t1, but conflicts on table name 681 onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop1uuid, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady) 682 }) 683 t.Run("t4 postponed", func(t *testing.T) { 684 // drop4 migration should postpone. 685 onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop4uuid, schema.OnlineDDLStatusQueued) 686 // Issue a complete and wait for successful completion. drop4 is non-conflicting and should be able to proceed 687 onlineddl.CheckCompleteMigration(t, &vtParams, shards, drop4uuid, true) 688 // This part may take a while, because we depend on vreplication polling 689 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, drop4uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 690 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 691 onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop4uuid, schema.OnlineDDLStatusComplete) 692 }) 693 t.Run("complete t1", func(t *testing.T) { 694 // t1 should be still running! 695 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning) 696 // Issue a complete and wait for successful completion 697 onlineddl.CheckCompleteMigration(t, &vtParams, shards, t1uuid, true) 698 // This part may take a while, because we depend on vreplication polling 699 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 700 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 701 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete) 702 }) 703 t.Run("t1drop unblocked", func(t *testing.T) { 704 // t1drop should now be unblocked! 705 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, drop1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 706 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 707 onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop1uuid, schema.OnlineDDLStatusComplete) 708 checkTable(t, t1Name, false) 709 }) 710 t.Run("revert t1 drop", func(t *testing.T) { 711 revertDrop3uuid := testRevertMigration(t, createRevertParams(drop1uuid, ddlStrategy+" -allow-concurrent", "vtgate", "", true)) 712 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, revertDrop3uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 713 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 714 onlineddl.CheckMigrationStatus(t, &vtParams, shards, revertDrop3uuid, schema.OnlineDDLStatusComplete) 715 checkTable(t, t1Name, true) 716 }) 717 }) 718 t.Run("conflicting migration does not block other queued migrations", func(t *testing.T) { 719 t1uuid = testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy, "vtgate", "", "", false)) // skip wait 720 t.Run("trivial t1 migration", func(t *testing.T) { 721 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete) 722 checkTable(t, t1Name, true) 723 }) 724 725 t1uuid = testRevertMigration(t, createRevertParams(t1uuid, ddlStrategy+" -postpone-completion", "vtgate", "", true)) 726 t.Run("expect t1 revert migration to run", func(t *testing.T) { 727 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusRunning) 728 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 729 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning) 730 }) 731 drop1uuid := testOnlineDDLStatement(t, createParams(dropT1Statement, ddlStrategy+" -allow-concurrent", "vtgate", "", "", true)) // skip wait 732 t.Run("t1drop blocked", func(t *testing.T) { 733 time.Sleep(ensureStateNotChangedTime) 734 // drop1 migration should block. It can run concurrently to t1, but conflicts on table name 735 onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop1uuid, schema.OnlineDDLStatusReady) 736 }) 737 t.Run("t3 ready to complete", func(t *testing.T) { 738 rs := onlineddl.ReadMigrations(t, &vtParams, drop1uuid) 739 require.NotNil(t, rs) 740 for _, row := range rs.Named().Rows { 741 readyToComplete := row.AsInt64("ready_to_complete", 0) 742 assert.Equal(t, int64(1), readyToComplete) 743 } 744 }) 745 t.Run("t3drop complete", func(t *testing.T) { 746 // drop3 migration should not block. It can run concurrently to t1, and does not conflict 747 // even though t1drop is blocked! This is the heart of this test 748 drop3uuid := testOnlineDDLStatement(t, createParams(dropT3Statement, ddlStrategy+" -allow-concurrent", "vtgate", "", "", false)) 749 onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop3uuid, schema.OnlineDDLStatusComplete) 750 }) 751 t.Run("cancel drop1", func(t *testing.T) { 752 // drop1 migration should block. It can run concurrently to t1, but conflicts on table name 753 onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop1uuid, schema.OnlineDDLStatusReady) 754 // let's cancel it 755 onlineddl.CheckCancelMigration(t, &vtParams, shards, drop1uuid, true) 756 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, drop1uuid, normalWaitTime, schema.OnlineDDLStatusFailed, schema.OnlineDDLStatusCancelled) 757 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 758 onlineddl.CheckMigrationStatus(t, &vtParams, shards, drop1uuid, schema.OnlineDDLStatusCancelled) 759 }) 760 t.Run("complete t1", func(t *testing.T) { 761 // t1 should be still running! 762 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusRunning) 763 // Issue a complete and wait for successful completion 764 onlineddl.CheckCompleteMigration(t, &vtParams, shards, t1uuid, true) 765 // This part may take a while, because we depend on vreplication polling 766 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 767 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 768 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete) 769 }) 770 }) 771 772 t.Run("Idempotent submission, retry failed migration", func(t *testing.T) { 773 uuid := "00000000_1111_2222_3333_444444444444" 774 overrideVtctlParams = &cluster.VtctlClientParams{DDLStrategy: ddlStrategy, SkipPreflight: true, UUIDList: uuid, MigrationContext: "idempotent:1111-2222-3333"} 775 defer func() { overrideVtctlParams = nil }() 776 // create a migration and cancel it. We don't let it complete. We want it in "failed" state 777 t.Run("start and fail migration", func(t *testing.T) { 778 executedUUID := testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy+" -postpone-completion", "vtctl", "", "", true)) // skip wait 779 require.Equal(t, uuid, executedUUID) 780 onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, normalWaitTime, schema.OnlineDDLStatusRunning) 781 // let's cancel it 782 onlineddl.CheckCancelMigration(t, &vtParams, shards, uuid, true) 783 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, normalWaitTime, schema.OnlineDDLStatusFailed, schema.OnlineDDLStatusCancelled) 784 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 785 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusCancelled) 786 }) 787 788 // now, we submit the exact same migratoin again: same UUID, same migration context. 789 t.Run("resubmit migration", func(t *testing.T) { 790 executedUUID := testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy, "vtctl", "", "", true)) // skip wait 791 require.Equal(t, uuid, executedUUID) 792 793 // expect it to complete 794 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 795 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 796 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 797 798 rs := onlineddl.ReadMigrations(t, &vtParams, uuid) 799 require.NotNil(t, rs) 800 for _, row := range rs.Named().Rows { 801 retries := row.AsInt64("retries", 0) 802 assert.Greater(t, retries, int64(0)) 803 } 804 }) 805 }) 806 807 t.Run("Idempotent submission, retry failed migration in singleton context", func(t *testing.T) { 808 uuid := "00000000_1111_3333_3333_444444444444" 809 ddlStrategy := ddlStrategy + " --singleton-context" 810 overrideVtctlParams = &cluster.VtctlClientParams{DDLStrategy: ddlStrategy, SkipPreflight: true, UUIDList: uuid, MigrationContext: "idempotent:1111-3333-3333"} 811 defer func() { overrideVtctlParams = nil }() 812 // create a migration and cancel it. We don't let it complete. We want it in "failed" state 813 t.Run("start and fail migration", func(t *testing.T) { 814 executedUUID := testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy+" --postpone-completion", "vtctl", "", "", true)) // skip wait 815 require.Equal(t, uuid, executedUUID) 816 onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, normalWaitTime, schema.OnlineDDLStatusRunning) 817 // let's cancel it 818 onlineddl.CheckCancelMigration(t, &vtParams, shards, uuid, true) 819 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, normalWaitTime, schema.OnlineDDLStatusFailed, schema.OnlineDDLStatusCancelled) 820 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 821 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusCancelled) 822 }) 823 824 // now, we submit the exact same migratoin again: same UUID, same migration context. 825 t.Run("resubmit migration", func(t *testing.T) { 826 executedUUID := testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, ddlStrategy, "vtctl", "", "", true)) // skip wait 827 require.Equal(t, uuid, executedUUID) 828 829 // expect it to complete 830 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 831 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 832 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 833 834 rs := onlineddl.ReadMigrations(t, &vtParams, uuid) 835 require.NotNil(t, rs) 836 for _, row := range rs.Named().Rows { 837 retries := row.AsInt64("retries", 0) 838 assert.Greater(t, retries, int64(0)) 839 } 840 }) 841 }) 842 843 // INSTANT DDL 844 instantDDLCapable, err := capableOf(mysql.InstantAddLastColumnFlavorCapability) 845 require.NoError(t, err) 846 if instantDDLCapable { 847 t.Run("INSTANT DDL: postpone-completion", func(t *testing.T) { 848 t1uuid := testOnlineDDLStatement(t, createParams(instantAlterT1Statement, ddlStrategy+" --prefer-instant-ddl --postpone-completion", "vtgate", "", "", true)) 849 850 t.Run("expect t1 queued", func(t *testing.T) { 851 // we want to validate that the migration remains queued even after some time passes. It must not move beyond 'queued' 852 time.Sleep(ensureStateNotChangedTime) 853 onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady) 854 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady) 855 }) 856 t.Run("complete t1", func(t *testing.T) { 857 // Issue a complete and wait for successful completion 858 onlineddl.CheckCompleteMigration(t, &vtParams, shards, t1uuid, true) 859 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 860 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 861 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete) 862 }) 863 }) 864 } 865 // 'mysql' strategy 866 t.Run("mysql strategy", func(t *testing.T) { 867 t.Run("declarative", func(t *testing.T) { 868 t1uuid = testOnlineDDLStatement(t, createParams(createT1Statement, "mysql --declarative", "vtgate", "just-created", "", false)) 869 870 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 871 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 872 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete) 873 checkTable(t, t1Name, true) 874 }) 875 876 t.Run("fail postpone-completion", func(t *testing.T) { 877 t1uuid := testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, "mysql --postpone-completion", "vtgate", "", "", true)) 878 879 // --postpone-completion not supported in mysql strategy 880 time.Sleep(ensureStateNotChangedTime) 881 onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusFailed) 882 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusFailed) 883 }) 884 t.Run("trivial", func(t *testing.T) { 885 t1uuid := testOnlineDDLStatement(t, createParams(trivialAlterT1Statement, "mysql", "vtgate", "", "", true)) 886 887 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 888 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 889 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete) 890 891 rs := onlineddl.ReadMigrations(t, &vtParams, t1uuid) 892 require.NotNil(t, rs) 893 for _, row := range rs.Named().Rows { 894 artifacts := row.AsString("artifacts", "-") 895 assert.Empty(t, artifacts) 896 } 897 }) 898 t.Run("instant", func(t *testing.T) { 899 t1uuid := testOnlineDDLStatement(t, createParams(instantAlterT1Statement, "mysql", "vtgate", "", "", true)) 900 901 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 902 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 903 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete) 904 }) 905 }) 906 // in-order-completion 907 t.Run("in-order-completion: multiple drops for nonexistent tables and views", func(t *testing.T) { 908 u, err := schema.CreateOnlineDDLUUID() 909 require.NoError(t, err) 910 911 sqls := []string{ 912 fmt.Sprintf("drop table if exists t4_%s", u), 913 fmt.Sprintf("drop view if exists t1_%s", u), 914 fmt.Sprintf("drop table if exists t2_%s", u), 915 fmt.Sprintf("drop view if exists t3_%s", u), 916 } 917 sql := strings.Join(sqls, ";") 918 var vuuids []string 919 t.Run("drop multiple tables and views, in-order-completion", func(t *testing.T) { 920 uuidList := testOnlineDDLStatement(t, createParams(sql, ddlStrategy+" --allow-concurrent --in-order-completion", "vtctl", "", "", true)) // skip wait 921 vuuids = strings.Split(uuidList, "\n") 922 assert.Equal(t, 4, len(vuuids)) 923 for _, uuid := range vuuids { 924 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 925 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 926 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 927 } 928 }) 929 require.Equal(t, 4, len(vuuids)) 930 for i := range vuuids { 931 if i > 0 { 932 testTableCompletionTimes(t, vuuids[i-1], vuuids[i]) 933 } 934 } 935 }) 936 t.Run("in-order-completion: two new views, one depends on the other", func(t *testing.T) { 937 u, err := schema.CreateOnlineDDLUUID() 938 require.NoError(t, err) 939 v2name := fmt.Sprintf("v2_%s", u) 940 createv2 := fmt.Sprintf("create view %s as select id from t1_test", v2name) 941 v1name := fmt.Sprintf("v1_%s", u) 942 createv1 := fmt.Sprintf("create view %s as select id from %s", v1name, v2name) 943 944 sql := fmt.Sprintf("%s; %s;", createv2, createv1) 945 var vuuids []string 946 t.Run("create two views, expect both complete", func(t *testing.T) { 947 uuidList := testOnlineDDLStatement(t, createParams(sql, ddlStrategy+" --allow-concurrent --in-order-completion", "vtctl", "", "", true)) // skip wait 948 vuuids = strings.Split(uuidList, "\n") 949 assert.Equal(t, 2, len(vuuids)) 950 for _, uuid := range vuuids { 951 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 952 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 953 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 954 } 955 }) 956 require.Equal(t, 2, len(vuuids)) 957 testTableCompletionTimes(t, vuuids[0], vuuids[1]) 958 }) 959 t.Run("in-order-completion: new table column, new view depends on said column", func(t *testing.T) { 960 // The VIEW creation can only succeed when the ALTER has completed and the table has the new column 961 t1uuid = testOnlineDDLStatement(t, createParams(alterExtraColumn, ddlStrategy+" --allow-concurrent --postpone-completion --in-order-completion", "vtctl", "", "", true)) // skip wait 962 v1uuid := testOnlineDDLStatement(t, createParams(createViewDependsOnExtraColumn, ddlStrategy+" --allow-concurrent --postpone-completion --in-order-completion", "vtctl", "", "", true)) // skip wait 963 964 testAllowConcurrent(t, "t1", t1uuid, 1) 965 testAllowConcurrent(t, "v1", v1uuid, 1) 966 t.Run("expect table running, expect view ready", func(t *testing.T) { 967 onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusRunning) 968 onlineddl.WaitForMigrationStatus(t, &vtParams, shards, v1uuid, normalWaitTime, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady) 969 time.Sleep(ensureStateNotChangedTime) 970 // nothing should change 971 onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusRunning) 972 onlineddl.WaitForMigrationStatus(t, &vtParams, shards, v1uuid, normalWaitTime, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady) 973 }) 974 t.Run("complete both", func(t *testing.T) { 975 onlineddl.CheckCompleteAllMigrations(t, &vtParams, len(shards)*2) 976 }) 977 t.Run("expect table success", func(t *testing.T) { 978 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, t1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 979 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 980 onlineddl.CheckMigrationStatus(t, &vtParams, shards, t1uuid, schema.OnlineDDLStatusComplete) 981 }) 982 t.Run("expect view success", func(t *testing.T) { 983 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, v1uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 984 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 985 onlineddl.CheckMigrationStatus(t, &vtParams, shards, v1uuid, schema.OnlineDDLStatusComplete) 986 }) 987 testTableCompletionTimes(t, t1uuid, v1uuid) 988 }) 989 } 990 991 func testSingleton(t *testing.T) { 992 defer cluster.PanicHandler(t) 993 shards = clusterInstance.Keyspaces[0].Shards 994 require.Equal(t, 1, len(shards)) 995 996 createParams := func(ddlStatement string, ddlStrategy string, executeStrategy string, migrationContext string, expectHint string, expectError string, skipWait bool) *testOnlineDDLStatementParams { 997 return &testOnlineDDLStatementParams{ 998 ddlStatement: ddlStatement, 999 ddlStrategy: ddlStrategy, 1000 executeStrategy: executeStrategy, 1001 migrationContext: migrationContext, 1002 expectHint: expectHint, 1003 expectError: expectError, 1004 skipWait: skipWait, 1005 } 1006 } 1007 1008 createRevertParams := func(revertUUID string, ddlStrategy string, executeStrategy string, migrationContext string, expectError string, skipWait bool) *testRevertMigrationParams { 1009 return &testRevertMigrationParams{ 1010 revertUUID: revertUUID, 1011 executeStrategy: executeStrategy, 1012 ddlStrategy: ddlStrategy, 1013 migrationContext: migrationContext, 1014 expectError: expectError, 1015 skipWait: skipWait, 1016 } 1017 } 1018 1019 var ( 1020 tableName = `stress_test` 1021 onlineSingletonDDLStrategy = "online --singleton" 1022 onlineSingletonContextDDLStrategy = "online --singleton-context" 1023 createStatement = ` 1024 CREATE TABLE stress_test ( 1025 id bigint(20) not null, 1026 rand_val varchar(32) null default '', 1027 hint_col varchar(64) not null default 'just-created', 1028 created_timestamp timestamp not null default current_timestamp, 1029 updates int unsigned not null default 0, 1030 PRIMARY KEY (id), 1031 key created_idx(created_timestamp), 1032 key updates_idx(updates) 1033 ) ENGINE=InnoDB 1034 ` 1035 // We will run this query with "gh-ost --max-load=Threads_running=1" 1036 alterTableThrottlingStatement = ` 1037 ALTER TABLE stress_test DROP COLUMN created_timestamp 1038 ` 1039 multiAlterTableThrottlingStatement = ` 1040 ALTER TABLE stress_test ENGINE=InnoDB; 1041 ALTER TABLE stress_test ENGINE=InnoDB; 1042 ALTER TABLE stress_test ENGINE=InnoDB; 1043 ` 1044 // A trivial statement which must succeed and does not change the schema 1045 alterTableTrivialStatement = ` 1046 ALTER TABLE stress_test ENGINE=InnoDB 1047 ` 1048 dropStatement = ` 1049 DROP TABLE stress_test 1050 ` 1051 dropIfExistsStatement = ` 1052 DROP TABLE IF EXISTS stress_test 1053 ` 1054 dropNonexistentTableStatement = ` 1055 DROP TABLE IF EXISTS t_non_existent 1056 ` 1057 multiDropStatements = `DROP TABLE IF EXISTS t1; DROP TABLE IF EXISTS t2; DROP TABLE IF EXISTS t3;` 1058 ) 1059 1060 var uuids []string 1061 // init-cleanup 1062 t.Run("init DROP TABLE", func(t *testing.T) { 1063 uuid := testOnlineDDLStatement(t, createParams(dropIfExistsStatement, onlineSingletonDDLStrategy, "vtgate", "", "", "", false)) 1064 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1065 checkTable(t, tableName, false) 1066 }) 1067 1068 // CREATE 1069 t.Run("CREATE TABLE", func(t *testing.T) { 1070 // The table does not exist 1071 uuid := testOnlineDDLStatement(t, createParams(createStatement, onlineSingletonDDLStrategy, "vtgate", "", "", "", false)) 1072 uuids = append(uuids, uuid) 1073 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1074 checkTable(t, tableName, true) 1075 }) 1076 t.Run("revert CREATE TABLE", func(t *testing.T) { 1077 // The table existed, so it will now be dropped (renamed) 1078 uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1], onlineSingletonDDLStrategy, "vtgate", "", "", false)) 1079 uuids = append(uuids, uuid) 1080 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1081 checkTable(t, tableName, false) 1082 }) 1083 t.Run("revert revert CREATE TABLE", func(t *testing.T) { 1084 // Table was dropped (renamed) so it will now be restored 1085 uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1], onlineSingletonDDLStrategy, "vtgate", "", "", false)) 1086 uuids = append(uuids, uuid) 1087 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1088 checkTable(t, tableName, true) 1089 }) 1090 1091 var throttledUUID string 1092 t.Run("throttled migration", func(t *testing.T) { 1093 throttledUUID = testOnlineDDLStatement(t, createParams(alterTableThrottlingStatement, "gh-ost --singleton --max-load=Threads_running=1", "vtgate", "", "hint_col", "", false)) 1094 onlineddl.CheckMigrationStatus(t, &vtParams, shards, throttledUUID, schema.OnlineDDLStatusRunning) 1095 }) 1096 t.Run("failed singleton migration, vtgate", func(t *testing.T) { 1097 uuid := testOnlineDDLStatement(t, createParams(alterTableThrottlingStatement, "gh-ost --singleton --max-load=Threads_running=1", "vtgate", "", "hint_col", "rejected", true)) 1098 assert.Empty(t, uuid) 1099 }) 1100 t.Run("failed singleton migration, vtctl", func(t *testing.T) { 1101 uuid := testOnlineDDLStatement(t, createParams(alterTableThrottlingStatement, "gh-ost --singleton --max-load=Threads_running=1", "vtctl", "", "hint_col", "rejected", true)) 1102 assert.Empty(t, uuid) 1103 }) 1104 t.Run("failed revert migration", func(t *testing.T) { 1105 uuid := testRevertMigration(t, createRevertParams(throttledUUID, onlineSingletonDDLStrategy, "vtgate", "", "rejected", true)) 1106 assert.Empty(t, uuid) 1107 }) 1108 t.Run("terminate throttled migration", func(t *testing.T) { 1109 onlineddl.CheckMigrationStatus(t, &vtParams, shards, throttledUUID, schema.OnlineDDLStatusRunning) 1110 onlineddl.CheckCancelMigration(t, &vtParams, shards, throttledUUID, true) 1111 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, throttledUUID, 20*time.Second, schema.OnlineDDLStatusFailed, schema.OnlineDDLStatusCancelled) 1112 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 1113 onlineddl.CheckMigrationStatus(t, &vtParams, shards, throttledUUID, schema.OnlineDDLStatusCancelled) 1114 }) 1115 t.Run("successful gh-ost alter, vtctl", func(t *testing.T) { 1116 uuid := testOnlineDDLStatement(t, createParams(alterTableTrivialStatement, "gh-ost --singleton", "vtctl", "", "hint_col", "", false)) 1117 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1118 onlineddl.CheckCancelMigration(t, &vtParams, shards, uuid, false) 1119 onlineddl.CheckRetryMigration(t, &vtParams, shards, uuid, false) 1120 }) 1121 t.Run("successful gh-ost alter, vtgate", func(t *testing.T) { 1122 uuid := testOnlineDDLStatement(t, createParams(alterTableTrivialStatement, "gh-ost --singleton", "vtgate", "", "hint_col", "", false)) 1123 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1124 onlineddl.CheckCancelMigration(t, &vtParams, shards, uuid, false) 1125 onlineddl.CheckRetryMigration(t, &vtParams, shards, uuid, false) 1126 }) 1127 1128 t.Run("successful online alter, vtgate", func(t *testing.T) { 1129 uuid := testOnlineDDLStatement(t, createParams(alterTableTrivialStatement, onlineSingletonDDLStrategy, "vtgate", "", "hint_col", "", false)) 1130 uuids = append(uuids, uuid) 1131 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1132 onlineddl.CheckCancelMigration(t, &vtParams, shards, uuid, false) 1133 onlineddl.CheckRetryMigration(t, &vtParams, shards, uuid, false) 1134 checkTable(t, tableName, true) 1135 }) 1136 t.Run("revert ALTER TABLE, vttablet", func(t *testing.T) { 1137 // The table existed, so it will now be dropped (renamed) 1138 uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1], onlineSingletonDDLStrategy, "vtctl", "", "", false)) 1139 uuids = append(uuids, uuid) 1140 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1141 checkTable(t, tableName, true) 1142 }) 1143 1144 var throttledUUIDs []string 1145 // singleton-context 1146 t.Run("throttled migrations, singleton-context", func(t *testing.T) { 1147 uuidList := testOnlineDDLStatement(t, createParams(multiAlterTableThrottlingStatement, "gh-ost --singleton-context --max-load=Threads_running=1", "vtctl", "", "hint_col", "", false)) 1148 throttledUUIDs = strings.Split(uuidList, "\n") 1149 assert.Equal(t, 3, len(throttledUUIDs)) 1150 for _, uuid := range throttledUUIDs { 1151 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady, schema.OnlineDDLStatusRunning) 1152 } 1153 }) 1154 t.Run("failed migrations, singleton-context", func(t *testing.T) { 1155 _ = testOnlineDDLStatement(t, createParams(multiAlterTableThrottlingStatement, "gh-ost --singleton-context --max-load=Threads_running=1", "vtctl", "", "hint_col", "rejected", false)) 1156 }) 1157 t.Run("terminate throttled migrations", func(t *testing.T) { 1158 for _, uuid := range throttledUUIDs { 1159 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusQueued, schema.OnlineDDLStatusReady, schema.OnlineDDLStatusRunning) 1160 onlineddl.CheckCancelMigration(t, &vtParams, shards, uuid, true) 1161 } 1162 time.Sleep(2 * time.Second) 1163 for _, uuid := range throttledUUIDs { 1164 uuid = strings.TrimSpace(uuid) 1165 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusFailed, schema.OnlineDDLStatusCancelled) 1166 } 1167 }) 1168 1169 t.Run("successful multiple statement, singleton-context, vtctl", func(t *testing.T) { 1170 uuidList := testOnlineDDLStatement(t, createParams(multiDropStatements, onlineSingletonContextDDLStrategy, "vtctl", "", "", "", false)) 1171 uuidSlice := strings.Split(uuidList, "\n") 1172 assert.Equal(t, 3, len(uuidSlice)) 1173 for _, uuid := range uuidSlice { 1174 uuid = strings.TrimSpace(uuid) 1175 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1176 } 1177 }) 1178 1179 //DROP 1180 1181 t.Run("online DROP TABLE", func(t *testing.T) { 1182 uuid := testOnlineDDLStatement(t, createParams(dropStatement, onlineSingletonDDLStrategy, "vtgate", "", "", "", false)) 1183 uuids = append(uuids, uuid) 1184 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1185 checkTable(t, tableName, false) 1186 }) 1187 t.Run("revert DROP TABLE", func(t *testing.T) { 1188 // This will recreate the table (well, actually, rename it back into place) 1189 uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1], onlineSingletonDDLStrategy, "vttablet", "", "", false)) 1190 uuids = append(uuids, uuid) 1191 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1192 checkTable(t, tableName, true) 1193 }) 1194 1195 t.Run("fail concurrent singleton, vtgate", func(t *testing.T) { 1196 uuid := testOnlineDDLStatement(t, createParams(alterTableTrivialStatement, "vitess --postpone-completion --singleton", "vtgate", "", "hint_col", "", true)) 1197 uuids = append(uuids, uuid) 1198 _ = testOnlineDDLStatement(t, createParams(dropNonexistentTableStatement, "vitess --singleton", "vtgate", "", "hint_col", "rejected", true)) 1199 onlineddl.CheckCompleteAllMigrations(t, &vtParams, len(shards)) 1200 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, 20*time.Second, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 1201 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 1202 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1203 }) 1204 t.Run("fail concurrent singleton-context with revert", func(t *testing.T) { 1205 revertUUID := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1], "vitess --allow-concurrent --postpone-completion --singleton-context", "vtctl", "rev:ctx", "", false)) 1206 onlineddl.WaitForMigrationStatus(t, &vtParams, shards, revertUUID, 20*time.Second, schema.OnlineDDLStatusRunning) 1207 // revert is running 1208 _ = testOnlineDDLStatement(t, createParams(dropNonexistentTableStatement, "vitess --allow-concurrent --singleton-context", "vtctl", "migrate:ctx", "", "rejected", true)) 1209 onlineddl.CheckCancelMigration(t, &vtParams, shards, revertUUID, true) 1210 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, revertUUID, 20*time.Second, schema.OnlineDDLStatusFailed, schema.OnlineDDLStatusCancelled) 1211 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 1212 onlineddl.CheckMigrationStatus(t, &vtParams, shards, revertUUID, schema.OnlineDDLStatusCancelled) 1213 }) 1214 t.Run("success concurrent singleton-context with no-context revert", func(t *testing.T) { 1215 revertUUID := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1], "vitess --allow-concurrent --postpone-completion", "vtctl", "rev:ctx", "", false)) 1216 onlineddl.WaitForMigrationStatus(t, &vtParams, shards, revertUUID, 20*time.Second, schema.OnlineDDLStatusRunning) 1217 // revert is running but has no --singleton-context. Our next migration should be able to run. 1218 uuid := testOnlineDDLStatement(t, createParams(dropNonexistentTableStatement, "vitess --allow-concurrent --singleton-context", "vtctl", "migrate:ctx", "", "", false)) 1219 uuids = append(uuids, uuid) 1220 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1221 onlineddl.CheckCancelMigration(t, &vtParams, shards, revertUUID, true) 1222 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, revertUUID, 20*time.Second, schema.OnlineDDLStatusFailed, schema.OnlineDDLStatusCancelled) 1223 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 1224 onlineddl.CheckMigrationStatus(t, &vtParams, shards, revertUUID, schema.OnlineDDLStatusCancelled) 1225 }) 1226 } 1227 func testDeclarative(t *testing.T) { 1228 defer cluster.PanicHandler(t) 1229 shards = clusterInstance.Keyspaces[0].Shards 1230 require.Equal(t, 1, len(shards)) 1231 1232 var ( 1233 tableName = `stress_test` 1234 viewBaseTableName = `view_base_table_test` 1235 viewName = `view_test` 1236 migrationContext = "1111-2222-3333" 1237 createStatement1 = ` 1238 CREATE TABLE stress_test ( 1239 id bigint(20) not null, 1240 rand_val varchar(32) null default '', 1241 hint_col varchar(64) not null default 'create1', 1242 created_timestamp timestamp not null default current_timestamp, 1243 updates int unsigned not null default 0, 1244 PRIMARY KEY (id), 1245 key created_idx(created_timestamp), 1246 key updates_idx(updates) 1247 ) ENGINE=InnoDB 1248 ` 1249 createStatement2 = ` 1250 CREATE TABLE stress_test ( 1251 id bigint(20) not null, 1252 rand_val varchar(32) null default '', 1253 hint_col varchar(64) not null default 'create2', 1254 created_timestamp timestamp not null default current_timestamp, 1255 updates int unsigned not null default 0, 1256 PRIMARY KEY (id), 1257 key created_idx(created_timestamp), 1258 key updates_idx(updates) 1259 ) ENGINE=InnoDB 1260 ` 1261 createIfNotExistsStatement = ` 1262 CREATE TABLE IF NOT EXISTS stress_test ( 1263 id bigint(20) not null, 1264 PRIMARY KEY (id) 1265 ) ENGINE=InnoDB 1266 ` 1267 createStatementZeroDate = ` 1268 CREATE TABLE zerodate_test ( 1269 id bigint(20) not null, 1270 hint_col varchar(64) not null default 'create_with_zero', 1271 zero_datetime datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 1272 PRIMARY KEY (id) 1273 ) ENGINE=InnoDB 1274 ` 1275 createStatementZeroDate2 = ` 1276 CREATE TABLE zerodate_test ( 1277 id bigint(20) not null, 1278 i int not null default 0, 1279 hint_col varchar(64) not null default 'create_with_zero2', 1280 zero_datetime datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 1281 zero_datetime2 datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 1282 PRIMARY KEY (id) 1283 ) ENGINE=InnoDB 1284 ` 1285 dropZeroDateStatement = ` 1286 DROP TABLE zerodate_test 1287 ` 1288 dropStatement = ` 1289 DROP TABLE stress_test 1290 ` 1291 dropIfExistsStatement = ` 1292 DROP TABLE IF EXISTS stress_test 1293 ` 1294 alterStatement = ` 1295 ALTER TABLE stress_test modify hint_col varchar(64) not null default 'this-should-fail' 1296 ` 1297 trivialAlterStatement = ` 1298 ALTER TABLE stress_test ENGINE=InnoDB 1299 ` 1300 dropViewBaseTableStatement = ` 1301 DROP TABLE IF EXISTS view_base_table_test 1302 ` 1303 createViewBaseTableStatement = ` 1304 CREATE TABLE view_base_table_test (id INT PRIMARY KEY) 1305 ` 1306 createViewStatement1 = ` 1307 CREATE VIEW view_test AS SELECT 'success_create1' AS msg FROM view_base_table_test 1308 ` 1309 createViewStatement2 = ` 1310 CREATE VIEW view_test AS SELECT 'success_create2' AS msg FROM view_base_table_test 1311 ` 1312 createOrReplaceViewStatement = ` 1313 CREATE OR REPLACE VIEW view_test AS SELECT 'success_replace' AS msg FROM view_base_table_test 1314 ` 1315 alterViewStatement = ` 1316 ALTER VIEW view_test AS SELECT 'success_alter' AS msg FROM view_base_table_test 1317 ` 1318 dropViewStatement = ` 1319 DROP VIEW view_test 1320 ` 1321 dropViewIfExistsStatement = ` 1322 DROP VIEW IF EXISTS view_test 1323 ` 1324 insertRowStatement = ` 1325 INSERT IGNORE INTO stress_test (id, rand_val) VALUES (%d, left(md5(rand()), 8)) 1326 ` 1327 updateRowStatement = ` 1328 UPDATE stress_test SET updates=updates+1 WHERE id=%d 1329 ` 1330 deleteRowStatement = ` 1331 DELETE FROM stress_test WHERE id=%d AND updates=1 1332 ` 1333 // We use CAST(SUM(updates) AS SIGNED) because SUM() returns a DECIMAL datatype, and we want to read a SIGNED INTEGER type 1334 selectCountRowsStatement = ` 1335 SELECT COUNT(*) AS num_rows, CAST(SUM(updates) AS SIGNED) AS sum_updates FROM stress_test 1336 ` 1337 truncateStatement = ` 1338 TRUNCATE TABLE stress_test 1339 ` 1340 writeMetrics WriteMetrics 1341 maxTableRows = 4096 1342 ) 1343 1344 declarativeStrategy := "online -declarative" 1345 var uuids []string 1346 1347 generateInsert := func(t *testing.T, conn *mysql.Conn) error { 1348 id := rand.Int31n(int32(maxTableRows)) 1349 query := fmt.Sprintf(insertRowStatement, id) 1350 qr, err := conn.ExecuteFetch(query, 1000, true) 1351 1352 func() { 1353 writeMetrics.mu.Lock() 1354 defer writeMetrics.mu.Unlock() 1355 1356 writeMetrics.insertsAttempts++ 1357 if err != nil { 1358 writeMetrics.insertsFailures++ 1359 return 1360 } 1361 assert.Less(t, qr.RowsAffected, uint64(2)) 1362 if qr.RowsAffected == 0 { 1363 writeMetrics.insertsNoops++ 1364 return 1365 } 1366 writeMetrics.inserts++ 1367 }() 1368 return err 1369 } 1370 1371 generateUpdate := func(t *testing.T, conn *mysql.Conn) error { 1372 id := rand.Int31n(int32(maxTableRows)) 1373 query := fmt.Sprintf(updateRowStatement, id) 1374 qr, err := conn.ExecuteFetch(query, 1000, true) 1375 1376 func() { 1377 writeMetrics.mu.Lock() 1378 defer writeMetrics.mu.Unlock() 1379 1380 writeMetrics.updatesAttempts++ 1381 if err != nil { 1382 writeMetrics.updatesFailures++ 1383 return 1384 } 1385 assert.Less(t, qr.RowsAffected, uint64(2)) 1386 if qr.RowsAffected == 0 { 1387 writeMetrics.updatesNoops++ 1388 return 1389 } 1390 writeMetrics.updates++ 1391 }() 1392 return err 1393 } 1394 1395 generateDelete := func(t *testing.T, conn *mysql.Conn) error { 1396 id := rand.Int31n(int32(maxTableRows)) 1397 query := fmt.Sprintf(deleteRowStatement, id) 1398 qr, err := conn.ExecuteFetch(query, 1000, true) 1399 1400 func() { 1401 writeMetrics.mu.Lock() 1402 defer writeMetrics.mu.Unlock() 1403 1404 writeMetrics.deletesAttempts++ 1405 if err != nil { 1406 writeMetrics.deletesFailures++ 1407 return 1408 } 1409 assert.Less(t, qr.RowsAffected, uint64(2)) 1410 if qr.RowsAffected == 0 { 1411 writeMetrics.deletesNoops++ 1412 return 1413 } 1414 writeMetrics.deletes++ 1415 }() 1416 return err 1417 } 1418 1419 initTable := func(t *testing.T) { 1420 log.Infof("initTable begin") 1421 defer log.Infof("initTable complete") 1422 1423 ctx := context.Background() 1424 conn, err := mysql.Connect(ctx, &vtParams) 1425 require.Nil(t, err) 1426 defer conn.Close() 1427 1428 writeMetrics.Clear() 1429 _, err = conn.ExecuteFetch(truncateStatement, 1000, true) 1430 require.Nil(t, err) 1431 1432 for i := 0; i < maxTableRows/2; i++ { 1433 generateInsert(t, conn) 1434 } 1435 for i := 0; i < maxTableRows/4; i++ { 1436 generateUpdate(t, conn) 1437 } 1438 for i := 0; i < maxTableRows/4; i++ { 1439 generateDelete(t, conn) 1440 } 1441 } 1442 1443 testSelectTableMetrics := func(t *testing.T) { 1444 writeMetrics.mu.Lock() 1445 defer writeMetrics.mu.Unlock() 1446 1447 log.Infof("%s", writeMetrics.String()) 1448 1449 ctx := context.Background() 1450 conn, err := mysql.Connect(ctx, &vtParams) 1451 require.Nil(t, err) 1452 defer conn.Close() 1453 1454 rs, err := conn.ExecuteFetch(selectCountRowsStatement, 1000, true) 1455 require.Nil(t, err) 1456 1457 row := rs.Named().Row() 1458 require.NotNil(t, row) 1459 log.Infof("testSelectTableMetrics, row: %v", row) 1460 numRows := row.AsInt64("num_rows", 0) 1461 sumUpdates := row.AsInt64("sum_updates", 0) 1462 1463 assert.NotZero(t, numRows) 1464 assert.NotZero(t, sumUpdates) 1465 assert.NotZero(t, writeMetrics.inserts) 1466 assert.NotZero(t, writeMetrics.deletes) 1467 assert.NotZero(t, writeMetrics.updates) 1468 assert.Equal(t, writeMetrics.inserts-writeMetrics.deletes, numRows) 1469 assert.Equal(t, writeMetrics.updates-writeMetrics.deletes, sumUpdates) // because we DELETE WHERE updates=1 1470 } 1471 1472 testOnlineDDL := func(t *testing.T, alterStatement string, ddlStrategy string, executeStrategy string, expectHint string, expectError string) (uuid string) { 1473 params := &testOnlineDDLStatementParams{ 1474 ddlStatement: alterStatement, 1475 ddlStrategy: ddlStrategy, 1476 executeStrategy: executeStrategy, 1477 expectHint: expectHint, 1478 expectError: expectError, 1479 } 1480 if executeStrategy != "vtgate" { 1481 params.migrationContext = migrationContext 1482 } 1483 return testOnlineDDLStatement(t, params) 1484 } 1485 createRevertParams := func(revertUUID string) *testRevertMigrationParams { 1486 return &testRevertMigrationParams{ 1487 revertUUID: revertUUID, 1488 executeStrategy: "vtctl", 1489 ddlStrategy: string(schema.DDLStrategyOnline), 1490 } 1491 } 1492 1493 // init-cleaup 1494 t.Run("init: drop table", func(t *testing.T) { 1495 // IF EXISTS is not supported in -declarative 1496 uuid := testOnlineDDL(t, dropIfExistsStatement, "online", "vtgate", "", "") 1497 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1498 }) 1499 t.Run("init: drop view base table", func(t *testing.T) { 1500 // IF EXISTS is not supported in -declarative 1501 uuid := testOnlineDDL(t, dropViewBaseTableStatement, "online", "vtgate", "", "") 1502 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1503 }) 1504 1505 // VIEWS 1506 t.Run("create base table for view", func(t *testing.T) { 1507 uuid := testOnlineDDL(t, createViewBaseTableStatement, declarativeStrategy, "vtgate", "", "") 1508 uuids = append(uuids, uuid) 1509 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1510 checkTable(t, viewBaseTableName, true) 1511 }) 1512 // CREATE VIEW 1 1513 t.Run("declarative CREATE VIEW where table does not exist", func(t *testing.T) { 1514 // The table does not exist 1515 uuid := testOnlineDDL(t, createViewStatement1, declarativeStrategy, "vtgate", "success_create1", "") 1516 uuids = append(uuids, uuid) 1517 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1518 onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true) 1519 checkTable(t, viewName, true) 1520 }) 1521 // CREATE VIEW 1 again, noop 1522 t.Run("declarative CREATE VIEW with no changes where view exists", func(t *testing.T) { 1523 // The exists with exact same schema 1524 uuid := testOnlineDDL(t, createViewStatement1, declarativeStrategy, "vtgate", "success_create1", "") 1525 uuids = append(uuids, uuid) 1526 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1527 onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, false) 1528 checkTable(t, viewName, true) 1529 }) 1530 t.Run("revert CREATE VIEW expecting noop", func(t *testing.T) { 1531 // Reverting a noop changes nothing 1532 uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1])) 1533 uuids = append(uuids, uuid) 1534 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1535 checkMigratedTable(t, viewName, "success_create1") 1536 checkTable(t, viewName, true) 1537 }) 1538 // CREATE OR REPLACE VIEW 1539 t.Run("CREATE OR REPLACE VIEW expecting failure", func(t *testing.T) { 1540 // IF NOT EXISTS is not supported in -declarative 1541 uuid := testOnlineDDL(t, createOrReplaceViewStatement, declarativeStrategy, "vtgate", "", "") 1542 uuids = append(uuids, uuid) 1543 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusFailed) 1544 checkMigratedTable(t, viewName, "success_create1") 1545 checkTable(t, viewName, true) 1546 }) 1547 t.Run("ALTER VIEW expecting failure", func(t *testing.T) { 1548 // IF NOT EXISTS is not supported in -declarative 1549 uuid := testOnlineDDL(t, alterViewStatement, declarativeStrategy, "vtgate", "", "") 1550 uuids = append(uuids, uuid) 1551 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusFailed) 1552 checkMigratedTable(t, viewName, "success_create1") 1553 checkTable(t, viewName, true) 1554 }) 1555 t.Run("DROP VIEW IF EXISTS expecting failure", func(t *testing.T) { 1556 // IF NOT EXISTS is not supported in -declarative 1557 uuid := testOnlineDDL(t, dropViewIfExistsStatement, declarativeStrategy, "vtgate", "", "") 1558 uuids = append(uuids, uuid) 1559 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusFailed) 1560 checkMigratedTable(t, viewName, "success_create1") 1561 checkTable(t, viewName, true) 1562 }) 1563 t.Run("declarative DROP VIEW", func(t *testing.T) { 1564 uuid := testOnlineDDL(t, dropViewStatement, declarativeStrategy, "vtgate", "", "") 1565 uuids = append(uuids, uuid) 1566 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1567 onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true) 1568 checkTable(t, viewName, false) 1569 }) 1570 // View dropped. Let's start afresh. 1571 1572 // CREATE VIEW1 1573 t.Run("declarative CREATE VIEW where view does not exist", func(t *testing.T) { 1574 // The table does not exist 1575 uuid := testOnlineDDL(t, createViewStatement1, declarativeStrategy, "vtgate", "success_create1", "") 1576 uuids = append(uuids, uuid) 1577 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1578 onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true) 1579 checkTable(t, viewName, true) 1580 }) 1581 // CREATE VIEW2: Change view 1582 t.Run("declarative CREATE VIEW with changes where view exists", func(t *testing.T) { 1583 // The table exists with different schema 1584 uuid := testOnlineDDL(t, createViewStatement2, declarativeStrategy, "vtgate", "success_create2", "") 1585 uuids = append(uuids, uuid) 1586 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1587 onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true) 1588 checkTable(t, viewName, true) 1589 }) 1590 t.Run("revert CREATE VIEW expecting previous schema", func(t *testing.T) { 1591 // Reverting back to 1st version 1592 uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1])) 1593 uuids = append(uuids, uuid) 1594 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1595 checkMigratedTable(t, viewName, "success_create1") 1596 checkTable(t, viewName, true) 1597 }) 1598 t.Run("declarative DROP VIEW", func(t *testing.T) { 1599 // Table exists 1600 uuid := testOnlineDDL(t, dropViewStatement, declarativeStrategy, "vtgate", "", "") 1601 uuids = append(uuids, uuid) 1602 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1603 onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true) 1604 checkTable(t, viewName, false) 1605 }) 1606 t.Run("revert DROP VIEW", func(t *testing.T) { 1607 // This will recreate the table (well, actually, rename it back into place) 1608 uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1])) 1609 uuids = append(uuids, uuid) 1610 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1611 checkTable(t, viewName, true) 1612 checkMigratedTable(t, viewName, "success_create1") 1613 }) 1614 t.Run("revert revert DROP VIEW", func(t *testing.T) { 1615 // This will reapply DROP VIEW 1616 uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1])) 1617 uuids = append(uuids, uuid) 1618 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1619 checkTable(t, viewName, false) 1620 }) 1621 t.Run("declarative DROP VIEW where view does not exist", func(t *testing.T) { 1622 uuid := testOnlineDDL(t, dropViewStatement, declarativeStrategy, "vtgate", "", "") 1623 uuids = append(uuids, uuid) 1624 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1625 onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, false) 1626 checkTable(t, viewName, false) 1627 }) 1628 t.Run("revert DROP VIEW where view did not exist", func(t *testing.T) { 1629 // Table will not be recreated because it didn't exist during the previous DROP VIEW 1630 uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1])) 1631 uuids = append(uuids, uuid) 1632 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1633 checkTable(t, viewName, false) 1634 }) 1635 // View dropped. Let's start afresh. 1636 1637 // TABLES 1638 1639 // CREATE1 1640 t.Run("declarative CREATE TABLE where table does not exist", func(t *testing.T) { 1641 // The table does not exist 1642 uuid := testOnlineDDL(t, createStatement1, declarativeStrategy, "vtgate", "create1", "") 1643 uuids = append(uuids, uuid) 1644 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1645 onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true) 1646 checkTable(t, tableName, true) 1647 initTable(t) 1648 testSelectTableMetrics(t) 1649 }) 1650 // CREATE1 again, noop 1651 t.Run("declarative CREATE TABLE with no changes where table exists", func(t *testing.T) { 1652 // The exists with exact same schema 1653 uuid := testOnlineDDL(t, createStatement1, declarativeStrategy, "vtgate", "create1", "") 1654 uuids = append(uuids, uuid) 1655 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1656 onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, false) 1657 checkTable(t, tableName, true) 1658 testSelectTableMetrics(t) 1659 }) 1660 t.Run("revert CREATE TABLE expecting noop", func(t *testing.T) { 1661 // Reverting a noop changes nothing 1662 uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1])) 1663 uuids = append(uuids, uuid) 1664 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1665 checkMigratedTable(t, tableName, "create1") 1666 checkTable(t, tableName, true) 1667 testSelectTableMetrics(t) 1668 }) 1669 t.Run("declarative DROP TABLE", func(t *testing.T) { 1670 uuid := testOnlineDDL(t, dropStatement, declarativeStrategy, "vtgate", "", "") 1671 uuids = append(uuids, uuid) 1672 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1673 onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true) 1674 checkTable(t, tableName, false) 1675 }) 1676 // Table dropped. Let's start afresh. 1677 1678 // CREATE1 1679 t.Run("declarative CREATE TABLE where table does not exist", func(t *testing.T) { 1680 // The table does not exist 1681 uuid := testOnlineDDL(t, createStatement1, declarativeStrategy, "vtgate", "create1", "") 1682 uuids = append(uuids, uuid) 1683 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1684 onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true) 1685 checkTable(t, tableName, true) 1686 initTable(t) 1687 testSelectTableMetrics(t) 1688 }) 1689 // CREATE2: Change schema 1690 t.Run("declarative CREATE TABLE with changes where table exists", func(t *testing.T) { 1691 // The table exists with different schema 1692 uuid := testOnlineDDL(t, createStatement2, declarativeStrategy, "vtgate", "create2", "") 1693 uuids = append(uuids, uuid) 1694 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1695 onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true) 1696 checkTable(t, tableName, true) 1697 testSelectTableMetrics(t) 1698 }) 1699 t.Run("revert CREATE TABLE expecting previous schema", func(t *testing.T) { 1700 // Reverting back to 1st version 1701 uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1])) 1702 uuids = append(uuids, uuid) 1703 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1704 checkMigratedTable(t, tableName, "create1") 1705 checkTable(t, tableName, true) 1706 testSelectTableMetrics(t) 1707 }) 1708 t.Run("declarative DROP TABLE", func(t *testing.T) { 1709 // Table exists 1710 uuid := testOnlineDDL(t, dropStatement, declarativeStrategy, "vtgate", "", "") 1711 uuids = append(uuids, uuid) 1712 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1713 onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true) 1714 checkTable(t, tableName, false) 1715 }) 1716 t.Run("revert DROP TABLE", func(t *testing.T) { 1717 // This will recreate the table (well, actually, rename it back into place) 1718 uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1])) 1719 uuids = append(uuids, uuid) 1720 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1721 checkTable(t, tableName, true) 1722 checkMigratedTable(t, tableName, "create1") 1723 testSelectTableMetrics(t) 1724 }) 1725 t.Run("revert revert DROP TABLE", func(t *testing.T) { 1726 // This will reapply DROP TABLE 1727 uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1])) 1728 uuids = append(uuids, uuid) 1729 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1730 checkTable(t, tableName, false) 1731 }) 1732 t.Run("declarative DROP TABLE where table does not exist", func(t *testing.T) { 1733 uuid := testOnlineDDL(t, dropStatement, declarativeStrategy, "vtgate", "", "") 1734 uuids = append(uuids, uuid) 1735 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1736 onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, false) 1737 checkTable(t, tableName, false) 1738 }) 1739 t.Run("revert DROP TABLE where table did not exist", func(t *testing.T) { 1740 // Table will not be recreated because it didn't exist during the previous DROP TABLE 1741 uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1])) 1742 uuids = append(uuids, uuid) 1743 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1744 checkTable(t, tableName, false) 1745 }) 1746 // Table dropped. Let's start afresh. 1747 1748 // CREATE1 1749 t.Run("declarative CREATE TABLE where table does not exist", func(t *testing.T) { 1750 // The table does not exist 1751 uuid := testOnlineDDL(t, createStatement1, declarativeStrategy, "vtgate", "create1", "") 1752 uuids = append(uuids, uuid) 1753 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1754 onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true) 1755 checkTable(t, tableName, true) 1756 initTable(t) 1757 testSelectTableMetrics(t) 1758 }) 1759 // CREATE2 1760 t.Run("declarative CREATE TABLE with changes where table exists", func(t *testing.T) { 1761 // The table exists but with different schema 1762 uuid := testOnlineDDL(t, createStatement2, declarativeStrategy, "vtgate", "create2", "") 1763 uuids = append(uuids, uuid) 1764 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1765 onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true) 1766 checkTable(t, tableName, true) 1767 testSelectTableMetrics(t) 1768 }) 1769 // CREATE1 again 1770 t.Run("declarative CREATE TABLE again with changes where table exists", func(t *testing.T) { 1771 // The table exists but with different schema 1772 uuid := testOnlineDDL(t, createStatement1, declarativeStrategy, "vtgate", "create1", "") 1773 uuids = append(uuids, uuid) 1774 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1775 onlineddl.CheckMigrationArtifacts(t, &vtParams, shards, uuid, true) 1776 checkTable(t, tableName, true) 1777 testSelectTableMetrics(t) 1778 }) 1779 t.Run("revert CREATE TABLE expecting previous schema", func(t *testing.T) { 1780 // Reverting back to previous version 1781 uuid := testRevertMigration(t, createRevertParams(uuids[len(uuids)-1])) 1782 uuids = append(uuids, uuid) 1783 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1784 checkMigratedTable(t, tableName, "create2") 1785 checkTable(t, tableName, true) 1786 testSelectTableMetrics(t) 1787 }) 1788 t.Run("ALTER TABLE expecting failure", func(t *testing.T) { 1789 // ALTER is not supported in -declarative 1790 uuid := testOnlineDDL(t, alterStatement, declarativeStrategy, "vtgate", "", "") 1791 uuids = append(uuids, uuid) 1792 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusFailed) 1793 checkMigratedTable(t, tableName, "create2") 1794 checkTable(t, tableName, true) 1795 testSelectTableMetrics(t) 1796 }) 1797 t.Run("CREATE TABLE IF NOT EXISTS expecting failure", func(t *testing.T) { 1798 // IF NOT EXISTS is not supported in -declarative 1799 uuid := testOnlineDDL(t, createIfNotExistsStatement, declarativeStrategy, "vtgate", "", "") 1800 uuids = append(uuids, uuid) 1801 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusFailed) 1802 checkMigratedTable(t, tableName, "create2") 1803 checkTable(t, tableName, true) 1804 testSelectTableMetrics(t) 1805 }) 1806 t.Run("DROP TABLE IF EXISTS expecting failure", func(t *testing.T) { 1807 // IF EXISTS is not supported in -declarative 1808 uuid := testOnlineDDL(t, dropIfExistsStatement, declarativeStrategy, "vtgate", "", "") 1809 uuids = append(uuids, uuid) 1810 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusFailed) 1811 checkMigratedTable(t, tableName, "create2") 1812 checkTable(t, tableName, true) 1813 testSelectTableMetrics(t) 1814 }) 1815 t.Run("CREATE TABLE IF NOT EXISTS non-declarative is successful", func(t *testing.T) { 1816 // IF NOT EXISTS is supported in non-declarative mode. Just verifying that the statement itself is good, 1817 // so that the failure we tested for, above, actually tests the "declarative" logic, rather than some 1818 // unrelated error. 1819 uuid := testOnlineDDL(t, createIfNotExistsStatement, "online", "vtgate", "", "") 1820 uuids = append(uuids, uuid) 1821 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1822 // the table existed, so we expect no changes in this non-declarative DDL 1823 checkMigratedTable(t, tableName, "create2") 1824 checkTable(t, tableName, true) 1825 testSelectTableMetrics(t) 1826 }) 1827 t.Run("CREATE TABLE with zero date and --allow-zero-in-date is successful", func(t *testing.T) { 1828 uuid := testOnlineDDL(t, createStatementZeroDate, "online --allow-zero-in-date", "vtgate", "", "") 1829 uuids = append(uuids, uuid) 1830 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1831 checkMigratedTable(t, "zerodate_test", "create_with_zero") 1832 checkTable(t, tableName, true) 1833 testSelectTableMetrics(t) 1834 }) 1835 t.Run("CREATE TABLE with zero date and --allow-zero-in-date is successful", func(t *testing.T) { 1836 uuid := testOnlineDDL(t, createStatementZeroDate, "online -declarative --allow-zero-in-date", "vtgate", "", "") 1837 uuids = append(uuids, uuid) 1838 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1839 checkMigratedTable(t, "zerodate_test", "create_with_zero") 1840 checkTable(t, tableName, true) 1841 testSelectTableMetrics(t) 1842 }) 1843 t.Run("CREATE TABLE with zero date and --allow-zero-in-date is successful", func(t *testing.T) { 1844 uuid := testOnlineDDL(t, createStatementZeroDate2, "online -declarative --allow-zero-in-date", "vtgate", "", "") 1845 uuids = append(uuids, uuid) 1846 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1847 checkMigratedTable(t, "zerodate_test", "create_with_zero2") 1848 checkTable(t, tableName, true) 1849 testSelectTableMetrics(t) 1850 }) 1851 1852 // ### The following tests are not strictly 'declarative' but are best served under this endtoend test 1853 1854 // Test duplicate context/SQL 1855 t.Run("Trivial statement with request context is successful", func(t *testing.T) { 1856 uuid := testOnlineDDL(t, trivialAlterStatement, "online", "vtctl", "", "") 1857 uuids = append(uuids, uuid) 1858 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1859 // the table existed, so we expect no changes in this non-declarative DDL 1860 checkTable(t, tableName, true) 1861 1862 rs := onlineddl.ReadMigrations(t, &vtParams, uuid) 1863 require.NotNil(t, rs) 1864 for _, row := range rs.Named().Rows { 1865 message := row["message"].ToString() 1866 require.NotContains(t, message, "duplicate DDL") 1867 } 1868 }) 1869 t.Run("Duplicate trivial statement with request context is successful", func(t *testing.T) { 1870 uuid := testOnlineDDL(t, trivialAlterStatement, "online", "vtctl", "", "") 1871 uuids = append(uuids, uuid) 1872 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 1873 // the table existed, so we expect no changes in this non-declarative DDL 1874 checkTable(t, tableName, true) 1875 1876 rs := onlineddl.ReadMigrations(t, &vtParams, uuid) 1877 require.NotNil(t, rs) 1878 for _, row := range rs.Named().Rows { 1879 message := row["message"].ToString() 1880 // Message suggests that the migration was identified as duplicate 1881 require.Contains(t, message, "duplicate DDL") 1882 } 1883 }) 1884 // Piggyride this test suite, let's also test --allow-zero-in-date for 'direct' strategy 1885 t.Run("drop non_online", func(t *testing.T) { 1886 _ = testOnlineDDL(t, dropZeroDateStatement, "direct", "vtctl", "", "") 1887 checkTable(t, "zerodate_test", false) 1888 }) 1889 t.Run("CREATE TABLE with zero date fails in 'direct' strategy", func(t *testing.T) { 1890 _ = testOnlineDDL(t, createStatementZeroDate, "direct", "vtctl", "", "Invalid default value for") 1891 checkTable(t, "zerodate_test", false) 1892 }) 1893 t.Run("CREATE TABLE with zero date and --allow-zero-in-date succeeds in 'direct' strategy", func(t *testing.T) { 1894 _ = testOnlineDDL(t, createStatementZeroDate, "direct --allow-zero-in-date", "vtctl", "", "") 1895 checkTable(t, "zerodate_test", true) 1896 }) 1897 } 1898 1899 func testForeignKeys(t *testing.T) { 1900 defer cluster.PanicHandler(t) 1901 1902 var ( 1903 createStatements = []string{ 1904 ` 1905 CREATE TABLE parent_table ( 1906 id INT NOT NULL, 1907 parent_hint_col INT NOT NULL DEFAULT 0, 1908 PRIMARY KEY (id) 1909 ) 1910 `, 1911 ` 1912 CREATE TABLE child_table ( 1913 id INT NOT NULL auto_increment, 1914 parent_id INT, 1915 child_hint_col INT NOT NULL DEFAULT 0, 1916 PRIMARY KEY (id), 1917 KEY parent_id_idx (parent_id), 1918 CONSTRAINT child_parent_fk FOREIGN KEY (parent_id) REFERENCES parent_table(id) ON DELETE CASCADE 1919 ) 1920 `, 1921 ` 1922 CREATE TABLE child_nofk_table ( 1923 id INT NOT NULL auto_increment, 1924 parent_id INT, 1925 child_hint_col INT NOT NULL DEFAULT 0, 1926 PRIMARY KEY (id), 1927 KEY parent_id_idx (parent_id) 1928 ) 1929 `, 1930 } 1931 insertStatements = []string{ 1932 "insert into parent_table (id) values(43)", 1933 "insert into child_table (id, parent_id) values(1,43)", 1934 "insert into child_table (id, parent_id) values(2,43)", 1935 "insert into child_table (id, parent_id) values(3,43)", 1936 "insert into child_table (id, parent_id) values(4,43)", 1937 } 1938 ddlStrategy = "online --allow-zero-in-date" 1939 ddlStrategyAllowFK = ddlStrategy + " --unsafe-allow-foreign-keys" 1940 ) 1941 1942 type testCase struct { 1943 name string 1944 sql string 1945 allowForeignKeys bool 1946 expectHint string 1947 } 1948 var testCases = []testCase{ 1949 { 1950 name: "modify parent, not allowed", 1951 sql: "alter table parent_table engine=innodb", 1952 allowForeignKeys: false, 1953 }, 1954 { 1955 name: "modify child, not allowed", 1956 sql: "alter table child_table engine=innodb", 1957 allowForeignKeys: false, 1958 }, 1959 { 1960 name: "add foreign key to child, not allowed", 1961 sql: "alter table child_table add CONSTRAINT another_fk FOREIGN KEY (parent_id) REFERENCES parent_table(id) ON DELETE CASCADE", 1962 allowForeignKeys: false, 1963 }, 1964 { 1965 name: "add foreign key to table which wasn't a child before, not allowed", 1966 sql: "alter table child_nofk_table add CONSTRAINT new_fk FOREIGN KEY (parent_id) REFERENCES parent_table(id) ON DELETE CASCADE", 1967 allowForeignKeys: false, 1968 }, 1969 { 1970 // on vanilla MySQL, this migration ends with the child_table referencing the old, original table, and not to the new table now called parent_table. 1971 // This is a fundamental foreign key limitation, see https://vitess.io/blog/2021-06-15-online-ddl-why-no-fk/ 1972 // However, this tests is still valid in the sense that it lets us modify the parent table in the first place. 1973 name: "modify parent, trivial", 1974 sql: "alter table parent_table engine=innodb", 1975 allowForeignKeys: true, 1976 expectHint: "parent_hint_col", 1977 }, 1978 { 1979 // on vanilla MySQL, this migration ends with two tables, the original and the new child_table, both referencing parent_table. This has 1980 // the unwanted property of then limiting actions on the parent_table based on what rows exist or do not exist on the now stale old 1981 // child table. 1982 // This is a fundamental foreign key limitation, see https://vitess.io/blog/2021-06-15-online-ddl-why-no-fk/ 1983 // However, this tests is still valid in the sense that it lets us modify the child table in the first place. 1984 // A valid use case: using FOREIGN_KEY_CHECKS=0 at all times. 1985 name: "modify child, trivial", 1986 sql: "alter table child_table engine=innodb", 1987 allowForeignKeys: true, 1988 expectHint: "REFERENCES `parent_table`", 1989 }, 1990 { 1991 // on vanilla MySQL, this migration ends with two tables, the original and the new child_table, both referencing parent_table. This has 1992 // the unwanted property of then limiting actions on the parent_table based on what rows exist or do not exist on the now stale old 1993 // child table. 1994 // This is a fundamental foreign key limitation, see https://vitess.io/blog/2021-06-15-online-ddl-why-no-fk/ 1995 // However, this tests is still valid in the sense that it lets us modify the child table in the first place. 1996 // A valid use case: using FOREIGN_KEY_CHECKS=0 at all times. 1997 name: "add foreign key to child", 1998 sql: "alter table child_table add CONSTRAINT another_fk FOREIGN KEY (parent_id) REFERENCES parent_table(id) ON DELETE CASCADE", 1999 allowForeignKeys: true, 2000 expectHint: "another_fk", 2001 }, 2002 { 2003 name: "add foreign key to table which wasn't a child before", 2004 sql: "alter table child_nofk_table add CONSTRAINT new_fk FOREIGN KEY (parent_id) REFERENCES parent_table(id) ON DELETE CASCADE", 2005 allowForeignKeys: true, 2006 expectHint: "new_fk", 2007 }, 2008 } 2009 2010 createParams := func(ddlStatement string, ddlStrategy string, executeStrategy string, expectHint string, expectError string, skipWait bool) *testOnlineDDLStatementParams { 2011 return &testOnlineDDLStatementParams{ 2012 ddlStatement: ddlStatement, 2013 ddlStrategy: ddlStrategy, 2014 executeStrategy: executeStrategy, 2015 expectHint: expectHint, 2016 expectError: expectError, 2017 skipWait: skipWait, 2018 } 2019 } 2020 2021 testStatement := func(t *testing.T, sql string, ddlStrategy string, expectHint string, expectError bool) (uuid string) { 2022 errorHint := "" 2023 if expectError { 2024 errorHint = anyErrorIndicator 2025 } 2026 return testOnlineDDLStatement(t, createParams(sql, ddlStrategy, "vtctl", expectHint, errorHint, false)) 2027 } 2028 for _, testcase := range testCases { 2029 t.Run(testcase.name, func(t *testing.T) { 2030 t.Run("create tables", func(t *testing.T) { 2031 for _, statement := range createStatements { 2032 t.Run(statement, func(t *testing.T) { 2033 uuid := testStatement(t, statement, ddlStrategyAllowFK, "", false) 2034 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 2035 }) 2036 } 2037 }) 2038 t.Run("populate tables", func(t *testing.T) { 2039 for _, statement := range insertStatements { 2040 t.Run(statement, func(t *testing.T) { 2041 onlineddl.VtgateExecQuery(t, &vtParams, statement, "") 2042 }) 2043 } 2044 }) 2045 var uuid string 2046 t.Run("run migration", func(t *testing.T) { 2047 if testcase.allowForeignKeys { 2048 uuid = testStatement(t, testcase.sql, ddlStrategyAllowFK, testcase.expectHint, false) 2049 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusComplete) 2050 } else { 2051 uuid = testStatement(t, testcase.sql, ddlStrategy, "", true) 2052 if uuid != "" { 2053 onlineddl.CheckMigrationStatus(t, &vtParams, shards, uuid, schema.OnlineDDLStatusFailed) 2054 } 2055 } 2056 }) 2057 t.Run("cleanup", func(t *testing.T) { 2058 var artifacts []string 2059 if uuid != "" { 2060 rs := onlineddl.ReadMigrations(t, &vtParams, uuid) 2061 require.NotNil(t, rs) 2062 row := rs.Named().Row() 2063 require.NotNil(t, row) 2064 2065 artifacts = textutil.SplitDelimitedList(row.AsString("artifacts", "")) 2066 } 2067 2068 artifacts = append(artifacts, "child_table", "child_nofk_table", "parent_table") 2069 // brute force drop all tables. In MySQL 8.0 you can do a single `DROP TABLE ... <list of all tables>` 2070 // which auto-resovled order. But in 5.7 you can't. 2071 droppedTables := map[string]bool{} 2072 for range artifacts { 2073 for _, artifact := range artifacts { 2074 if droppedTables[artifact] { 2075 continue 2076 } 2077 statement := fmt.Sprintf("DROP TABLE IF EXISTS %s", artifact) 2078 _, err := clusterInstance.VtctlclientProcess.ApplySchemaWithOutput(keyspaceName, statement, cluster.VtctlClientParams{DDLStrategy: "direct", SkipPreflight: true}) 2079 if err == nil { 2080 droppedTables[artifact] = true 2081 } 2082 } 2083 } 2084 statement := fmt.Sprintf("DROP TABLE IF EXISTS %s", strings.Join(artifacts, ",")) 2085 t.Run(statement, func(t *testing.T) { 2086 testStatement(t, statement, "direct", "", false) 2087 }) 2088 }) 2089 }) 2090 } 2091 } 2092 2093 // testOnlineDDLStatement runs an online DDL, ALTER statement 2094 func testOnlineDDLStatement(t *testing.T, params *testOnlineDDLStatementParams) (uuid string) { 2095 strategySetting, err := schema.ParseDDLStrategy(params.ddlStrategy) 2096 require.NoError(t, err) 2097 2098 tableName := parseTableName(t, params.ddlStatement) 2099 2100 if params.executeStrategy == "vtgate" { 2101 require.Empty(t, params.migrationContext, "explicit migration context not supported in vtgate. Test via vtctl") 2102 result := onlineddl.VtgateExecDDL(t, &vtParams, params.ddlStrategy, params.ddlStatement, params.expectError) 2103 if result != nil { 2104 row := result.Named().Row() 2105 if row != nil { 2106 uuid = row.AsString("uuid", "") 2107 } 2108 } 2109 } else { 2110 vtctlParams := &cluster.VtctlClientParams{DDLStrategy: params.ddlStrategy, MigrationContext: params.migrationContext, SkipPreflight: true} 2111 if overrideVtctlParams != nil { 2112 vtctlParams = overrideVtctlParams 2113 } 2114 output, err := clusterInstance.VtctlclientProcess.ApplySchemaWithOutput(keyspaceName, params.ddlStatement, *vtctlParams) 2115 switch params.expectError { 2116 case anyErrorIndicator: 2117 if err != nil { 2118 // fine. We got any error. 2119 t.Logf("expected any error, got this error: %v", err) 2120 return 2121 } 2122 uuid = output 2123 case "": 2124 assert.NoError(t, err) 2125 uuid = output 2126 default: 2127 assert.Error(t, err) 2128 assert.Contains(t, output, params.expectError) 2129 } 2130 } 2131 uuid = strings.TrimSpace(uuid) 2132 fmt.Println("# Generated UUID (for debug purposes):") 2133 fmt.Printf("<%s>\n", uuid) 2134 2135 if !strategySetting.Strategy.IsDirect() && !params.skipWait && uuid != "" { 2136 status := onlineddl.WaitForMigrationStatus(t, &vtParams, shards, uuid, normalWaitTime, schema.OnlineDDLStatusComplete, schema.OnlineDDLStatusFailed) 2137 fmt.Printf("# Migration status (for debug purposes): <%s>\n", status) 2138 } 2139 2140 if params.expectError == "" && params.expectHint != "" { 2141 checkMigratedTable(t, tableName, params.expectHint) 2142 } 2143 return uuid 2144 } 2145 2146 // testRevertMigration reverts a given migration 2147 func testRevertMigration(t *testing.T, params *testRevertMigrationParams) (uuid string) { 2148 revertQuery := fmt.Sprintf("revert vitess_migration '%s'", params.revertUUID) 2149 if params.executeStrategy == "vtgate" { 2150 require.Empty(t, params.migrationContext, "explicit migration context not supported in vtgate. Test via vtctl") 2151 result := onlineddl.VtgateExecDDL(t, &vtParams, params.ddlStrategy, revertQuery, params.expectError) 2152 if result != nil { 2153 row := result.Named().Row() 2154 if row != nil { 2155 uuid = row.AsString("uuid", "") 2156 } 2157 } 2158 } else { 2159 output, err := clusterInstance.VtctlclientProcess.ApplySchemaWithOutput(keyspaceName, revertQuery, cluster.VtctlClientParams{DDLStrategy: params.ddlStrategy, MigrationContext: params.migrationContext, SkipPreflight: true}) 2160 if params.expectError == "" { 2161 assert.NoError(t, err) 2162 uuid = output 2163 } else { 2164 assert.Error(t, err) 2165 assert.Contains(t, output, params.expectError) 2166 } 2167 } 2168 2169 if params.expectError == "" { 2170 uuid = strings.TrimSpace(uuid) 2171 fmt.Println("# Generated UUID (for debug purposes):") 2172 fmt.Printf("<%s>\n", uuid) 2173 } 2174 if !params.skipWait { 2175 time.Sleep(time.Second * 20) 2176 } 2177 return uuid 2178 } 2179 2180 // checkTable checks the number of tables in the first two shards. 2181 func checkTable(t *testing.T, showTableName string, expectExists bool) bool { 2182 expectCount := 0 2183 if expectExists { 2184 expectCount = 1 2185 } 2186 for i := range clusterInstance.Keyspaces[0].Shards { 2187 if !checkTablesCount(t, clusterInstance.Keyspaces[0].Shards[i].Vttablets[0], showTableName, expectCount) { 2188 return false 2189 } 2190 } 2191 return true 2192 } 2193 2194 // checkTablesCount checks the number of tables in the given tablet 2195 func checkTablesCount(t *testing.T, tablet *cluster.Vttablet, showTableName string, expectCount int) bool { 2196 query := fmt.Sprintf(`show tables like '%%%s%%';`, showTableName) 2197 queryResult, err := tablet.VttabletProcess.QueryTablet(query, keyspaceName, true) 2198 require.Nil(t, err) 2199 return assert.Equal(t, expectCount, len(queryResult.Rows)) 2200 } 2201 2202 // checkMigratedTables checks the CREATE STATEMENT of a table after migration 2203 func checkMigratedTable(t *testing.T, tableName, expectHint string) { 2204 for i := range clusterInstance.Keyspaces[0].Shards { 2205 createStatement := getCreateTableStatement(t, clusterInstance.Keyspaces[0].Shards[i].Vttablets[0], tableName) 2206 assert.Contains(t, createStatement, expectHint) 2207 } 2208 } 2209 2210 // getCreateTableStatement returns the CREATE TABLE statement for a given table 2211 func getCreateTableStatement(t *testing.T, tablet *cluster.Vttablet, tableName string) (statement string) { 2212 queryResult, err := tablet.VttabletProcess.QueryTablet(fmt.Sprintf("show create table %s;", tableName), keyspaceName, true) 2213 require.Nil(t, err) 2214 2215 assert.Equal(t, len(queryResult.Rows), 1) 2216 assert.GreaterOrEqual(t, len(queryResult.Rows[0]), 2) // table name, create statement, and if it's a view then additional columns 2217 statement = queryResult.Rows[0][1].ToString() 2218 return statement 2219 }