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  }