vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/tabletserver_test.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package tabletserver 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "io" 24 "math/rand" 25 "net/http" 26 "net/http/httptest" 27 "os" 28 "reflect" 29 "strings" 30 "sync" 31 "syscall" 32 "testing" 33 "time" 34 35 "vitess.io/vitess/go/vt/sidecardb" 36 37 "vitess.io/vitess/go/vt/callerid" 38 39 "vitess.io/vitess/go/mysql/fakesqldb" 40 "vitess.io/vitess/go/test/utils" 41 42 "github.com/stretchr/testify/assert" 43 44 "github.com/stretchr/testify/require" 45 46 "vitess.io/vitess/go/mysql" 47 "vitess.io/vitess/go/sqltypes" 48 "vitess.io/vitess/go/vt/log" 49 "vitess.io/vitess/go/vt/sqlparser" 50 "vitess.io/vitess/go/vt/tableacl" 51 "vitess.io/vitess/go/vt/tableacl/simpleacl" 52 "vitess.io/vitess/go/vt/topo/memorytopo" 53 "vitess.io/vitess/go/vt/vterrors" 54 "vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv" 55 56 querypb "vitess.io/vitess/go/vt/proto/query" 57 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 58 vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" 59 ) 60 61 func TestTabletServerHealthz(t *testing.T) { 62 db, tsv := setupTabletServerTest(t, "") 63 defer tsv.StopService() 64 defer db.Close() 65 66 req, err := http.NewRequest("GET", "/healthz", nil) 67 if err != nil { 68 t.Fatal(err) 69 } 70 71 rr := httptest.NewRecorder() 72 handler := http.HandlerFunc(tsv.healthzHandler) 73 handler.ServeHTTP(rr, req) 74 75 expectedCode := http.StatusOK 76 if status := rr.Code; status != expectedCode { 77 t.Errorf("handler returned wrong status code: got %v want %v", 78 status, expectedCode) 79 } 80 81 expected := "ok\n" 82 if rr.Body.String() != expected { 83 t.Errorf("handler returned unexpected body: got %v want %v", 84 rr.Body.String(), expected) 85 } 86 } 87 88 func TestTabletServerHealthzNotConnected(t *testing.T) { 89 db, tsv := setupTabletServerTest(t, "") 90 defer tsv.StopService() 91 defer db.Close() 92 93 tsv.sm.SetServingType(topodatapb.TabletType_PRIMARY, time.Time{}, StateNotConnected, "test disconnected") 94 95 req, err := http.NewRequest("GET", "/healthz", nil) 96 if err != nil { 97 t.Fatal(err) 98 } 99 100 rr := httptest.NewRecorder() 101 handler := http.HandlerFunc(tsv.healthzHandler) 102 handler.ServeHTTP(rr, req) 103 104 expectedCode := http.StatusInternalServerError 105 if status := rr.Code; status != expectedCode { 106 t.Errorf("handler returned wrong status code: got %v want %v", 107 status, expectedCode) 108 } 109 110 expected := "500 internal server error: vttablet is not serving\n" 111 if rr.Body.String() != expected { 112 t.Errorf("handler returned unexpected body: got %v want %v", 113 rr.Body.String(), expected) 114 } 115 } 116 117 func TestBeginOnReplica(t *testing.T) { 118 db, tsv := setupTabletServerTest(t, "") 119 defer tsv.StopService() 120 defer db.Close() 121 122 db.AddQueryPattern(".*", &sqltypes.Result{}) 123 target := querypb.Target{TabletType: topodatapb.TabletType_REPLICA} 124 err := tsv.SetServingType(topodatapb.TabletType_REPLICA, time.Time{}, true, "") 125 require.NoError(t, err) 126 127 options := querypb.ExecuteOptions{ 128 TransactionIsolation: querypb.ExecuteOptions_CONSISTENT_SNAPSHOT_READ_ONLY, 129 } 130 state, err := tsv.Begin(ctx, &target, &options) 131 require.NoError(t, err, "failed to create read only tx on replica") 132 assert.Equal(t, tsv.alias, state.TabletAlias, "Wrong tablet alias from Begin") 133 _, err = tsv.Rollback(ctx, &target, state.TransactionID) 134 require.NoError(t, err, "failed to rollback read only tx") 135 136 // test that we can still create transactions even in read-only mode 137 options = querypb.ExecuteOptions{} 138 state, err = tsv.Begin(ctx, &target, &options) 139 require.NoError(t, err, "expected write tx to be allowed") 140 _, err = tsv.Rollback(ctx, &target, state.TransactionID) 141 require.NoError(t, err) 142 } 143 144 func TestTabletServerPrimaryToReplica(t *testing.T) { 145 // Reuse code from tx_executor_test. 146 _, tsv, db := newTestTxExecutor(t) 147 defer tsv.StopService() 148 defer db.Close() 149 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 150 state1, err := tsv.Begin(ctx, &target, nil) 151 require.NoError(t, err) 152 153 _, err = tsv.Execute(ctx, &target, "update test_table set `name` = 2 where pk = 1", nil, state1.TransactionID, 0, nil) 154 require.NoError(t, err) 155 err = tsv.Prepare(ctx, &target, state1.TransactionID, "aa") 156 require.NoError(t, err) 157 state2, err := tsv.Begin(ctx, &target, nil) 158 require.NoError(t, err) 159 160 // This makes txid2 busy 161 conn2, err := tsv.te.txPool.GetAndLock(state2.TransactionID, "for query") 162 require.NoError(t, err) 163 ch := make(chan bool) 164 go func() { 165 tsv.SetServingType(topodatapb.TabletType_REPLICA, time.Time{}, true, "") 166 ch <- true 167 }() 168 169 // SetServingType must rollback the prepared transaction, 170 // but it must wait for the unprepared (txid2) to become non-busy. 171 select { 172 case <-ch: 173 t.Fatal("ch should not fire") 174 case <-time.After(10 * time.Millisecond): 175 } 176 require.EqualValues(t, 1, tsv.te.txPool.scp.active.Size(), "tsv.te.txPool.scp.active.Size()") 177 178 // Concluding conn2 will allow the transition to go through. 179 tsv.te.txPool.RollbackAndRelease(ctx, conn2) 180 <-ch 181 } 182 183 func TestTabletServerRedoLogIsKeptBetweenRestarts(t *testing.T) { 184 // Reuse code from tx_executor_test. 185 _, tsv, db := newTestTxExecutor(t) 186 defer tsv.StopService() 187 defer db.Close() 188 tsv.SetServingType(topodatapb.TabletType_REPLICA, time.Time{}, true, "") 189 190 turnOnTxEngine := func() { 191 tsv.SetServingType(topodatapb.TabletType_PRIMARY, time.Time{}, true, "") 192 tsv.TwoPCEngineWait() 193 } 194 turnOffTxEngine := func() { 195 tsv.SetServingType(topodatapb.TabletType_REPLICA, time.Time{}, true, "") 196 } 197 198 tpc := tsv.te.twoPC 199 200 db.AddQuery(tpc.readAllRedo, &sqltypes.Result{}) 201 turnOnTxEngine() 202 assert.Empty(t, tsv.te.preparedPool.conns, "tsv.te.preparedPool.conns") 203 turnOffTxEngine() 204 205 db.AddQuery(tpc.readAllRedo, &sqltypes.Result{ 206 Fields: []*querypb.Field{ 207 {Type: sqltypes.VarBinary}, 208 {Type: sqltypes.Uint64}, 209 {Type: sqltypes.Uint64}, 210 {Type: sqltypes.VarBinary}, 211 }, 212 Rows: [][]sqltypes.Value{{ 213 sqltypes.NewVarBinary("dtid0"), 214 sqltypes.NewInt64(RedoStatePrepared), 215 sqltypes.NewVarBinary(""), 216 sqltypes.NewVarBinary("update test_table set `name` = 2 where pk = 1 limit 10001"), 217 }}, 218 }) 219 turnOnTxEngine() 220 assert.EqualValues(t, 1, len(tsv.te.preparedPool.conns), "len(tsv.te.preparedPool.conns)") 221 got := tsv.te.preparedPool.conns["dtid0"].TxProperties().Queries 222 want := []string{"update test_table set `name` = 2 where pk = 1 limit 10001"} 223 utils.MustMatch(t, want, got, "Prepared queries") 224 turnOffTxEngine() 225 assert.Empty(t, tsv.te.preparedPool.conns, "tsv.te.preparedPool.conns") 226 227 tsv.te.txPool.scp.lastID.Set(1) 228 // Ensure we continue past errors. 229 db.AddQuery(tpc.readAllRedo, &sqltypes.Result{ 230 Fields: []*querypb.Field{ 231 {Type: sqltypes.VarBinary}, 232 {Type: sqltypes.Uint64}, 233 {Type: sqltypes.Uint64}, 234 {Type: sqltypes.VarBinary}, 235 }, 236 Rows: [][]sqltypes.Value{{ 237 sqltypes.NewVarBinary("bogus"), 238 sqltypes.NewInt64(RedoStatePrepared), 239 sqltypes.NewVarBinary(""), 240 sqltypes.NewVarBinary("bogus"), 241 }, { 242 sqltypes.NewVarBinary("a:b:10"), 243 sqltypes.NewInt64(RedoStatePrepared), 244 sqltypes.NewVarBinary(""), 245 sqltypes.NewVarBinary("update test_table set `name` = 2 where pk = 1 limit 10001"), 246 }, { 247 sqltypes.NewVarBinary("a:b:20"), 248 sqltypes.NewInt64(RedoStateFailed), 249 sqltypes.NewVarBinary(""), 250 sqltypes.NewVarBinary("unused"), 251 }}, 252 }) 253 turnOnTxEngine() 254 assert.EqualValues(t, 1, len(tsv.te.preparedPool.conns), "len(tsv.te.preparedPool.conns)") 255 got = tsv.te.preparedPool.conns["a:b:10"].TxProperties().Queries 256 want = []string{"update test_table set `name` = 2 where pk = 1 limit 10001"} 257 utils.MustMatch(t, want, got, "Prepared queries") 258 wantFailed := map[string]error{"a:b:20": errPrepFailed} 259 if !reflect.DeepEqual(tsv.te.preparedPool.reserved, wantFailed) { 260 t.Errorf("Failed dtids: %v, want %v", tsv.te.preparedPool.reserved, wantFailed) 261 } 262 // Verify last id got adjusted. 263 assert.EqualValues(t, 20, tsv.te.txPool.scp.lastID.Get(), "tsv.te.txPool.lastID.Get()") 264 turnOffTxEngine() 265 assert.Empty(t, tsv.te.preparedPool.conns, "tsv.te.preparedPool.conns") 266 } 267 268 func TestTabletServerCreateTransaction(t *testing.T) { 269 _, tsv, db := newTestTxExecutor(t) 270 defer tsv.StopService() 271 defer db.Close() 272 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 273 274 db.AddQueryPattern(fmt.Sprintf("insert into _vt\\.dt_state\\(dtid, state, time_created\\) values \\('aa', %d,.*", int(querypb.TransactionState_PREPARE)), &sqltypes.Result{}) 275 db.AddQueryPattern("insert into _vt\\.dt_participant\\(dtid, id, keyspace, shard\\) values \\('aa', 1,.*", &sqltypes.Result{}) 276 err := tsv.CreateTransaction(ctx, &target, "aa", []*querypb.Target{{ 277 Keyspace: "t1", 278 Shard: "0", 279 }}) 280 require.NoError(t, err) 281 } 282 283 func TestTabletServerStartCommit(t *testing.T) { 284 _, tsv, db := newTestTxExecutor(t) 285 defer tsv.StopService() 286 defer db.Close() 287 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 288 289 commitTransition := fmt.Sprintf("update _vt.dt_state set state = %d where dtid = 'aa' and state = %d", int(querypb.TransactionState_COMMIT), int(querypb.TransactionState_PREPARE)) 290 db.AddQuery(commitTransition, &sqltypes.Result{RowsAffected: 1}) 291 txid := newTxForPrep(tsv) 292 err := tsv.StartCommit(ctx, &target, txid, "aa") 293 require.NoError(t, err) 294 295 db.AddQuery(commitTransition, &sqltypes.Result{}) 296 txid = newTxForPrep(tsv) 297 err = tsv.StartCommit(ctx, &target, txid, "aa") 298 assert.EqualError(t, err, "could not transition to COMMIT: aa", "Prepare err") 299 } 300 301 func TestTabletserverSetRollback(t *testing.T) { 302 _, tsv, db := newTestTxExecutor(t) 303 defer tsv.StopService() 304 defer db.Close() 305 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 306 307 rollbackTransition := fmt.Sprintf("update _vt.dt_state set state = %d where dtid = 'aa' and state = %d", int(querypb.TransactionState_ROLLBACK), int(querypb.TransactionState_PREPARE)) 308 db.AddQuery(rollbackTransition, &sqltypes.Result{RowsAffected: 1}) 309 txid := newTxForPrep(tsv) 310 err := tsv.SetRollback(ctx, &target, "aa", txid) 311 require.NoError(t, err) 312 313 db.AddQuery(rollbackTransition, &sqltypes.Result{}) 314 txid = newTxForPrep(tsv) 315 err = tsv.SetRollback(ctx, &target, "aa", txid) 316 assert.EqualError(t, err, "could not transition to ROLLBACK: aa", "Prepare err") 317 } 318 319 func TestTabletServerReadTransaction(t *testing.T) { 320 _, tsv, db := newTestTxExecutor(t) 321 defer tsv.StopService() 322 defer db.Close() 323 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 324 325 db.AddQuery("select dtid, state, time_created from _vt.dt_state where dtid = 'aa'", &sqltypes.Result{}) 326 got, err := tsv.ReadTransaction(ctx, &target, "aa") 327 require.NoError(t, err) 328 want := &querypb.TransactionMetadata{} 329 utils.MustMatch(t, want, got, "ReadTransaction") 330 331 txResult := &sqltypes.Result{ 332 Fields: []*querypb.Field{ 333 {Type: sqltypes.VarBinary}, 334 {Type: sqltypes.Uint64}, 335 {Type: sqltypes.Uint64}, 336 }, 337 Rows: [][]sqltypes.Value{{ 338 sqltypes.NewVarBinary("aa"), 339 sqltypes.NewInt64(int64(querypb.TransactionState_PREPARE)), 340 sqltypes.NewVarBinary("1"), 341 }}, 342 } 343 db.AddQuery("select dtid, state, time_created from _vt.dt_state where dtid = 'aa'", txResult) 344 db.AddQuery("select keyspace, shard from _vt.dt_participant where dtid = 'aa'", &sqltypes.Result{ 345 Fields: []*querypb.Field{ 346 {Type: sqltypes.VarBinary}, 347 {Type: sqltypes.VarBinary}, 348 }, 349 Rows: [][]sqltypes.Value{{ 350 sqltypes.NewVarBinary("test1"), 351 sqltypes.NewVarBinary("0"), 352 }, { 353 sqltypes.NewVarBinary("test2"), 354 sqltypes.NewVarBinary("1"), 355 }}, 356 }) 357 got, err = tsv.ReadTransaction(ctx, &target, "aa") 358 require.NoError(t, err) 359 want = &querypb.TransactionMetadata{ 360 Dtid: "aa", 361 State: querypb.TransactionState_PREPARE, 362 TimeCreated: 1, 363 Participants: []*querypb.Target{{ 364 Keyspace: "test1", 365 Shard: "0", 366 TabletType: topodatapb.TabletType_PRIMARY, 367 }, { 368 Keyspace: "test2", 369 Shard: "1", 370 TabletType: topodatapb.TabletType_PRIMARY, 371 }}, 372 } 373 utils.MustMatch(t, want, got, "ReadTransaction") 374 375 txResult = &sqltypes.Result{ 376 Fields: []*querypb.Field{ 377 {Type: sqltypes.VarBinary}, 378 {Type: sqltypes.Uint64}, 379 {Type: sqltypes.Uint64}, 380 }, 381 Rows: [][]sqltypes.Value{{ 382 sqltypes.NewVarBinary("aa"), 383 sqltypes.NewInt64(int64(querypb.TransactionState_COMMIT)), 384 sqltypes.NewVarBinary("1"), 385 }}, 386 } 387 db.AddQuery("select dtid, state, time_created from _vt.dt_state where dtid = 'aa'", txResult) 388 want.State = querypb.TransactionState_COMMIT 389 got, err = tsv.ReadTransaction(ctx, &target, "aa") 390 require.NoError(t, err) 391 utils.MustMatch(t, want, got, "ReadTransaction") 392 393 txResult = &sqltypes.Result{ 394 Fields: []*querypb.Field{ 395 {Type: sqltypes.VarBinary}, 396 {Type: sqltypes.Uint64}, 397 {Type: sqltypes.Uint64}, 398 }, 399 Rows: [][]sqltypes.Value{{ 400 sqltypes.NewVarBinary("aa"), 401 sqltypes.NewInt64(int64(querypb.TransactionState_ROLLBACK)), 402 sqltypes.NewVarBinary("1"), 403 }}, 404 } 405 db.AddQuery("select dtid, state, time_created from _vt.dt_state where dtid = 'aa'", txResult) 406 want.State = querypb.TransactionState_ROLLBACK 407 got, err = tsv.ReadTransaction(ctx, &target, "aa") 408 require.NoError(t, err) 409 utils.MustMatch(t, want, got, "ReadTransaction") 410 } 411 412 func TestTabletServerConcludeTransaction(t *testing.T) { 413 _, tsv, db := newTestTxExecutor(t) 414 defer tsv.StopService() 415 defer db.Close() 416 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 417 418 db.AddQuery("delete from _vt.dt_state where dtid = 'aa'", &sqltypes.Result{}) 419 db.AddQuery("delete from _vt.dt_participant where dtid = 'aa'", &sqltypes.Result{}) 420 err := tsv.ConcludeTransaction(ctx, &target, "aa") 421 require.NoError(t, err) 422 } 423 424 func TestTabletServerBeginFail(t *testing.T) { 425 config := tabletenv.NewDefaultConfig() 426 config.TxPool.Size = 1 427 db, tsv := setupTabletServerTestCustom(t, config, "") 428 defer tsv.StopService() 429 defer db.Close() 430 431 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 432 ctx, cancel := context.WithTimeout(context.Background(), 1*time.Nanosecond) 433 defer cancel() 434 tsv.Begin(ctx, &target, nil) 435 _, err := tsv.Begin(ctx, &target, nil) 436 require.EqualError(t, err, "transaction pool aborting request due to already expired context", "Begin err") 437 } 438 439 func TestTabletServerCommitTransaction(t *testing.T) { 440 db, tsv := setupTabletServerTest(t, "") 441 defer tsv.StopService() 442 defer db.Close() 443 444 executeSQL := "select * from test_table limit 1000" 445 executeSQLResult := &sqltypes.Result{ 446 Fields: []*querypb.Field{ 447 {Type: sqltypes.VarBinary}, 448 }, 449 Rows: [][]sqltypes.Value{ 450 {sqltypes.NewVarBinary("row01")}, 451 }, 452 } 453 db.AddQuery(executeSQL, executeSQLResult) 454 455 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 456 state, err := tsv.Begin(ctx, &target, nil) 457 require.NoError(t, err) 458 _, err = tsv.Execute(ctx, &target, executeSQL, nil, state.TransactionID, 0, nil) 459 require.NoError(t, err) 460 _, err = tsv.Commit(ctx, &target, state.TransactionID) 461 require.NoError(t, err) 462 } 463 464 func TestTabletServerCommiRollbacktFail(t *testing.T) { 465 db, tsv := setupTabletServerTest(t, "") 466 defer tsv.StopService() 467 defer db.Close() 468 469 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 470 _, err := tsv.Commit(ctx, &target, -1) 471 want := "transaction -1: not found" 472 require.Equal(t, want, err.Error()) 473 _, err = tsv.Rollback(ctx, &target, -1) 474 require.Equal(t, want, err.Error()) 475 } 476 477 func TestTabletServerRollback(t *testing.T) { 478 db, tsv := setupTabletServerTest(t, "") 479 defer tsv.StopService() 480 defer db.Close() 481 482 executeSQL := "select * from test_table limit 1000" 483 executeSQLResult := &sqltypes.Result{ 484 Fields: []*querypb.Field{ 485 {Type: sqltypes.VarBinary}, 486 }, 487 Rows: [][]sqltypes.Value{ 488 {sqltypes.NewVarBinary("row01")}, 489 }, 490 } 491 db.AddQuery(executeSQL, executeSQLResult) 492 493 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 494 state, err := tsv.Begin(ctx, &target, nil) 495 require.NoError(t, err) 496 if err != nil { 497 t.Fatalf("call TabletServer.Begin failed: %v", err) 498 } 499 _, err = tsv.Execute(ctx, &target, executeSQL, nil, state.TransactionID, 0, nil) 500 require.NoError(t, err) 501 _, err = tsv.Rollback(ctx, &target, state.TransactionID) 502 require.NoError(t, err) 503 } 504 505 func TestTabletServerPrepare(t *testing.T) { 506 // Reuse code from tx_executor_test. 507 _, tsv, db := newTestTxExecutor(t) 508 defer tsv.StopService() 509 defer db.Close() 510 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 511 state, err := tsv.Begin(ctx, &target, nil) 512 require.NoError(t, err) 513 _, err = tsv.Execute(ctx, &target, "update test_table set `name` = 2 where pk = 1", nil, state.TransactionID, 0, nil) 514 require.NoError(t, err) 515 defer tsv.RollbackPrepared(ctx, &target, "aa", 0) 516 err = tsv.Prepare(ctx, &target, state.TransactionID, "aa") 517 require.NoError(t, err) 518 } 519 520 func TestTabletServerCommitPrepared(t *testing.T) { 521 // Reuse code from tx_executor_test. 522 _, tsv, db := newTestTxExecutor(t) 523 defer tsv.StopService() 524 defer db.Close() 525 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 526 state, err := tsv.Begin(ctx, &target, nil) 527 require.NoError(t, err) 528 _, err = tsv.Execute(ctx, &target, "update test_table set `name` = 2 where pk = 1", nil, state.TransactionID, 0, nil) 529 require.NoError(t, err) 530 err = tsv.Prepare(ctx, &target, state.TransactionID, "aa") 531 require.NoError(t, err) 532 defer tsv.RollbackPrepared(ctx, &target, "aa", 0) 533 err = tsv.CommitPrepared(ctx, &target, "aa") 534 require.NoError(t, err) 535 } 536 537 func TestSmallerTimeout(t *testing.T) { 538 testcases := []struct { 539 t1, t2, want time.Duration 540 }{{ 541 t1: 0, 542 t2: 0, 543 want: 0, 544 }, { 545 t1: 0, 546 t2: 1 * time.Millisecond, 547 want: 1 * time.Millisecond, 548 }, { 549 t1: 1 * time.Millisecond, 550 t2: 0, 551 want: 1 * time.Millisecond, 552 }, { 553 t1: 1 * time.Millisecond, 554 t2: 2 * time.Millisecond, 555 want: 1 * time.Millisecond, 556 }, { 557 t1: 2 * time.Millisecond, 558 t2: 1 * time.Millisecond, 559 want: 1 * time.Millisecond, 560 }} 561 for _, tcase := range testcases { 562 got := smallerTimeout(tcase.t1, tcase.t2) 563 assert.Equal(t, tcase.want, got, tcase.t1, tcase.t2) 564 } 565 } 566 567 func TestTabletServerReserveConnection(t *testing.T) { 568 db, tsv := setupTabletServerTest(t, "") 569 defer tsv.StopService() 570 defer db.Close() 571 572 db.AddQueryPattern(".*", &sqltypes.Result{}) 573 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 574 options := &querypb.ExecuteOptions{} 575 576 // reserve a connection 577 state, _, err := tsv.ReserveExecute(ctx, &target, nil, "select 42", nil, 0, options) 578 require.NoError(t, err) 579 580 // run a query in it 581 _, err = tsv.Execute(ctx, &target, "select 42", nil, 0, state.ReservedID, options) 582 require.NoError(t, err) 583 584 // release the connection 585 err = tsv.Release(ctx, &target, 0, state.ReservedID) 586 require.NoError(t, err) 587 } 588 589 func TestTabletServerExecNonExistentConnection(t *testing.T) { 590 db, tsv := setupTabletServerTest(t, "") 591 defer tsv.StopService() 592 defer db.Close() 593 594 db.AddQueryPattern(".*", &sqltypes.Result{}) 595 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 596 options := &querypb.ExecuteOptions{} 597 598 // run a query with a non-existent reserved id 599 _, err := tsv.Execute(ctx, &target, "select 42", nil, 0, 123456, options) 600 require.Error(t, err) 601 } 602 603 func TestTabletServerReleaseNonExistentConnection(t *testing.T) { 604 db, tsv := setupTabletServerTest(t, "") 605 defer tsv.StopService() 606 defer db.Close() 607 608 db.AddQueryPattern(".*", &sqltypes.Result{}) 609 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 610 611 // run a query with a non-existent reserved id 612 err := tsv.Release(ctx, &target, 0, 123456) 613 require.Error(t, err) 614 } 615 616 func TestMakeSureToCloseDbConnWhenBeginQueryFails(t *testing.T) { 617 db, tsv := setupTabletServerTest(t, "") 618 defer tsv.StopService() 619 defer db.Close() 620 621 db.AddRejectedQuery("begin", errors.New("it broke")) 622 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 623 options := &querypb.ExecuteOptions{} 624 625 // run a query with a non-existent reserved id 626 _, _, err := tsv.ReserveBeginExecute(ctx, &target, []string{}, nil, "select 42", nil, options) 627 require.Error(t, err) 628 } 629 630 func TestTabletServerReserveAndBeginCommit(t *testing.T) { 631 db, tsv := setupTabletServerTest(t, "") 632 defer tsv.StopService() 633 defer db.Close() 634 635 db.AddQueryPattern(".*", &sqltypes.Result{}) 636 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 637 options := &querypb.ExecuteOptions{} 638 639 // reserve a connection and a transaction 640 state, _, err := tsv.ReserveBeginExecute(ctx, &target, nil, nil, "select 42", nil, options) 641 require.NoError(t, err) 642 defer func() { 643 // fallback so the test finishes quickly 644 tsv.Release(ctx, &target, state.TransactionID, state.ReservedID) 645 }() 646 647 // run a query in it 648 _, err = tsv.Execute(ctx, &target, "select 42", nil, state.TransactionID, state.ReservedID, options) 649 require.NoError(t, err) 650 651 // run a query in a non-existent connection 652 _, err = tsv.Execute(ctx, &target, "select 42", nil, state.TransactionID, state.ReservedID+100, options) 653 require.Error(t, err) 654 _, err = tsv.Execute(ctx, &target, "select 42", nil, state.TransactionID+100, state.ReservedID, options) 655 require.Error(t, err) 656 657 // commit 658 newRID, err := tsv.Commit(ctx, &target, state.TransactionID) 659 require.NoError(t, err) 660 assert.NotEqual(t, state.ReservedID, newRID) 661 rID := newRID 662 663 // begin and rollback 664 beginState, _, err := tsv.BeginExecute(ctx, &target, nil, "select 42", nil, rID, options) 665 require.NoError(t, err) 666 assert.Equal(t, newRID, beginState.TransactionID) 667 rID = newRID 668 669 newRID, err = tsv.Rollback(ctx, &target, beginState.TransactionID) 670 require.NoError(t, err) 671 assert.NotEqual(t, rID, newRID) 672 rID = newRID 673 674 // release the connection 675 err = tsv.Release(ctx, &target, 0, rID) 676 require.NoError(t, err) 677 678 // release the connection again and fail 679 err = tsv.Release(ctx, &target, 0, rID) 680 require.Error(t, err) 681 } 682 683 func TestTabletServerRollbackPrepared(t *testing.T) { 684 // Reuse code from tx_executor_test. 685 _, tsv, db := newTestTxExecutor(t) 686 defer tsv.StopService() 687 defer db.Close() 688 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 689 state, err := tsv.Begin(ctx, &target, nil) 690 require.NoError(t, err) 691 _, err = tsv.Execute(ctx, &target, "update test_table set `name` = 2 where pk = 1", nil, state.TransactionID, 0, nil) 692 require.NoError(t, err) 693 err = tsv.Prepare(ctx, &target, state.TransactionID, "aa") 694 require.NoError(t, err) 695 err = tsv.RollbackPrepared(ctx, &target, "aa", state.TransactionID) 696 require.NoError(t, err) 697 } 698 699 func TestTabletServerStreamExecute(t *testing.T) { 700 db, tsv := setupTabletServerTest(t, "") 701 defer tsv.StopService() 702 defer db.Close() 703 704 executeSQL := "select * from test_table limit 1000" 705 executeSQLResult := &sqltypes.Result{ 706 Fields: []*querypb.Field{ 707 {Type: sqltypes.VarBinary}, 708 }, 709 Rows: [][]sqltypes.Value{ 710 {sqltypes.NewVarBinary("row01")}, 711 }, 712 } 713 db.AddQuery(executeSQL, executeSQLResult) 714 715 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 716 callback := func(*sqltypes.Result) error { return nil } 717 if err := tsv.StreamExecute(ctx, &target, executeSQL, nil, 0, 0, nil, callback); err != nil { 718 t.Fatalf("TabletServer.StreamExecute should success: %s, but get error: %v", 719 executeSQL, err) 720 } 721 } 722 723 func TestTabletServerStreamExecuteComments(t *testing.T) { 724 db, tsv := setupTabletServerTest(t, "") 725 defer tsv.StopService() 726 defer db.Close() 727 728 executeSQL := "/* leading */ select * from test_table limit 1000 /* trailing */" 729 executeSQLResult := &sqltypes.Result{ 730 Fields: []*querypb.Field{ 731 {Type: sqltypes.VarBinary}, 732 }, 733 Rows: [][]sqltypes.Value{ 734 {sqltypes.NewVarBinary("row01")}, 735 }, 736 } 737 db.AddQuery(executeSQL, executeSQLResult) 738 739 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 740 callback := func(*sqltypes.Result) error { return nil } 741 742 ch := tabletenv.StatsLogger.Subscribe("test stats logging") 743 defer tabletenv.StatsLogger.Unsubscribe(ch) 744 745 if err := tsv.StreamExecute(ctx, &target, executeSQL, nil, 0, 0, nil, callback); err != nil { 746 t.Fatalf("TabletServer.StreamExecute should success: %s, but get error: %v", 747 executeSQL, err) 748 } 749 750 wantSQL := executeSQL 751 select { 752 case out := <-ch: 753 stats, ok := out.(*tabletenv.LogStats) 754 if !ok { 755 t.Errorf("Unexpected value in query logs: %#v (expecting value of type %T)", out, &tabletenv.LogStats{}) 756 } 757 758 if wantSQL != stats.OriginalSQL { 759 t.Errorf("logstats: SQL want %s got %s", wantSQL, stats.OriginalSQL) 760 } 761 default: 762 t.Fatal("stats are empty") 763 } 764 } 765 766 func TestTabletServerBeginStreamExecute(t *testing.T) { 767 db, tsv := setupTabletServerTest(t, "") 768 defer tsv.StopService() 769 defer db.Close() 770 771 executeSQL := "select * from test_table limit 1000" 772 executeSQLResult := &sqltypes.Result{ 773 Fields: []*querypb.Field{ 774 {Type: sqltypes.VarBinary}, 775 }, 776 Rows: [][]sqltypes.Value{ 777 {sqltypes.NewVarBinary("row01")}, 778 }, 779 } 780 db.AddQuery(executeSQL, executeSQLResult) 781 782 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 783 callback := func(*sqltypes.Result) error { return nil } 784 state, err := tsv.BeginStreamExecute(ctx, &target, nil, executeSQL, nil, 0, nil, callback) 785 if err != nil { 786 t.Fatalf("TabletServer.BeginStreamExecute should success: %s, but get error: %v", 787 executeSQL, err) 788 } 789 require.NoError(t, err) 790 _, err = tsv.Commit(ctx, &target, state.TransactionID) 791 require.NoError(t, err) 792 } 793 794 func TestTabletServerBeginStreamExecuteWithError(t *testing.T) { 795 db, tsv := setupTabletServerTest(t, "") 796 defer tsv.StopService() 797 defer db.Close() 798 799 // Enforce an error so we can validate we get one back properly 800 tsv.qe.strictTableACL = true 801 802 executeSQL := "select * from test_table limit 1000" 803 executeSQLResult := &sqltypes.Result{ 804 Fields: []*querypb.Field{ 805 {Type: sqltypes.VarBinary}, 806 }, 807 Rows: [][]sqltypes.Value{ 808 {sqltypes.NewVarBinary("row01")}, 809 }, 810 } 811 db.AddQuery(executeSQL, executeSQLResult) 812 813 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 814 callback := func(*sqltypes.Result) error { return nil } 815 state, err := tsv.BeginStreamExecute(ctx, &target, nil, executeSQL, nil, 0, nil, callback) 816 require.Error(t, err) 817 err = tsv.Release(ctx, &target, state.TransactionID, 0) 818 require.NoError(t, err) 819 } 820 821 func TestSerializeTransactionsSameRow(t *testing.T) { 822 // This test runs three transaction in parallel: 823 // tx1 | tx2 | tx3 824 // However, tx1 and tx2 have the same WHERE clause (i.e. target the same row) 825 // and therefore tx2 cannot start until the first query of tx1 has finished. 826 // The actual execution looks like this: 827 // tx1 | tx3 828 // tx2 829 config := tabletenv.NewDefaultConfig() 830 config.HotRowProtection.Mode = tabletenv.Enable 831 config.HotRowProtection.MaxConcurrency = 1 832 // Reduce the txpool to 2 because we should never consume more than two slots. 833 config.TxPool.Size = 2 834 db, tsv := setupTabletServerTestCustom(t, config, "") 835 defer tsv.StopService() 836 defer db.Close() 837 838 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 839 countStart := tsv.stats.WaitTimings.Counts()["TabletServerTest.TxSerializer"] 840 841 // Fake data. 842 q1 := "update test_table set name_string = 'tx1' where pk = :pk and `name` = :name" 843 q2 := "update test_table set name_string = 'tx2' where pk = :pk and `name` = :name" 844 q3 := "update test_table set name_string = 'tx3' where pk = :pk and `name` = :name" 845 // Every request needs their own bind variables to avoid data races. 846 bvTx1 := map[string]*querypb.BindVariable{ 847 "pk": sqltypes.Int64BindVariable(1), 848 "name": sqltypes.Int64BindVariable(1), 849 } 850 bvTx2 := map[string]*querypb.BindVariable{ 851 "pk": sqltypes.Int64BindVariable(1), 852 "name": sqltypes.Int64BindVariable(1), 853 } 854 bvTx3 := map[string]*querypb.BindVariable{ 855 "pk": sqltypes.Int64BindVariable(2), 856 "name": sqltypes.Int64BindVariable(1), 857 } 858 859 // Make sure that tx2 and tx3 start only after tx1 is running its Execute(). 860 tx1Started := make(chan struct{}) 861 // Make sure that tx3 could finish while tx2 could not. 862 tx3Finished := make(chan struct{}) 863 864 db.SetBeforeFunc("update test_table set name_string = 'tx1' where pk = 1 and `name` = 1 limit 10001", 865 func() { 866 close(tx1Started) 867 if err := waitForTxSerializationPendingQueries(tsv, "test_table where pk = 1 and `name` = 1", 2); err != nil { 868 t.Fatal(err) 869 } 870 }) 871 872 // Run all three transactions. 873 wg := sync.WaitGroup{} 874 875 // tx1. 876 wg.Add(1) 877 go func() { 878 defer wg.Done() 879 880 state1, _, err := tsv.BeginExecute(ctx, &target, nil, q1, bvTx1, 0, nil) 881 if err != nil { 882 t.Errorf("failed to execute query: %s: %s", q1, err) 883 } 884 if _, err := tsv.Commit(ctx, &target, state1.TransactionID); err != nil { 885 t.Errorf("call TabletServer.Commit failed: %v", err) 886 } 887 }() 888 889 // tx2. 890 wg.Add(1) 891 go func() { 892 defer wg.Done() 893 894 <-tx1Started 895 state2, _, err := tsv.BeginExecute(ctx, &target, nil, q2, bvTx2, 0, nil) 896 if err != nil { 897 t.Errorf("failed to execute query: %s: %s", q2, err) 898 } 899 // TODO(mberlin): This should actually be in the BeforeFunc() of tx1 but 900 // then the test is hanging. It looks like the MySQL C client library cannot 901 // open a second connection while the request of the first connection is 902 // still pending. 903 <-tx3Finished 904 if _, err := tsv.Commit(ctx, &target, state2.TransactionID); err != nil { 905 t.Errorf("call TabletServer.Commit failed: %v", err) 906 } 907 }() 908 909 // tx3. 910 wg.Add(1) 911 go func() { 912 defer wg.Done() 913 914 <-tx1Started 915 state3, _, err := tsv.BeginExecute(ctx, &target, nil, q3, bvTx3, 0, nil) 916 if err != nil { 917 t.Errorf("failed to execute query: %s: %s", q3, err) 918 } 919 if _, err := tsv.Commit(ctx, &target, state3.TransactionID); err != nil { 920 t.Errorf("call TabletServer.Commit failed: %v", err) 921 } 922 close(tx3Finished) 923 }() 924 925 wg.Wait() 926 927 got, ok := tsv.stats.WaitTimings.Counts()["TabletServerTest.TxSerializer"] 928 want := countStart + 1 929 if !ok || got != want { 930 t.Fatalf("only tx2 should have been serialized: ok? %v got: %v want: %v", ok, got, want) 931 } 932 } 933 934 func TestDMLQueryWithoutWhereClause(t *testing.T) { 935 config := tabletenv.NewDefaultConfig() 936 config.HotRowProtection.Mode = tabletenv.Enable 937 config.HotRowProtection.MaxConcurrency = 1 938 config.TxPool.Size = 2 939 db, tsv := setupTabletServerTestCustom(t, config, "") 940 defer tsv.StopService() 941 defer db.Close() 942 943 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 944 q := "delete from test_table" 945 946 db.AddQuery(q+" limit 10001", &sqltypes.Result{}) 947 948 state, _, err := tsv.BeginExecute(ctx, &target, nil, q, nil, 0, nil) 949 require.NoError(t, err) 950 _, err = tsv.Commit(ctx, &target, state.TransactionID) 951 require.NoError(t, err) 952 } 953 954 func TestSerializeTransactionsSameRow_ConcurrentTransactions(t *testing.T) { 955 // This test runs three transaction in parallel: 956 // tx1 | tx2 | tx3 957 // Out of these three, two can run in parallel because we increased the 958 // ConcurrentTransactions limit to 2. 959 // One out of the three transaction will always get serialized though. 960 config := tabletenv.NewDefaultConfig() 961 config.HotRowProtection.Mode = tabletenv.Enable 962 config.HotRowProtection.MaxConcurrency = 2 963 // Reduce the txpool to 2 because we should never consume more than two slots. 964 config.TxPool.Size = 2 965 db, tsv := setupTabletServerTestCustom(t, config, "") 966 defer tsv.StopService() 967 defer db.Close() 968 969 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 970 countStart := tsv.stats.WaitTimings.Counts()["TabletServerTest.TxSerializer"] 971 972 // Fake data. 973 q1 := "update test_table set name_string = 'tx1' where pk = :pk and `name` = :name" 974 q2 := "update test_table set name_string = 'tx2' where pk = :pk and `name` = :name" 975 q3 := "update test_table set name_string = 'tx3' where pk = :pk and `name` = :name" 976 // Every request needs their own bind variables to avoid data races. 977 bvTx1 := map[string]*querypb.BindVariable{ 978 "pk": sqltypes.Int64BindVariable(1), 979 "name": sqltypes.Int64BindVariable(1), 980 } 981 bvTx2 := map[string]*querypb.BindVariable{ 982 "pk": sqltypes.Int64BindVariable(1), 983 "name": sqltypes.Int64BindVariable(1), 984 } 985 bvTx3 := map[string]*querypb.BindVariable{ 986 "pk": sqltypes.Int64BindVariable(1), 987 "name": sqltypes.Int64BindVariable(1), 988 } 989 990 tx1Started := make(chan struct{}) 991 allQueriesPending := make(chan struct{}) 992 db.SetBeforeFunc("update test_table set name_string = 'tx1' where pk = 1 and `name` = 1 limit 10001", 993 func() { 994 close(tx1Started) 995 <-allQueriesPending 996 }) 997 998 // Run all three transactions. 999 wg := sync.WaitGroup{} 1000 1001 // tx1. 1002 wg.Add(1) 1003 go func() { 1004 defer wg.Done() 1005 1006 state1, _, err := tsv.BeginExecute(ctx, &target, nil, q1, bvTx1, 0, nil) 1007 if err != nil { 1008 t.Errorf("failed to execute query: %s: %s", q1, err) 1009 } 1010 1011 if _, err := tsv.Commit(ctx, &target, state1.TransactionID); err != nil { 1012 t.Errorf("call TabletServer.Commit failed: %v", err) 1013 } 1014 }() 1015 1016 // tx2. 1017 wg.Add(1) 1018 go func() { 1019 defer wg.Done() 1020 1021 // Wait for tx1 to avoid that this tx could pass tx1, without any contention. 1022 // In that case, we would see less than 3 pending transactions. 1023 <-tx1Started 1024 1025 state2, _, err := tsv.BeginExecute(ctx, &target, nil, q2, bvTx2, 0, nil) 1026 if err != nil { 1027 t.Errorf("failed to execute query: %s: %s", q2, err) 1028 } 1029 1030 if _, err := tsv.Commit(ctx, &target, state2.TransactionID); err != nil { 1031 t.Errorf("call TabletServer.Commit failed: %v", err) 1032 } 1033 }() 1034 1035 // tx3. 1036 wg.Add(1) 1037 go func() { 1038 defer wg.Done() 1039 1040 // Wait for tx1 to avoid that this tx could pass tx1, without any contention. 1041 // In that case, we would see less than 3 pending transactions. 1042 <-tx1Started 1043 1044 state3, _, err := tsv.BeginExecute(ctx, &target, nil, q3, bvTx3, 0, nil) 1045 if err != nil { 1046 t.Errorf("failed to execute query: %s: %s", q3, err) 1047 } 1048 1049 if _, err := tsv.Commit(ctx, &target, state3.TransactionID); err != nil { 1050 t.Errorf("call TabletServer.Commit failed: %v", err) 1051 } 1052 }() 1053 1054 // At this point, all three transactions should be blocked in BeginExecute() 1055 // and therefore count as pending transaction by the Hot Row Protection. 1056 // 1057 // NOTE: We are not doing more sophisticated synchronizations between the 1058 // transactions via db.SetBeforeFunc() for the same reason as mentioned 1059 // in TestSerializeTransactionsSameRow: The MySQL C client does not seem 1060 // to allow more than connection attempt at a time. 1061 err := waitForTxSerializationPendingQueries(tsv, "test_table where pk = 1 and `name` = 1", 3) 1062 require.NoError(t, err) 1063 close(allQueriesPending) 1064 1065 wg.Wait() 1066 1067 got, ok := tsv.stats.WaitTimings.Counts()["TabletServerTest.TxSerializer"] 1068 want := countStart + 1 1069 if !ok || got != want { 1070 t.Fatalf("One out of the three transactions must have waited: ok? %v got: %v want: %v", ok, got, want) 1071 } 1072 } 1073 1074 func waitForTxSerializationPendingQueries(tsv *TabletServer, key string, i int) error { 1075 start := time.Now() 1076 for { 1077 got, want := tsv.qe.txSerializer.Pending(key), i 1078 if got == want { 1079 return nil 1080 } 1081 1082 if time.Since(start) > 10*time.Second { 1083 return fmt.Errorf("wait for query count increase in TxSerializer timed out: got = %v, want = %v", got, want) 1084 } 1085 time.Sleep(1 * time.Millisecond) 1086 } 1087 } 1088 1089 func TestSerializeTransactionsSameRow_TooManyPendingRequests(t *testing.T) { 1090 // This test is similar to TestSerializeTransactionsSameRow, but tests only 1091 // that there must not be too many pending BeginExecute() requests which are 1092 // serialized. 1093 // Since we start to queue before the transaction pool would queue, we need 1094 // to enforce an upper limit as well to protect vttablet. 1095 config := tabletenv.NewDefaultConfig() 1096 config.HotRowProtection.Mode = tabletenv.Enable 1097 config.HotRowProtection.MaxQueueSize = 1 1098 config.HotRowProtection.MaxConcurrency = 1 1099 db, tsv := setupTabletServerTestCustom(t, config, "") 1100 defer tsv.StopService() 1101 defer db.Close() 1102 1103 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 1104 countStart := tsv.stats.WaitTimings.Counts()["TabletServerTest.TxSerializer"] 1105 1106 // Fake data. 1107 q1 := "update test_table set name_string = 'tx1' where pk = :pk and `name` = :name" 1108 q2 := "update test_table set name_string = 'tx2' where pk = :pk and `name` = :name" 1109 // Every request needs their own bind variables to avoid data races. 1110 bvTx1 := map[string]*querypb.BindVariable{ 1111 "pk": sqltypes.Int64BindVariable(1), 1112 "name": sqltypes.Int64BindVariable(1), 1113 } 1114 bvTx2 := map[string]*querypb.BindVariable{ 1115 "pk": sqltypes.Int64BindVariable(1), 1116 "name": sqltypes.Int64BindVariable(1), 1117 } 1118 1119 // Make sure that tx2 starts only after tx1 is running its Execute(). 1120 tx1Started := make(chan struct{}) 1121 // Signal when tx2 is done. 1122 tx2Failed := make(chan struct{}) 1123 1124 db.SetBeforeFunc("update test_table set name_string = 'tx1' where pk = 1 and `name` = 1 limit 10001", 1125 func() { 1126 close(tx1Started) 1127 <-tx2Failed 1128 }) 1129 1130 // Run the two transactions. 1131 wg := sync.WaitGroup{} 1132 1133 // tx1. 1134 wg.Add(1) 1135 go func() { 1136 defer wg.Done() 1137 1138 state1, _, err := tsv.BeginExecute(ctx, &target, nil, q1, bvTx1, 0, nil) 1139 if err != nil { 1140 t.Errorf("failed to execute query: %s: %s", q1, err) 1141 } 1142 if _, err := tsv.Commit(ctx, &target, state1.TransactionID); err != nil { 1143 t.Errorf("call TabletServer.Commit failed: %v", err) 1144 } 1145 }() 1146 1147 // tx2. 1148 wg.Add(1) 1149 go func() { 1150 defer wg.Done() 1151 defer close(tx2Failed) 1152 1153 <-tx1Started 1154 _, _, err := tsv.BeginExecute(ctx, &target, nil, q2, bvTx2, 0, nil) 1155 if err == nil || vterrors.Code(err) != vtrpcpb.Code_RESOURCE_EXHAUSTED || err.Error() != "hot row protection: too many queued transactions (1 >= 1) for the same row (table + WHERE clause: 'test_table where pk = 1 and `name` = 1')" { 1156 t.Errorf("tx2 should have failed because there are too many pending requests: %v", err) 1157 } 1158 // No commit necessary because the Begin failed. 1159 }() 1160 1161 wg.Wait() 1162 1163 got := tsv.stats.WaitTimings.Counts()["TabletServerTest.TxSerializer"] 1164 want := countStart + 0 1165 if got != want { 1166 t.Fatalf("tx2 should have failed early and not tracked as serialized: got: %v want: %v", got, want) 1167 } 1168 } 1169 1170 func TestSerializeTransactionsSameRow_RequestCanceled(t *testing.T) { 1171 // This test is similar to TestSerializeTransactionsSameRow, but tests only 1172 // that a queued request unblocks itself when its context is done. 1173 // 1174 // tx1 and tx2 run against the same row. 1175 // tx2 is blocked on tx1. Eventually, tx2 is canceled and its request fails. 1176 // Only after that tx1 commits and finishes. 1177 config := tabletenv.NewDefaultConfig() 1178 config.HotRowProtection.Mode = tabletenv.Enable 1179 config.HotRowProtection.MaxConcurrency = 1 1180 db, tsv := setupTabletServerTestCustom(t, config, "") 1181 defer tsv.StopService() 1182 defer db.Close() 1183 1184 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 1185 countStart := tsv.stats.WaitTimings.Counts()["TabletServerTest.TxSerializer"] 1186 1187 // Fake data. 1188 q1 := "update test_table set name_string = 'tx1' where pk = :pk and `name` = :name" 1189 q2 := "update test_table set name_string = 'tx2' where pk = :pk and `name` = :name" 1190 q3 := "update test_table set name_string = 'tx3' where pk = :pk and `name` = :name" 1191 // Every request needs their own bind variables to avoid data races. 1192 bvTx1 := map[string]*querypb.BindVariable{ 1193 "pk": sqltypes.Int64BindVariable(1), 1194 "name": sqltypes.Int64BindVariable(1), 1195 } 1196 bvTx2 := map[string]*querypb.BindVariable{ 1197 "pk": sqltypes.Int64BindVariable(1), 1198 "name": sqltypes.Int64BindVariable(1), 1199 } 1200 bvTx3 := map[string]*querypb.BindVariable{ 1201 "pk": sqltypes.Int64BindVariable(1), 1202 "name": sqltypes.Int64BindVariable(1), 1203 } 1204 1205 // Make sure that tx2 starts only after tx1 is running its Execute(). 1206 tx1Started := make(chan struct{}) 1207 // Signal when tx2 is done. 1208 tx2Done := make(chan struct{}) 1209 1210 db.SetBeforeFunc("update test_table set name_string = 'tx1' where pk = 1 and `name` = 1 limit 10001", 1211 func() { 1212 close(tx1Started) 1213 // Keep blocking until tx2 was canceled. 1214 <-tx2Done 1215 }) 1216 1217 // Run the two transactions. 1218 wg := sync.WaitGroup{} 1219 1220 // tx1. 1221 wg.Add(1) 1222 go func() { 1223 defer wg.Done() 1224 1225 state1, _, err := tsv.BeginExecute(ctx, &target, nil, q1, bvTx1, 0, nil) 1226 if err != nil { 1227 t.Errorf("failed to execute query: %s: %s", q1, err) 1228 } 1229 1230 if _, err := tsv.Commit(ctx, &target, state1.TransactionID); err != nil { 1231 t.Errorf("call TabletServer.Commit failed: %v", err) 1232 } 1233 }() 1234 1235 // tx2. 1236 ctxTx2, cancelTx2 := context.WithCancel(ctx) 1237 wg.Add(1) 1238 go func() { 1239 defer wg.Done() 1240 defer close(tx2Done) 1241 1242 // Wait until tx1 has started to make the test deterministic. 1243 <-tx1Started 1244 1245 _, _, err := tsv.BeginExecute(ctxTx2, &target, nil, q2, bvTx2, 0, nil) 1246 if err == nil || vterrors.Code(err) != vtrpcpb.Code_CANCELED || err.Error() != "context canceled" { 1247 t.Errorf("tx2 should have failed because the context was canceled: %v", err) 1248 } 1249 // No commit necessary because the Begin failed. 1250 }() 1251 1252 // tx3. 1253 wg.Add(1) 1254 go func() { 1255 defer wg.Done() 1256 1257 // Wait until tx1 and tx2 are pending to make the test deterministic. 1258 if err := waitForTxSerializationPendingQueries(tsv, "test_table where pk = 1 and `name` = 1", 2); err != nil { 1259 t.Error(err) 1260 } 1261 1262 state3, _, err := tsv.BeginExecute(ctx, &target, nil, q3, bvTx3, 0, nil) 1263 if err != nil { 1264 t.Errorf("failed to execute query: %s: %s", q3, err) 1265 } 1266 1267 if _, err := tsv.Commit(ctx, &target, state3.TransactionID); err != nil { 1268 t.Errorf("call TabletServer.Commit failed: %v", err) 1269 } 1270 }() 1271 1272 // Wait until tx1, 2 and 3 are pending. 1273 err := waitForTxSerializationPendingQueries(tsv, "test_table where pk = 1 and `name` = 1", 3) 1274 require.NoError(t, err) 1275 // Now unblock tx2 and cancel it. 1276 cancelTx2() 1277 1278 wg.Wait() 1279 1280 got, ok := tsv.stats.WaitTimings.Counts()["TabletServerTest.TxSerializer"] 1281 want := countStart + 2 1282 if got != want { 1283 t.Fatalf("tx2 and tx3 should have been serialized: ok? %v got: %v want: %v", ok, got, want) 1284 } 1285 } 1286 1287 func TestMessageStream(t *testing.T) { 1288 _, tsv, db := newTestTxExecutor(t) 1289 defer db.Close() 1290 defer tsv.StopService() 1291 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 1292 1293 err := tsv.MessageStream(ctx, &target, "nomsg", func(qr *sqltypes.Result) error { 1294 return nil 1295 }) 1296 wantErr := "table nomsg not found in schema" 1297 if err == nil || err.Error() != wantErr { 1298 t.Errorf("tsv.MessageStream: %v, want %s", err, wantErr) 1299 } 1300 1301 // Check that the streaming mechanism works. 1302 called := false 1303 err = tsv.MessageStream(ctx, &target, "msg", func(qr *sqltypes.Result) error { 1304 called = true 1305 return io.EOF 1306 }) 1307 require.NoError(t, err) 1308 if !called { 1309 t.Fatal("callback was not called for MessageStream") 1310 } 1311 } 1312 1313 func TestCheckMySQLGauge(t *testing.T) { 1314 _, tsv, db := newTestTxExecutor(t) 1315 defer db.Close() 1316 defer tsv.StopService() 1317 1318 // Check that initially checkMySQLGauge has 0 value 1319 assert.EqualValues(t, 0, tsv.checkMysqlGaugeFunc.Get()) 1320 tsv.CheckMySQL() 1321 // After the checkMySQL call checkMySQLGauge should have 1 value 1322 assert.EqualValues(t, 1, tsv.checkMysqlGaugeFunc.Get()) 1323 1324 // Wait for CheckMySQL to finish. 1325 // This wait is required because CheckMySQL waits for 1 second after it finishes execution 1326 // before letting go of the acquired locks. 1327 timeout := time.After(2 * time.Second) 1328 for { 1329 select { 1330 case <-timeout: 1331 t.Fatalf("Timedout waiting for CheckMySQL to finish") 1332 default: 1333 if tsv.checkMysqlGaugeFunc.Get() == 0 { 1334 return 1335 } 1336 time.Sleep(100 * time.Millisecond) 1337 } 1338 } 1339 } 1340 1341 func TestMessageAck(t *testing.T) { 1342 _, tsv, db := newTestTxExecutor(t) 1343 defer db.Close() 1344 defer tsv.StopService() 1345 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 1346 1347 ids := []*querypb.Value{{ 1348 Type: sqltypes.VarChar, 1349 Value: []byte("1"), 1350 }, { 1351 Type: sqltypes.VarChar, 1352 Value: []byte("2"), 1353 }} 1354 _, err := tsv.MessageAck(ctx, &target, "nonmsg", ids) 1355 want := "message table nonmsg not found in schema" 1356 require.Error(t, err) 1357 require.Contains(t, err.Error(), want) 1358 1359 _, err = tsv.MessageAck(ctx, &target, "msg", ids) 1360 want = "query: 'update msg set time_acked" 1361 require.Error(t, err) 1362 assert.Contains(t, err.Error(), want) 1363 1364 db.AddQueryPattern("update msg set time_acked = .*", &sqltypes.Result{RowsAffected: 1}) 1365 count, err := tsv.MessageAck(ctx, &target, "msg", ids) 1366 require.NoError(t, err) 1367 require.EqualValues(t, 1, count) 1368 } 1369 1370 func TestRescheduleMessages(t *testing.T) { 1371 _, tsv, db := newTestTxExecutor(t) 1372 defer db.Close() 1373 defer tsv.StopService() 1374 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 1375 1376 _, err := tsv.messager.GetGenerator("nonmsg") 1377 want := "message table nonmsg not found in schema" 1378 require.Error(t, err) 1379 require.Contains(t, err.Error(), want) 1380 1381 gen, err := tsv.messager.GetGenerator("msg") 1382 require.NoError(t, err) 1383 1384 _, err = tsv.PostponeMessages(ctx, &target, gen, []string{"1", "2"}) 1385 want = "query: 'update msg set time_next" 1386 require.Error(t, err) 1387 assert.Contains(t, err.Error(), want) 1388 db.AddQueryPattern("update msg set time_next = .*", &sqltypes.Result{RowsAffected: 1}) 1389 count, err := tsv.PostponeMessages(ctx, &target, gen, []string{"1", "2"}) 1390 require.NoError(t, err) 1391 require.EqualValues(t, 1, count) 1392 } 1393 1394 func TestPurgeMessages(t *testing.T) { 1395 _, tsv, db := newTestTxExecutor(t) 1396 defer db.Close() 1397 defer tsv.StopService() 1398 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 1399 1400 _, err := tsv.messager.GetGenerator("nonmsg") 1401 want := "message table nonmsg not found in schema" 1402 require.Error(t, err) 1403 require.Contains(t, err.Error(), want) 1404 1405 gen, err := tsv.messager.GetGenerator("msg") 1406 require.NoError(t, err) 1407 1408 _, err = tsv.PurgeMessages(ctx, &target, gen, 0) 1409 want = "query: 'delete from msg where time_acked" 1410 require.Error(t, err) 1411 assert.Contains(t, err.Error(), want) 1412 1413 db.AddQuery("delete from msg where time_acked < 3 limit 500", &sqltypes.Result{RowsAffected: 1}) 1414 count, err := tsv.PurgeMessages(ctx, &target, gen, 3) 1415 require.NoError(t, err) 1416 require.EqualValues(t, 1, count) 1417 } 1418 1419 func TestHandleExecUnknownError(t *testing.T) { 1420 logStats := tabletenv.NewLogStats(ctx, "TestHandleExecError") 1421 config := tabletenv.NewDefaultConfig() 1422 tsv := NewTabletServer("TabletServerTest", config, memorytopo.NewServer(""), &topodatapb.TabletAlias{}) 1423 defer tsv.handlePanicAndSendLogStats("select * from test_table", nil, logStats) 1424 panic("unknown exec error") 1425 } 1426 1427 type testLogger struct { 1428 logs []string 1429 savedInfof func(format string, args ...any) 1430 savedErrorf func(format string, args ...any) 1431 } 1432 1433 func newTestLogger() *testLogger { 1434 tl := &testLogger{ 1435 savedInfof: log.Infof, 1436 savedErrorf: log.Errorf, 1437 } 1438 log.Infof = tl.recordInfof 1439 log.Errorf = tl.recordErrorf 1440 return tl 1441 } 1442 1443 func (tl *testLogger) Close() { 1444 log.Infof = tl.savedInfof 1445 log.Errorf = tl.savedErrorf 1446 } 1447 1448 func (tl *testLogger) recordInfof(format string, args ...any) { 1449 msg := fmt.Sprintf(format, args...) 1450 tl.logs = append(tl.logs, msg) 1451 tl.savedInfof(msg) 1452 } 1453 1454 func (tl *testLogger) recordErrorf(format string, args ...any) { 1455 msg := fmt.Sprintf(format, args...) 1456 tl.logs = append(tl.logs, msg) 1457 tl.savedErrorf(msg) 1458 } 1459 1460 func (tl *testLogger) getLog(i int) string { 1461 if i < len(tl.logs) { 1462 return tl.logs[i] 1463 } 1464 return fmt.Sprintf("ERROR: log %d/%d does not exist", i, len(tl.logs)) 1465 } 1466 1467 func TestHandleExecTabletError(t *testing.T) { 1468 config := tabletenv.NewDefaultConfig() 1469 tsv := NewTabletServer("TabletServerTest", config, memorytopo.NewServer(""), &topodatapb.TabletAlias{}) 1470 tl := newTestLogger() 1471 defer tl.Close() 1472 err := tsv.convertAndLogError( 1473 ctx, 1474 "select * from test_table", 1475 nil, 1476 vterrors.Errorf(vtrpcpb.Code_INTERNAL, "tablet error"), 1477 nil, 1478 ) 1479 want := "tablet error" 1480 require.Error(t, err) 1481 assert.Contains(t, err.Error(), want) 1482 want = "Sql: \"select * from test_table\", BindVars: {}" 1483 if !strings.Contains(tl.getLog(0), want) { 1484 t.Errorf("error log %s, want '%s'", tl.getLog(0), want) 1485 } 1486 } 1487 1488 func TestTerseErrors(t *testing.T) { 1489 config := tabletenv.NewDefaultConfig() 1490 config.TerseErrors = true 1491 config.SanitizeLogMessages = false 1492 tsv := NewTabletServer("TabletServerTest", config, memorytopo.NewServer(""), &topodatapb.TabletAlias{}) 1493 tl := newTestLogger() 1494 defer tl.Close() 1495 1496 sql := "select * from test_table where xyz = :vtg1 order by abc desc" 1497 sqlErr := mysql.NewSQLError(10, "HY000", "sensitive message") 1498 sqlErr.Query = "select * from test_table where xyz = 'this is kinda long eh'" 1499 err := tsv.convertAndLogError( 1500 ctx, 1501 sql, 1502 map[string]*querypb.BindVariable{"vtg1": sqltypes.StringBindVariable("this is kinda long eh")}, 1503 sqlErr, 1504 nil, 1505 ) 1506 1507 // The client error message should be redacted (made terse) 1508 wantErr := "(errno 10) (sqlstate HY000): Sql: \"select * from test_table where xyz = :vtg1 order by abc desc\", BindVars: {[REDACTED]}" 1509 if err == nil || err.Error() != wantErr { 1510 t.Errorf("error got '%v', want '%s'", err, wantErr) 1511 } 1512 1513 // But the log message should NOT be 1514 wantLog := "(errno 10) (sqlstate HY000): Sql: \"select * from test_table where xyz = :vtg1 order by abc desc\", BindVars: {vtg1: \"type:VARCHAR value:\\\"this is kinda long eh\\\"\"}" 1515 if wantLog != tl.getLog(0) { 1516 t.Errorf("log got '%s', want '%s'", tl.getLog(0), wantLog) 1517 } 1518 } 1519 1520 func TestSanitizeLogMessages(t *testing.T) { 1521 config := tabletenv.NewDefaultConfig() 1522 config.TerseErrors = false 1523 config.SanitizeLogMessages = true 1524 tsv := NewTabletServer("TabletServerTest", config, memorytopo.NewServer(""), &topodatapb.TabletAlias{}) 1525 tl := newTestLogger() 1526 defer tl.Close() 1527 1528 sql := "select * from test_table where xyz = :vtg1 order by abc desc" 1529 sqlErr := mysql.NewSQLError(10, "HY000", "sensitive message") 1530 sqlErr.Query = "select * from test_table where xyz = 'this is pretty rad my doo, getting swole'" 1531 err := tsv.convertAndLogError( 1532 ctx, 1533 sql, 1534 map[string]*querypb.BindVariable{"vtg1": sqltypes.StringBindVariable("this is pretty rad my doo, getting swole")}, 1535 sqlErr, 1536 nil, 1537 ) 1538 1539 // Error is not sanitized, nor truncated 1540 wantErr := "sensitive message (errno 10) (sqlstate HY000): Sql: \"select * from test_table where xyz = :vtg1 order by abc desc\", BindVars: {vtg1: \"type:VARCHAR value:\\\"this is pretty rad my doo, getting swole\\\"\"}" 1541 if err == nil || err.Error() != wantErr { 1542 t.Errorf("error got '%v', want '%s'", err, wantErr) 1543 } 1544 1545 // But the log message is sanitized 1546 wantLog := "sensitive message (errno 10) (sqlstate HY000): Sql: \"select * from test_table where xyz = :vtg1 order by abc desc\", BindVars: {[REDACTED]}" 1547 if wantLog != tl.getLog(0) { 1548 t.Errorf("log got '%s', want '%s'", tl.getLog(0), wantLog) 1549 } 1550 } 1551 1552 func TestTerseErrorsNonSQLError(t *testing.T) { 1553 config := tabletenv.NewDefaultConfig() 1554 config.TerseErrors = true 1555 tsv := NewTabletServer("TabletServerTest", config, memorytopo.NewServer(""), &topodatapb.TabletAlias{}) 1556 tl := newTestLogger() 1557 defer tl.Close() 1558 err := tsv.convertAndLogError( 1559 ctx, 1560 "select * from test_table", 1561 nil, 1562 vterrors.Errorf(vtrpcpb.Code_INTERNAL, "tablet error"), 1563 nil, 1564 ) 1565 want := "tablet error" 1566 require.Error(t, err) 1567 assert.Contains(t, err.Error(), want) 1568 want = "Sql: \"select * from test_table\", BindVars: {}" 1569 if !strings.Contains(tl.getLog(0), want) { 1570 t.Errorf("error log %s, want '%s'", tl.getLog(0), want) 1571 } 1572 } 1573 1574 func TestSanitizeLogMessagesNonSQLError(t *testing.T) { 1575 config := tabletenv.NewDefaultConfig() 1576 config.TerseErrors = false 1577 config.SanitizeLogMessages = true 1578 tsv := NewTabletServer("TabletServerTest", config, memorytopo.NewServer(""), &topodatapb.TabletAlias{}) 1579 tl := newTestLogger() 1580 defer tl.Close() 1581 err := tsv.convertAndLogError( 1582 ctx, 1583 "select * from test_table where a = :a", 1584 map[string]*querypb.BindVariable{"a": sqltypes.Int64BindVariable(5)}, 1585 vterrors.Errorf(vtrpcpb.Code_INTERNAL, "tablet error"), 1586 nil, 1587 ) 1588 want := "tablet error" 1589 require.Error(t, err) 1590 assert.Contains(t, err.Error(), want) 1591 want = "Sql: \"select * from test_table where a = :a\", BindVars: {[REDACTED]}" 1592 if !strings.Contains(tl.getLog(0), want) { 1593 t.Errorf("error log %s, want '%s'", tl.getLog(0), want) 1594 } 1595 } 1596 1597 func TestSanitizeMessagesBindVars(t *testing.T) { 1598 config := tabletenv.NewDefaultConfig() 1599 config.TerseErrors = true 1600 config.SanitizeLogMessages = true 1601 tsv := NewTabletServer("TabletServerTest", config, memorytopo.NewServer(""), &topodatapb.TabletAlias{}) 1602 tl := newTestLogger() 1603 defer tl.Close() 1604 1605 sqlErr := mysql.NewSQLError(10, "HY000", "sensitive message") 1606 sqlErr.Query = "select * from test_table where a = 1" 1607 1608 err := tsv.convertAndLogError( 1609 ctx, 1610 "select * from test_table where a = :a", 1611 map[string]*querypb.BindVariable{"a": sqltypes.Int64BindVariable(1)}, 1612 sqlErr, 1613 nil, 1614 ) 1615 wantErr := "(errno 10) (sqlstate HY000): Sql: \"select * from test_table where a = :a\", BindVars: {[REDACTED]}" 1616 if err == nil || err.Error() != wantErr { 1617 t.Errorf("error got '%v', want '%s'", err, wantErr) 1618 } 1619 1620 wantLog := wantErr 1621 if wantLog != tl.getLog(0) { 1622 t.Errorf("log got '%s', want '%s'", tl.getLog(0), wantLog) 1623 } 1624 } 1625 1626 func TestSanitizeMessagesNoBindVars(t *testing.T) { 1627 config := tabletenv.NewDefaultConfig() 1628 config.TerseErrors = true 1629 config.SanitizeLogMessages = true 1630 tsv := NewTabletServer("TabletServerTest", config, memorytopo.NewServer(""), &topodatapb.TabletAlias{}) 1631 tl := newTestLogger() 1632 defer tl.Close() 1633 err := tsv.convertAndLogError(ctx, "", nil, vterrors.Errorf(vtrpcpb.Code_DEADLINE_EXCEEDED, "sensitive message"), nil) 1634 want := "sensitive message" 1635 require.Error(t, err) 1636 assert.Contains(t, err.Error(), want) 1637 want = "Sql: \"\", BindVars: {}" 1638 if !strings.Contains(tl.getLog(0), want) { 1639 t.Errorf("error log '%s', want '%s'", tl.getLog(0), want) 1640 } 1641 } 1642 1643 func TestTruncateMessages(t *testing.T) { 1644 config := tabletenv.NewDefaultConfig() 1645 config.TerseErrors = false 1646 // Sanitize the log messages, which means that the bind vars are omitted 1647 config.SanitizeLogMessages = true 1648 tsv := NewTabletServer("TabletServerTest", config, memorytopo.NewServer(""), &topodatapb.TabletAlias{}) 1649 tl := newTestLogger() 1650 defer tl.Close() 1651 1652 sqlparser.SetTruncateErrLen(52) 1653 sql := "select * from test_table where xyz = :vtg1 order by abc desc" 1654 sqlErr := mysql.NewSQLError(10, "HY000", "sensitive message") 1655 sqlErr.Query = "select * from test_table where xyz = 'this is kinda long eh'" 1656 err := tsv.convertAndLogError( 1657 ctx, 1658 sql, 1659 map[string]*querypb.BindVariable{"vtg1": sqltypes.StringBindVariable("this is kinda long eh")}, 1660 sqlErr, 1661 nil, 1662 ) 1663 1664 // Error not truncated 1665 wantErr := "sensitive message (errno 10) (sqlstate HY000): Sql: \"select * from test_table where xyz = :vtg1 order by abc desc\", BindVars: {vtg1: \"type:VARCHAR value:\\\"this is kinda long eh\\\"\"}" 1666 if err == nil || err.Error() != wantErr { 1667 t.Errorf("error got '%v', want '%s'", err, wantErr) 1668 } 1669 1670 // but log *is* truncated, and sanitized 1671 wantLog := "sensitive message (errno 10) (sqlstate HY000): Sql: \"select * from test_table where xyz = :vt [TRUNCATED]\", BindVars: {[REDACTED]}" 1672 if wantLog != tl.getLog(0) { 1673 t.Errorf("log got '%s', want '%s'", tl.getLog(0), wantLog) 1674 } 1675 1676 sqlparser.SetTruncateErrLen(140) 1677 err = tsv.convertAndLogError( 1678 ctx, 1679 sql, 1680 map[string]*querypb.BindVariable{"vtg1": sqltypes.StringBindVariable("this is kinda long eh")}, 1681 sqlErr, 1682 nil, 1683 ) 1684 1685 // Error not truncated 1686 wantErr = "sensitive message (errno 10) (sqlstate HY000): Sql: \"select * from test_table where xyz = :vtg1 order by abc desc\", BindVars: {vtg1: \"type:VARCHAR value:\\\"this is kinda long eh\\\"\"}" 1687 if err == nil || err.Error() != wantErr { 1688 t.Errorf("error got '%v', want '%s'", err, wantErr) 1689 } 1690 1691 // Log not truncated, since our limit is large enough now, but it is still sanitized 1692 wantLog = "sensitive message (errno 10) (sqlstate HY000): Sql: \"select * from test_table where xyz = :vtg1 order by abc desc\", BindVars: {[REDACTED]}" 1693 if wantLog != tl.getLog(1) { 1694 t.Errorf("log got '%s', want '%s'", tl.getLog(1), wantLog) 1695 } 1696 sqlparser.SetTruncateErrLen(0) 1697 } 1698 1699 func TestTerseErrorsIgnoreFailoverInProgress(t *testing.T) { 1700 config := tabletenv.NewDefaultConfig() 1701 config.TerseErrors = true 1702 tsv := NewTabletServer("TabletServerTest", config, memorytopo.NewServer(""), &topodatapb.TabletAlias{}) 1703 tl := newTestLogger() 1704 defer tl.Close() 1705 err := tsv.convertAndLogError(ctx, "select * from test_table where id = :a", 1706 map[string]*querypb.BindVariable{"a": sqltypes.Int64BindVariable(1)}, 1707 mysql.NewSQLError(1227, mysql.SSClientError, "failover in progress"), 1708 nil, 1709 ) 1710 if got, want := err.Error(), "failover in progress (errno 1227) (sqlstate 42000)"; !strings.HasPrefix(got, want) { 1711 t.Fatalf("'failover in progress' text must never be stripped: got = %v, want = %v", got, want) 1712 } 1713 1714 // errors during failover aren't logged at all 1715 require.Empty(t, tl.logs, "unexpected error log during failover") 1716 } 1717 1718 var aclJSON1 = `{ 1719 "table_groups": [ 1720 { 1721 "name": "group01", 1722 "table_names_or_prefixes": ["test_table1"], 1723 "readers": ["vt1"], 1724 "writers": ["vt1"] 1725 } 1726 ] 1727 }` 1728 var aclJSON2 = `{ 1729 "table_groups": [ 1730 { 1731 "name": "group02", 1732 "table_names_or_prefixes": ["test_table2"], 1733 "readers": ["vt2"], 1734 "admins": ["vt2"] 1735 } 1736 ] 1737 }` 1738 1739 func TestACLHUP(t *testing.T) { 1740 tableacl.Register("simpleacl", &simpleacl.Factory{}) 1741 config := tabletenv.NewDefaultConfig() 1742 tsv := NewTabletServer("TabletServerTest", config, memorytopo.NewServer(""), &topodatapb.TabletAlias{}) 1743 1744 f, err := os.CreateTemp("", "tableacl") 1745 require.NoError(t, err) 1746 defer os.Remove(f.Name()) 1747 1748 _, err = io.WriteString(f, aclJSON1) 1749 require.NoError(t, err) 1750 err = f.Close() 1751 require.NoError(t, err) 1752 1753 tsv.InitACL(f.Name(), true, 0) 1754 1755 groups1 := tableacl.GetCurrentConfig().TableGroups 1756 if name1 := groups1[0].GetName(); name1 != "group01" { 1757 t.Fatalf("Expected name 'group01', got '%s'", name1) 1758 } 1759 1760 f, err = os.Create(f.Name()) 1761 require.NoError(t, err) 1762 _, err = io.WriteString(f, aclJSON2) 1763 require.NoError(t, err) 1764 1765 syscall.Kill(syscall.Getpid(), syscall.SIGHUP) 1766 time.Sleep(100 * time.Millisecond) // wait for signal handler 1767 1768 groups2 := tableacl.GetCurrentConfig().TableGroups 1769 if len(groups2) != 1 { 1770 t.Fatalf("Expected only one table group") 1771 } 1772 group2 := groups2[0] 1773 if name2 := group2.GetName(); name2 != "group02" { 1774 t.Fatalf("Expected name 'group02', got '%s'", name2) 1775 } 1776 if group2.GetAdmins() == nil { 1777 t.Fatalf("Expected 'admins' to exist, but it didn't") 1778 } 1779 if group2.GetWriters() != nil { 1780 t.Fatalf("Expected 'writers' to not exist, got '%s'", group2.GetWriters()) 1781 } 1782 } 1783 1784 func TestConfigChanges(t *testing.T) { 1785 db, tsv := setupTabletServerTest(t, "") 1786 defer tsv.StopService() 1787 defer db.Close() 1788 1789 newSize := 10 1790 newDuration := time.Duration(10 * time.Millisecond) 1791 1792 tsv.SetPoolSize(newSize) 1793 if val := tsv.PoolSize(); val != newSize { 1794 t.Errorf("PoolSize: %d, want %d", val, newSize) 1795 } 1796 if val := int(tsv.qe.conns.Capacity()); val != newSize { 1797 t.Errorf("tsv.qe.connPool.Capacity: %d, want %d", val, newSize) 1798 } 1799 1800 tsv.SetStreamPoolSize(newSize) 1801 if val := tsv.StreamPoolSize(); val != newSize { 1802 t.Errorf("StreamPoolSize: %d, want %d", val, newSize) 1803 } 1804 if val := int(tsv.qe.streamConns.Capacity()); val != newSize { 1805 t.Errorf("tsv.qe.streamConnPool.Capacity: %d, want %d", val, newSize) 1806 } 1807 1808 tsv.SetTxPoolSize(newSize) 1809 if val := tsv.TxPoolSize(); val != newSize { 1810 t.Errorf("TxPoolSize: %d, want %d", val, newSize) 1811 } 1812 if val := int(tsv.te.txPool.scp.Capacity()); val != newSize { 1813 t.Errorf("tsv.te.txPool.pool.Capacity: %d, want %d", val, newSize) 1814 } 1815 1816 tsv.Config().SetTxTimeoutForWorkload(newDuration, querypb.ExecuteOptions_OLTP) 1817 if val := tsv.Config().TxTimeoutForWorkload(querypb.ExecuteOptions_OLTP); val != newDuration { 1818 t.Errorf("tsv.TxTimeout: %v, want %v", val, newDuration) 1819 } 1820 if val := tsv.te.txPool.env.Config().TxTimeoutForWorkload(querypb.ExecuteOptions_OLTP); val != newDuration { 1821 t.Errorf("tsv.te.Pool().Timeout: %v, want %v", val, newDuration) 1822 } 1823 1824 tsv.SetQueryPlanCacheCap(newSize) 1825 if val := tsv.QueryPlanCacheCap(); val != newSize { 1826 t.Errorf("QueryPlanCacheCap: %d, want %d", val, newSize) 1827 } 1828 if val := int(tsv.qe.QueryPlanCacheCap()); val != newSize { 1829 t.Errorf("tsv.qe.QueryPlanCacheCap: %d, want %d", val, newSize) 1830 } 1831 1832 tsv.SetMaxResultSize(newSize) 1833 if val := tsv.MaxResultSize(); val != newSize { 1834 t.Errorf("MaxResultSize: %d, want %d", val, newSize) 1835 } 1836 if val := int(tsv.qe.maxResultSize.Get()); val != newSize { 1837 t.Errorf("tsv.qe.maxResultSize.Get: %d, want %d", val, newSize) 1838 } 1839 1840 tsv.SetWarnResultSize(newSize) 1841 if val := tsv.WarnResultSize(); val != newSize { 1842 t.Errorf("WarnResultSize: %d, want %d", val, newSize) 1843 } 1844 if val := int(tsv.qe.warnResultSize.Get()); val != newSize { 1845 t.Errorf("tsv.qe.warnResultSize.Get: %d, want %d", val, newSize) 1846 } 1847 } 1848 1849 func TestReserveBeginExecute(t *testing.T) { 1850 db, tsv := setupTabletServerTest(t, "") 1851 defer tsv.StopService() 1852 defer db.Close() 1853 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 1854 1855 state, _, err := tsv.ReserveBeginExecute(ctx, &target, []string{"select 43"}, nil, "select 42", nil, &querypb.ExecuteOptions{}) 1856 require.NoError(t, err) 1857 1858 assert.Greater(t, state.TransactionID, int64(0), "transactionID") 1859 assert.Equal(t, state.ReservedID, state.TransactionID, "reservedID should equal transactionID") 1860 expected := []string{ 1861 "select 43", 1862 "begin", 1863 "select 42 from dual limit 10001", 1864 } 1865 splitOutput := strings.Split(db.QueryLog(), ";") 1866 for _, exp := range expected { 1867 assert.Contains(t, splitOutput, exp, "expected queries to run") 1868 } 1869 err = tsv.Release(ctx, &target, state.TransactionID, state.ReservedID) 1870 require.NoError(t, err) 1871 } 1872 1873 func TestReserveExecute_WithoutTx(t *testing.T) { 1874 db, tsv := setupTabletServerTest(t, "") 1875 defer tsv.StopService() 1876 defer db.Close() 1877 1878 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 1879 1880 state, _, err := tsv.ReserveExecute(ctx, &target, []string{"select 43"}, "select 42", nil, 0, &querypb.ExecuteOptions{}) 1881 require.NoError(t, err) 1882 assert.NotEqual(t, int64(0), state.ReservedID, "reservedID should not be zero") 1883 expected := []string{ 1884 "select 43", 1885 "select 42 from dual limit 10001", 1886 } 1887 splitOutput := strings.Split(db.QueryLog(), ";") 1888 for _, exp := range expected { 1889 assert.Contains(t, splitOutput, exp, "expected queries to run") 1890 } 1891 err = tsv.Release(ctx, &target, 0, state.ReservedID) 1892 require.NoError(t, err) 1893 } 1894 1895 func TestReserveExecute_WithTx(t *testing.T) { 1896 db, tsv := setupTabletServerTest(t, "") 1897 defer tsv.StopService() 1898 defer db.Close() 1899 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 1900 1901 beginState, err := tsv.Begin(ctx, &target, &querypb.ExecuteOptions{}) 1902 require.NoError(t, err) 1903 require.NotEqual(t, int64(0), beginState.TransactionID) 1904 db.ResetQueryLog() 1905 1906 reserveState, _, err := tsv.ReserveExecute(ctx, &target, []string{"select 43"}, "select 42", nil, beginState.TransactionID, &querypb.ExecuteOptions{}) 1907 require.NoError(t, err) 1908 defer tsv.Release(ctx, &target, beginState.TransactionID, reserveState.ReservedID) 1909 assert.Equal(t, beginState.TransactionID, reserveState.ReservedID, "reservedID should be equal to transactionID") 1910 expected := []string{ 1911 "select 43", 1912 "select 42 from dual limit 10001", 1913 } 1914 splitOutput := strings.Split(db.QueryLog(), ";") 1915 for _, exp := range expected { 1916 assert.Contains(t, splitOutput, exp, "expected queries to run") 1917 } 1918 err = tsv.Release(ctx, &target, beginState.TransactionID, reserveState.ReservedID) 1919 require.NoError(t, err) 1920 } 1921 1922 func TestRelease(t *testing.T) { 1923 type testcase struct { 1924 begin, reserve bool 1925 expectedQueries []string 1926 err bool 1927 } 1928 1929 tests := []testcase{{ 1930 begin: true, 1931 reserve: false, 1932 expectedQueries: []string{"rollback"}, 1933 }, { 1934 begin: true, 1935 reserve: true, 1936 }, { 1937 begin: false, 1938 reserve: true, 1939 }, { 1940 begin: false, 1941 reserve: false, 1942 err: true, 1943 }} 1944 1945 for i, test := range tests { 1946 1947 name := fmt.Sprintf("%d", i) 1948 if test.begin { 1949 name += " begin" 1950 } 1951 if test.reserve { 1952 name += " reserve" 1953 } 1954 t.Run(name, func(t *testing.T) { 1955 db, tsv := setupTabletServerTest(t, "") 1956 defer tsv.StopService() 1957 defer db.Close() 1958 db.AddQueryPattern(".*", &sqltypes.Result{}) 1959 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 1960 1961 var transactionID, reservedID int64 1962 1963 switch { 1964 case test.begin && test.reserve: 1965 state, _, err := tsv.ReserveBeginExecute(ctx, &target, []string{"select 1212"}, nil, "select 42", nil, &querypb.ExecuteOptions{}) 1966 require.NoError(t, err) 1967 transactionID = state.TransactionID 1968 reservedID = state.ReservedID 1969 require.NotEqual(t, int64(0), transactionID) 1970 require.NotEqual(t, int64(0), reservedID) 1971 case test.begin: 1972 state, _, err := tsv.BeginExecute(ctx, &target, nil, "select 42", nil, 0, &querypb.ExecuteOptions{}) 1973 require.NoError(t, err) 1974 transactionID = state.TransactionID 1975 require.NotEqual(t, int64(0), transactionID) 1976 case test.reserve: 1977 state, _, err := tsv.ReserveExecute(ctx, &target, nil, "select 42", nil, 0, &querypb.ExecuteOptions{}) 1978 require.NoError(t, err) 1979 reservedID = state.ReservedID 1980 require.NotEqual(t, int64(0), reservedID) 1981 } 1982 1983 db.ResetQueryLog() 1984 1985 err := tsv.Release(ctx, &target, transactionID, reservedID) 1986 if test.err { 1987 require.Error(t, err) 1988 } else { 1989 require.NoError(t, err) 1990 } 1991 assert.Contains(t, db.QueryLog(), strings.Join(test.expectedQueries, ";"), "expected queries to run") 1992 }) 1993 } 1994 } 1995 1996 func TestReserveStats(t *testing.T) { 1997 db, tsv := setupTabletServerTest(t, "") 1998 defer tsv.StopService() 1999 defer db.Close() 2000 2001 target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY} 2002 2003 callerID := &querypb.VTGateCallerID{ 2004 Username: "test", 2005 } 2006 ctx := callerid.NewContext(context.Background(), nil, callerID) 2007 2008 // Starts reserved connection and transaction 2009 rbeState, _, err := tsv.ReserveBeginExecute(ctx, &target, nil, nil, "select 42", nil, &querypb.ExecuteOptions{}) 2010 require.NoError(t, err) 2011 assert.EqualValues(t, 1, tsv.te.txPool.env.Stats().UserActiveReservedCount.Counts()["test"]) 2012 2013 // Starts reserved connection 2014 reState, _, err := tsv.ReserveExecute(ctx, &target, nil, "select 42", nil, 0, &querypb.ExecuteOptions{}) 2015 require.NoError(t, err) 2016 assert.EqualValues(t, 2, tsv.te.txPool.env.Stats().UserActiveReservedCount.Counts()["test"]) 2017 2018 // Use previous reserved connection to start transaction 2019 reBeState, _, err := tsv.BeginExecute(ctx, &target, nil, "select 42", nil, reState.ReservedID, &querypb.ExecuteOptions{}) 2020 require.NoError(t, err) 2021 assert.EqualValues(t, 2, tsv.te.txPool.env.Stats().UserActiveReservedCount.Counts()["test"]) 2022 2023 // Starts transaction. 2024 beState, _, err := tsv.BeginExecute(ctx, &target, nil, "select 42", nil, 0, &querypb.ExecuteOptions{}) 2025 require.NoError(t, err) 2026 assert.EqualValues(t, 2, tsv.te.txPool.env.Stats().UserActiveReservedCount.Counts()["test"]) 2027 2028 // Reserved the connection on previous transaction 2029 beReState, _, err := tsv.ReserveExecute(ctx, &target, nil, "select 42", nil, beState.TransactionID, &querypb.ExecuteOptions{}) 2030 require.NoError(t, err) 2031 assert.EqualValues(t, 3, tsv.te.txPool.env.Stats().UserActiveReservedCount.Counts()["test"]) 2032 2033 err = tsv.Release(ctx, &target, rbeState.TransactionID, rbeState.ReservedID) 2034 require.NoError(t, err) 2035 assert.EqualValues(t, 2, tsv.te.txPool.env.Stats().UserActiveReservedCount.Counts()["test"]) 2036 assert.EqualValues(t, 1, tsv.te.txPool.env.Stats().UserReservedCount.Counts()["test"]) 2037 2038 err = tsv.Release(ctx, &target, reBeState.TransactionID, reState.ReservedID) 2039 require.NoError(t, err) 2040 assert.EqualValues(t, 1, tsv.te.txPool.env.Stats().UserActiveReservedCount.Counts()["test"]) 2041 assert.EqualValues(t, 2, tsv.te.txPool.env.Stats().UserReservedCount.Counts()["test"]) 2042 2043 err = tsv.Release(ctx, &target, beState.TransactionID, beReState.ReservedID) 2044 require.NoError(t, err) 2045 assert.Zero(t, tsv.te.txPool.env.Stats().UserActiveReservedCount.Counts()["test"]) 2046 assert.EqualValues(t, 3, tsv.te.txPool.env.Stats().UserReservedCount.Counts()["test"]) 2047 assert.NotEmpty(t, tsv.te.txPool.env.Stats().UserReservedTimesNs.Counts()["test"]) 2048 } 2049 2050 func TestDatabaseNameReplaceByKeyspaceNameExecuteMethod(t *testing.T) { 2051 db, tsv := setupTabletServerTest(t, "keyspaceName") 2052 setDBName(db, tsv, "databaseInMysql") 2053 defer tsv.StopService() 2054 defer db.Close() 2055 2056 executeSQL := "select * from test_table limit 1000" 2057 executeSQLResult := &sqltypes.Result{ 2058 Fields: []*querypb.Field{ 2059 { 2060 Type: sqltypes.VarBinary, 2061 Database: "databaseInMysql", 2062 }, 2063 }, 2064 RowsAffected: 1, 2065 Rows: [][]sqltypes.Value{ 2066 {sqltypes.NewVarBinary("row01")}, 2067 }, 2068 } 2069 db.AddQuery(executeSQL, executeSQLResult) 2070 target := tsv.sm.target 2071 2072 // Testing Execute Method 2073 state, err := tsv.Begin(ctx, target, nil) 2074 require.NoError(t, err) 2075 res, err := tsv.Execute(ctx, target, executeSQL, nil, state.TransactionID, 0, &querypb.ExecuteOptions{ 2076 IncludedFields: querypb.ExecuteOptions_ALL, 2077 }) 2078 require.NoError(t, err) 2079 for _, field := range res.Fields { 2080 require.Equal(t, "keyspaceName", field.Database) 2081 } 2082 _, err = tsv.Commit(ctx, target, state.TransactionID) 2083 require.NoError(t, err) 2084 } 2085 2086 func TestDatabaseNameReplaceByKeyspaceNameStreamExecuteMethod(t *testing.T) { 2087 db, tsv := setupTabletServerTest(t, "keyspaceName") 2088 setDBName(db, tsv, "databaseInMysql") 2089 defer tsv.StopService() 2090 defer db.Close() 2091 2092 executeSQL := "select * from test_table limit 1000" 2093 executeSQLResult := &sqltypes.Result{ 2094 Fields: []*querypb.Field{ 2095 { 2096 Type: sqltypes.VarBinary, 2097 Database: "databaseInMysql", 2098 }, 2099 }, 2100 RowsAffected: 1, 2101 Rows: [][]sqltypes.Value{ 2102 {sqltypes.NewVarBinary("row01")}, 2103 }, 2104 } 2105 db.AddQuery(executeSQL, executeSQLResult) 2106 target := tsv.sm.target 2107 2108 // Testing StreamExecute Method 2109 callback := func(res *sqltypes.Result) error { 2110 for _, field := range res.Fields { 2111 if field.Database != "" { 2112 require.Equal(t, "keyspaceName", field.Database) 2113 } 2114 } 2115 return nil 2116 } 2117 err := tsv.StreamExecute(ctx, target, executeSQL, nil, 0, 0, &querypb.ExecuteOptions{ 2118 IncludedFields: querypb.ExecuteOptions_ALL, 2119 }, callback) 2120 require.NoError(t, err) 2121 } 2122 2123 func TestDatabaseNameReplaceByKeyspaceNameBeginExecuteMethod(t *testing.T) { 2124 db, tsv := setupTabletServerTest(t, "keyspaceName") 2125 setDBName(db, tsv, "databaseInMysql") 2126 defer tsv.StopService() 2127 defer db.Close() 2128 2129 executeSQL := "select * from test_table limit 1000" 2130 executeSQLResult := &sqltypes.Result{ 2131 Fields: []*querypb.Field{ 2132 { 2133 Type: sqltypes.VarBinary, 2134 Database: "databaseInMysql", 2135 }, 2136 }, 2137 RowsAffected: 1, 2138 Rows: [][]sqltypes.Value{ 2139 {sqltypes.NewVarBinary("row01")}, 2140 }, 2141 } 2142 db.AddQuery(executeSQL, executeSQLResult) 2143 target := tsv.sm.target 2144 2145 // Test BeginExecute Method 2146 state, res, err := tsv.BeginExecute(ctx, target, nil, executeSQL, nil, 0, &querypb.ExecuteOptions{ 2147 IncludedFields: querypb.ExecuteOptions_ALL, 2148 }) 2149 require.NoError(t, err) 2150 for _, field := range res.Fields { 2151 require.Equal(t, "keyspaceName", field.Database) 2152 } 2153 _, err = tsv.Commit(ctx, target, state.TransactionID) 2154 require.NoError(t, err) 2155 } 2156 2157 func setDBName(db *fakesqldb.DB, tsv *TabletServer, s string) { 2158 tsv.config.DB.DBName = "databaseInMysql" 2159 db.SetName("databaseInMysql") 2160 } 2161 2162 func TestDatabaseNameReplaceByKeyspaceNameReserveExecuteMethod(t *testing.T) { 2163 db, tsv := setupTabletServerTest(t, "keyspaceName") 2164 setDBName(db, tsv, "databaseInMysql") 2165 defer tsv.StopService() 2166 defer db.Close() 2167 2168 executeSQL := "select * from test_table limit 1000" 2169 executeSQLResult := &sqltypes.Result{ 2170 Fields: []*querypb.Field{ 2171 { 2172 Type: sqltypes.VarBinary, 2173 Database: "databaseInMysql", 2174 }, 2175 }, 2176 RowsAffected: 1, 2177 Rows: [][]sqltypes.Value{ 2178 {sqltypes.NewVarBinary("row01")}, 2179 }, 2180 } 2181 db.AddQuery(executeSQL, executeSQLResult) 2182 target := tsv.sm.target 2183 2184 // Test ReserveExecute 2185 state, res, err := tsv.ReserveExecute(ctx, target, nil, executeSQL, nil, 0, &querypb.ExecuteOptions{ 2186 IncludedFields: querypb.ExecuteOptions_ALL, 2187 }) 2188 require.NoError(t, err) 2189 for _, field := range res.Fields { 2190 require.Equal(t, "keyspaceName", field.Database) 2191 } 2192 err = tsv.Release(ctx, target, 0, state.ReservedID) 2193 require.NoError(t, err) 2194 } 2195 2196 func TestDatabaseNameReplaceByKeyspaceNameReserveBeginExecuteMethod(t *testing.T) { 2197 db, tsv := setupTabletServerTest(t, "keyspaceName") 2198 setDBName(db, tsv, "databaseInMysql") 2199 defer tsv.StopService() 2200 defer db.Close() 2201 2202 executeSQL := "select * from test_table limit 1000" 2203 executeSQLResult := &sqltypes.Result{ 2204 Fields: []*querypb.Field{ 2205 { 2206 Type: sqltypes.VarBinary, 2207 Database: "databaseInMysql", 2208 }, 2209 }, 2210 RowsAffected: 1, 2211 Rows: [][]sqltypes.Value{ 2212 {sqltypes.NewVarBinary("row01")}, 2213 }, 2214 } 2215 db.AddQuery(executeSQL, executeSQLResult) 2216 target := tsv.sm.target 2217 2218 // Test for ReserveBeginExecute 2219 state, res, err := tsv.ReserveBeginExecute(ctx, target, nil, nil, executeSQL, nil, &querypb.ExecuteOptions{ 2220 IncludedFields: querypb.ExecuteOptions_ALL, 2221 }) 2222 require.NoError(t, err) 2223 for _, field := range res.Fields { 2224 require.Equal(t, "keyspaceName", field.Database) 2225 } 2226 err = tsv.Release(ctx, target, state.TransactionID, state.ReservedID) 2227 require.NoError(t, err) 2228 } 2229 2230 func setupTabletServerTest(t *testing.T, keyspaceName string) (*fakesqldb.DB, *TabletServer) { 2231 config := tabletenv.NewDefaultConfig() 2232 return setupTabletServerTestCustom(t, config, keyspaceName) 2233 } 2234 2235 func setupTabletServerTestCustom(t *testing.T, config *tabletenv.TabletConfig, keyspaceName string) (*fakesqldb.DB, *TabletServer) { 2236 db := setupFakeDB(t) 2237 sidecardb.AddSchemaInitQueries(db, true) 2238 tsv := NewTabletServer("TabletServerTest", config, memorytopo.NewServer(""), &topodatapb.TabletAlias{}) 2239 require.Equal(t, StateNotConnected, tsv.sm.State()) 2240 dbcfgs := newDBConfigs(db) 2241 target := &querypb.Target{ 2242 Keyspace: keyspaceName, 2243 TabletType: topodatapb.TabletType_PRIMARY, 2244 } 2245 err := tsv.StartService(target, dbcfgs, nil /* mysqld */) 2246 require.NoError(t, err) 2247 return db, tsv 2248 } 2249 2250 func setupFakeDB(t *testing.T) *fakesqldb.DB { 2251 db := fakesqldb.New(t) 2252 addTabletServerSupportedQueries(db) 2253 db.AddQueryPattern(baseShowTablesPattern, &sqltypes.Result{ 2254 Fields: mysql.BaseShowTablesFields, 2255 Rows: [][]sqltypes.Value{ 2256 mysql.BaseShowTablesRow("test_table", false, ""), 2257 mysql.BaseShowTablesRow("msg", false, "vitess_message,vt_ack_wait=30,vt_purge_after=120,vt_batch_size=1,vt_cache_size=10,vt_poller_interval=30"), 2258 }, 2259 }) 2260 db.AddQuery("show status like 'Innodb_rows_read'", sqltypes.MakeTestResult(sqltypes.MakeTestFields( 2261 "Variable_name|Value", 2262 "varchar|int64"), 2263 "Innodb_rows_read|0", 2264 )) 2265 2266 return db 2267 } 2268 2269 func addTabletServerSupportedQueries(db *fakesqldb.DB) { 2270 queryResultMap := map[string]*sqltypes.Result{ 2271 // Queries for how row protection test (txserializer). 2272 "update test_table set name_string = 'tx1' where pk = 1 and `name` = 1 limit 10001": { 2273 RowsAffected: 1, 2274 }, 2275 "update test_table set name_string = 'tx2' where pk = 1 and `name` = 1 limit 10001": { 2276 RowsAffected: 1, 2277 }, 2278 "update test_table set name_string = 'tx3' where pk = 1 and `name` = 1 limit 10001": { 2279 RowsAffected: 1, 2280 }, 2281 // tx3, but with different primary key. 2282 "update test_table set name_string = 'tx3' where pk = 2 and `name` = 1 limit 10001": { 2283 RowsAffected: 1, 2284 }, 2285 // queries for schema info 2286 "select unix_timestamp()": { 2287 Fields: []*querypb.Field{{ 2288 Type: sqltypes.Uint64, 2289 }}, 2290 Rows: [][]sqltypes.Value{ 2291 {sqltypes.NewVarBinary("1427325875")}, 2292 }, 2293 }, 2294 "select @@global.sql_mode": { 2295 Fields: []*querypb.Field{{ 2296 Type: sqltypes.VarChar, 2297 }}, 2298 Rows: [][]sqltypes.Value{ 2299 {sqltypes.NewVarBinary("STRICT_TRANS_TABLES")}, 2300 }, 2301 }, 2302 "select @@autocommit": { 2303 Fields: []*querypb.Field{{ 2304 Type: sqltypes.Uint64, 2305 }}, 2306 Rows: [][]sqltypes.Value{ 2307 {sqltypes.NewVarBinary("1")}, 2308 }, 2309 }, 2310 "select @@sql_auto_is_null": { 2311 Fields: []*querypb.Field{{ 2312 Type: sqltypes.Uint64, 2313 }}, 2314 Rows: [][]sqltypes.Value{ 2315 {sqltypes.NewVarBinary("0")}, 2316 }, 2317 }, 2318 mysql.BaseShowPrimary: { 2319 Fields: mysql.ShowPrimaryFields, 2320 Rows: [][]sqltypes.Value{ 2321 mysql.ShowPrimaryRow("test_table", "pk"), 2322 mysql.ShowPrimaryRow("msg", "id"), 2323 }, 2324 }, 2325 // queries for TestReserve* 2326 "select 42 from dual where 1 != 1": { 2327 Fields: []*querypb.Field{{ 2328 Name: "42", 2329 Type: sqltypes.Int32, 2330 }}, 2331 }, 2332 "select 42 from dual limit 10001": { 2333 Fields: []*querypb.Field{{ 2334 Name: "42", 2335 Type: sqltypes.Int32, 2336 }}, 2337 }, 2338 "select 43": { 2339 Fields: []*querypb.Field{{ 2340 Name: "43", 2341 Type: sqltypes.Int32, 2342 }}, 2343 }, 2344 "select * from test_table where 1 != 1": { 2345 Fields: []*querypb.Field{{ 2346 Name: "pk", 2347 Type: sqltypes.Int32, 2348 }, { 2349 Name: "name", 2350 Type: sqltypes.Int32, 2351 }, { 2352 Name: "addr", 2353 Type: sqltypes.Int32, 2354 }, { 2355 Name: "name_string", 2356 Type: sqltypes.VarChar, 2357 }}, 2358 }, 2359 "select * from msg where 1 != 1": { 2360 Fields: []*querypb.Field{{ 2361 Name: "id", 2362 Type: sqltypes.Int64, 2363 }, { 2364 Name: "priority", 2365 Type: sqltypes.Int64, 2366 }, { 2367 Name: "time_next", 2368 Type: sqltypes.Int64, 2369 }, { 2370 Name: "epoch", 2371 Type: sqltypes.Int64, 2372 }, { 2373 Name: "time_acked", 2374 Type: sqltypes.Int64, 2375 }, { 2376 Name: "message", 2377 Type: sqltypes.Int64, 2378 }}, 2379 }, 2380 "begin": {}, 2381 "commit": {}, 2382 "rollback": {}, 2383 fmt.Sprintf(sqlReadAllRedo, "_vt", "_vt"): {}, 2384 } 2385 sidecardb.AddSchemaInitQueries(db, true) 2386 for query, result := range queryResultMap { 2387 db.AddQuery(query, result) 2388 } 2389 db.MockQueriesForTable("test_table", &sqltypes.Result{ 2390 Fields: []*querypb.Field{{ 2391 Name: "pk", 2392 Type: sqltypes.Int32, 2393 }, { 2394 Name: "name", 2395 Type: sqltypes.Int32, 2396 }, { 2397 Name: "addr", 2398 Type: sqltypes.Int32, 2399 }, { 2400 Name: "name_string", 2401 Type: sqltypes.VarChar, 2402 }}, 2403 }) 2404 db.MockQueriesForTable("msg", &sqltypes.Result{ 2405 Fields: []*querypb.Field{{ 2406 Name: "id", 2407 Type: sqltypes.Int64, 2408 }, { 2409 Name: "priority", 2410 Type: sqltypes.Int64, 2411 }, { 2412 Name: "time_next", 2413 Type: sqltypes.Int64, 2414 }, { 2415 Name: "epoch", 2416 Type: sqltypes.Int64, 2417 }, { 2418 Name: "time_acked", 2419 Type: sqltypes.Int64, 2420 }, { 2421 Name: "message", 2422 Type: sqltypes.Int64, 2423 }}, 2424 }) 2425 } 2426 2427 func init() { 2428 rand.Seed(time.Now().UnixNano()) 2429 }