vitess.io/vitess@v0.16.2/go/vt/vttablet/tabletserver/tx_executor_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  	"reflect"
    24  	"testing"
    25  	"time"
    26  
    27  	"vitess.io/vitess/go/vt/vttablet/tabletserver/tx"
    28  
    29  	"github.com/stretchr/testify/require"
    30  	"google.golang.org/protobuf/proto"
    31  
    32  	"vitess.io/vitess/go/mysql/fakesqldb"
    33  	"vitess.io/vitess/go/sqltypes"
    34  	"vitess.io/vitess/go/vt/vtgate/fakerpcvtgateconn"
    35  	"vitess.io/vitess/go/vt/vtgate/vtgateconn"
    36  	"vitess.io/vitess/go/vt/vttablet/tabletserver/tabletenv"
    37  
    38  	querypb "vitess.io/vitess/go/vt/proto/query"
    39  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    40  )
    41  
    42  func TestTxExecutorEmptyPrepare(t *testing.T) {
    43  	txe, tsv, db := newTestTxExecutor(t)
    44  	defer db.Close()
    45  	defer tsv.StopService()
    46  	txid := newTransaction(tsv, nil)
    47  	err := txe.Prepare(txid, "aa")
    48  	require.NoError(t, err)
    49  	// Nothing should be prepared.
    50  	require.Empty(t, txe.te.preparedPool.conns, "txe.te.preparedPool.conns")
    51  }
    52  
    53  func TestTxExecutorPrepare(t *testing.T) {
    54  	txe, tsv, db := newTestTxExecutor(t)
    55  	defer db.Close()
    56  	defer tsv.StopService()
    57  	txid := newTxForPrep(tsv)
    58  	err := txe.Prepare(txid, "aa")
    59  	require.NoError(t, err)
    60  	err = txe.RollbackPrepared("aa", 1)
    61  	require.NoError(t, err)
    62  	// A retry should still succeed.
    63  	err = txe.RollbackPrepared("aa", 1)
    64  	require.NoError(t, err)
    65  	// A retry  with no original id should also succeed.
    66  	err = txe.RollbackPrepared("aa", 0)
    67  	require.NoError(t, err)
    68  }
    69  
    70  func TestTxExecutorPrepareNotInTx(t *testing.T) {
    71  	txe, tsv, db := newTestTxExecutor(t)
    72  	defer db.Close()
    73  	defer tsv.StopService()
    74  	err := txe.Prepare(0, "aa")
    75  	require.EqualError(t, err, "transaction 0: not found")
    76  }
    77  
    78  func TestTxExecutorPreparePoolFail(t *testing.T) {
    79  	txe, tsv, db := newTestTxExecutor(t)
    80  	defer db.Close()
    81  	defer tsv.StopService()
    82  	txid1 := newTxForPrep(tsv)
    83  	txid2 := newTxForPrep(tsv)
    84  	err := txe.Prepare(txid1, "aa")
    85  	require.NoError(t, err)
    86  	defer txe.RollbackPrepared("aa", 0)
    87  	err = txe.Prepare(txid2, "bb")
    88  	require.Error(t, err)
    89  	require.Contains(t, err.Error(), "prepared transactions exceeded limit")
    90  }
    91  
    92  func TestTxExecutorPrepareRedoBeginFail(t *testing.T) {
    93  	txe, tsv, db := newTestTxExecutor(t)
    94  	defer db.Close()
    95  	defer tsv.StopService()
    96  	txid := newTxForPrep(tsv)
    97  	db.AddRejectedQuery("begin", errors.New("begin fail"))
    98  	err := txe.Prepare(txid, "aa")
    99  	defer txe.RollbackPrepared("aa", 0)
   100  	require.Error(t, err)
   101  	require.Contains(t, err.Error(), "begin fail")
   102  }
   103  
   104  func TestTxExecutorPrepareRedoFail(t *testing.T) {
   105  	txe, tsv, db := newTestTxExecutor(t)
   106  	defer db.Close()
   107  	defer tsv.StopService()
   108  	txid := newTxForPrep(tsv)
   109  	err := txe.Prepare(txid, "bb")
   110  	defer txe.RollbackPrepared("bb", 0)
   111  	require.Error(t, err)
   112  	require.Contains(t, err.Error(), "is not supported")
   113  }
   114  
   115  func TestTxExecutorPrepareRedoCommitFail(t *testing.T) {
   116  	txe, tsv, db := newTestTxExecutor(t)
   117  	defer db.Close()
   118  	defer tsv.StopService()
   119  	txid := newTxForPrep(tsv)
   120  	db.AddRejectedQuery("commit", errors.New("commit fail"))
   121  	err := txe.Prepare(txid, "aa")
   122  	defer txe.RollbackPrepared("aa", 0)
   123  	require.Error(t, err)
   124  	require.Contains(t, err.Error(), "commit fail")
   125  }
   126  
   127  func TestTxExecutorCommit(t *testing.T) {
   128  	txe, tsv, db := newTestTxExecutor(t)
   129  	defer db.Close()
   130  	defer tsv.StopService()
   131  	txid := newTxForPrep(tsv)
   132  	err := txe.Prepare(txid, "aa")
   133  	require.NoError(t, err)
   134  	err = txe.CommitPrepared("aa")
   135  	require.NoError(t, err)
   136  	// Committing an absent transaction should succeed.
   137  	err = txe.CommitPrepared("bb")
   138  	require.NoError(t, err)
   139  }
   140  
   141  func TestTxExecutorCommitRedoFail(t *testing.T) {
   142  	txe, tsv, db := newTestTxExecutor(t)
   143  	defer db.Close()
   144  	defer tsv.StopService()
   145  	txid := newTxForPrep(tsv)
   146  	// Allow all additions to redo logs to succeed
   147  	db.AddQueryPattern("insert into _vt\\.redo_state.*", &sqltypes.Result{})
   148  	err := txe.Prepare(txid, "bb")
   149  	require.NoError(t, err)
   150  	defer txe.RollbackPrepared("bb", 0)
   151  	db.AddQuery("update _vt.redo_state set state = 'Failed' where dtid = 'bb'", &sqltypes.Result{})
   152  	err = txe.CommitPrepared("bb")
   153  	require.Error(t, err)
   154  	require.Contains(t, err.Error(), "is not supported")
   155  	// A retry should fail differently.
   156  	err = txe.CommitPrepared("bb")
   157  	require.Error(t, err)
   158  	require.Contains(t, err.Error(), "cannot commit dtid bb, state: failed")
   159  }
   160  
   161  func TestTxExecutorCommitRedoCommitFail(t *testing.T) {
   162  	txe, tsv, db := newTestTxExecutor(t)
   163  	defer db.Close()
   164  	defer tsv.StopService()
   165  	txid := newTxForPrep(tsv)
   166  	err := txe.Prepare(txid, "aa")
   167  	require.NoError(t, err)
   168  	defer txe.RollbackPrepared("aa", 0)
   169  	db.AddRejectedQuery("commit", errors.New("commit fail"))
   170  	err = txe.CommitPrepared("aa")
   171  	require.Error(t, err)
   172  	require.Contains(t, err.Error(), "commit fail")
   173  }
   174  
   175  func TestTxExecutorRollbackBeginFail(t *testing.T) {
   176  	txe, tsv, db := newTestTxExecutor(t)
   177  	defer db.Close()
   178  	defer tsv.StopService()
   179  	txid := newTxForPrep(tsv)
   180  	err := txe.Prepare(txid, "aa")
   181  	require.NoError(t, err)
   182  	db.AddRejectedQuery("begin", errors.New("begin fail"))
   183  	err = txe.RollbackPrepared("aa", txid)
   184  	require.Error(t, err)
   185  	require.Contains(t, err.Error(), "begin fail")
   186  }
   187  
   188  func TestTxExecutorRollbackRedoFail(t *testing.T) {
   189  	txe, tsv, db := newTestTxExecutor(t)
   190  	defer db.Close()
   191  	defer tsv.StopService()
   192  	txid := newTxForPrep(tsv)
   193  	// Allow all additions to redo logs to succeed
   194  	db.AddQueryPattern("insert into _vt\\.redo_state.*", &sqltypes.Result{})
   195  	err := txe.Prepare(txid, "bb")
   196  	require.NoError(t, err)
   197  	err = txe.RollbackPrepared("bb", txid)
   198  	require.Error(t, err)
   199  	require.Contains(t, err.Error(), "is not supported")
   200  }
   201  
   202  func TestExecutorCreateTransaction(t *testing.T) {
   203  	txe, tsv, db := newTestTxExecutor(t)
   204  	defer db.Close()
   205  	defer tsv.StopService()
   206  
   207  	db.AddQueryPattern(fmt.Sprintf("insert into _vt\\.dt_state\\(dtid, state, time_created\\) values \\('aa', %d,.*", int(querypb.TransactionState_PREPARE)), &sqltypes.Result{})
   208  	db.AddQueryPattern("insert into _vt\\.dt_participant\\(dtid, id, keyspace, shard\\) values \\('aa', 1,.*", &sqltypes.Result{})
   209  	err := txe.CreateTransaction("aa", []*querypb.Target{{
   210  		Keyspace: "t1",
   211  		Shard:    "0",
   212  	}})
   213  	require.NoError(t, err)
   214  }
   215  
   216  func TestExecutorStartCommit(t *testing.T) {
   217  	txe, tsv, db := newTestTxExecutor(t)
   218  	defer db.Close()
   219  	defer tsv.StopService()
   220  
   221  	commitTransition := fmt.Sprintf("update _vt.dt_state set state = %d where dtid = 'aa' and state = %d", int(querypb.TransactionState_COMMIT), int(querypb.TransactionState_PREPARE))
   222  	db.AddQuery(commitTransition, &sqltypes.Result{RowsAffected: 1})
   223  	txid := newTxForPrep(tsv)
   224  	err := txe.StartCommit(txid, "aa")
   225  	require.NoError(t, err)
   226  
   227  	db.AddQuery(commitTransition, &sqltypes.Result{})
   228  	txid = newTxForPrep(tsv)
   229  	err = txe.StartCommit(txid, "aa")
   230  	require.Error(t, err)
   231  	require.Contains(t, err.Error(), "could not transition to COMMIT: aa")
   232  }
   233  
   234  func TestExecutorSetRollback(t *testing.T) {
   235  	txe, tsv, db := newTestTxExecutor(t)
   236  	defer db.Close()
   237  	defer tsv.StopService()
   238  
   239  	rollbackTransition := fmt.Sprintf("update _vt.dt_state set state = %d where dtid = 'aa' and state = %d", int(querypb.TransactionState_ROLLBACK), int(querypb.TransactionState_PREPARE))
   240  	db.AddQuery(rollbackTransition, &sqltypes.Result{RowsAffected: 1})
   241  	txid := newTxForPrep(tsv)
   242  	err := txe.SetRollback("aa", txid)
   243  	require.NoError(t, err)
   244  
   245  	db.AddQuery(rollbackTransition, &sqltypes.Result{})
   246  	txid = newTxForPrep(tsv)
   247  	err = txe.SetRollback("aa", txid)
   248  	require.Error(t, err)
   249  	require.Contains(t, err.Error(), "could not transition to ROLLBACK: aa")
   250  }
   251  
   252  func TestExecutorConcludeTransaction(t *testing.T) {
   253  	txe, tsv, db := newTestTxExecutor(t)
   254  	defer db.Close()
   255  	defer tsv.StopService()
   256  
   257  	db.AddQuery("delete from _vt.dt_state where dtid = 'aa'", &sqltypes.Result{})
   258  	db.AddQuery("delete from _vt.dt_participant where dtid = 'aa'", &sqltypes.Result{})
   259  	err := txe.ConcludeTransaction("aa")
   260  	require.NoError(t, err)
   261  }
   262  
   263  func TestExecutorReadTransaction(t *testing.T) {
   264  	txe, tsv, db := newTestTxExecutor(t)
   265  	defer db.Close()
   266  	defer tsv.StopService()
   267  
   268  	db.AddQuery("select dtid, state, time_created from _vt.dt_state where dtid = 'aa'", &sqltypes.Result{})
   269  	got, err := txe.ReadTransaction("aa")
   270  	require.NoError(t, err)
   271  	want := &querypb.TransactionMetadata{}
   272  	if !proto.Equal(got, want) {
   273  		t.Errorf("ReadTransaction: %v, want %v", got, want)
   274  	}
   275  
   276  	txResult := &sqltypes.Result{
   277  		Fields: []*querypb.Field{
   278  			{Type: sqltypes.VarChar},
   279  			{Type: sqltypes.Int64},
   280  			{Type: sqltypes.Int64},
   281  		},
   282  		Rows: [][]sqltypes.Value{{
   283  			sqltypes.NewVarBinary("aa"),
   284  			sqltypes.NewInt64(int64(querypb.TransactionState_PREPARE)),
   285  			sqltypes.NewVarBinary("1"),
   286  		}},
   287  	}
   288  	db.AddQuery("select dtid, state, time_created from _vt.dt_state where dtid = 'aa'", txResult)
   289  	db.AddQuery("select keyspace, shard from _vt.dt_participant where dtid = 'aa'", &sqltypes.Result{
   290  		Fields: []*querypb.Field{
   291  			{Type: sqltypes.VarChar},
   292  			{Type: sqltypes.VarChar},
   293  		},
   294  		Rows: [][]sqltypes.Value{{
   295  			sqltypes.NewVarBinary("test1"),
   296  			sqltypes.NewVarBinary("0"),
   297  		}, {
   298  			sqltypes.NewVarBinary("test2"),
   299  			sqltypes.NewVarBinary("1"),
   300  		}},
   301  	})
   302  	got, err = txe.ReadTransaction("aa")
   303  	require.NoError(t, err)
   304  	want = &querypb.TransactionMetadata{
   305  		Dtid:        "aa",
   306  		State:       querypb.TransactionState_PREPARE,
   307  		TimeCreated: 1,
   308  		Participants: []*querypb.Target{{
   309  			Keyspace:   "test1",
   310  			Shard:      "0",
   311  			TabletType: topodatapb.TabletType_PRIMARY,
   312  		}, {
   313  			Keyspace:   "test2",
   314  			Shard:      "1",
   315  			TabletType: topodatapb.TabletType_PRIMARY,
   316  		}},
   317  	}
   318  	if !proto.Equal(got, want) {
   319  		t.Errorf("ReadTransaction: %v, want %v", got, want)
   320  	}
   321  
   322  	txResult = &sqltypes.Result{
   323  		Fields: []*querypb.Field{
   324  			{Type: sqltypes.VarChar},
   325  			{Type: sqltypes.Int64},
   326  			{Type: sqltypes.Int64},
   327  		},
   328  		Rows: [][]sqltypes.Value{{
   329  			sqltypes.NewVarBinary("aa"),
   330  			sqltypes.NewInt64(int64(querypb.TransactionState_COMMIT)),
   331  			sqltypes.NewVarBinary("1"),
   332  		}},
   333  	}
   334  	db.AddQuery("select dtid, state, time_created from _vt.dt_state where dtid = 'aa'", txResult)
   335  	want.State = querypb.TransactionState_COMMIT
   336  	got, err = txe.ReadTransaction("aa")
   337  	require.NoError(t, err)
   338  	if !proto.Equal(got, want) {
   339  		t.Errorf("ReadTransaction: %v, want %v", got, want)
   340  	}
   341  
   342  	txResult = &sqltypes.Result{
   343  		Fields: []*querypb.Field{
   344  			{Type: sqltypes.VarChar},
   345  			{Type: sqltypes.Int64},
   346  			{Type: sqltypes.Int64},
   347  		},
   348  		Rows: [][]sqltypes.Value{{
   349  			sqltypes.NewVarBinary("aa"),
   350  			sqltypes.NewInt64(int64(querypb.TransactionState_ROLLBACK)),
   351  			sqltypes.NewVarBinary("1"),
   352  		}},
   353  	}
   354  	db.AddQuery("select dtid, state, time_created from _vt.dt_state where dtid = 'aa'", txResult)
   355  	want.State = querypb.TransactionState_ROLLBACK
   356  	got, err = txe.ReadTransaction("aa")
   357  	require.NoError(t, err)
   358  	if !proto.Equal(got, want) {
   359  		t.Errorf("ReadTransaction: %v, want %v", got, want)
   360  	}
   361  }
   362  
   363  func TestExecutorReadAllTransactions(t *testing.T) {
   364  	txe, tsv, db := newTestTxExecutor(t)
   365  	defer db.Close()
   366  	defer tsv.StopService()
   367  
   368  	db.AddQuery(txe.te.twoPC.readAllTransactions, &sqltypes.Result{
   369  		Fields: []*querypb.Field{
   370  			{Type: sqltypes.VarChar},
   371  			{Type: sqltypes.Int64},
   372  			{Type: sqltypes.Int64},
   373  			{Type: sqltypes.VarChar},
   374  			{Type: sqltypes.VarChar},
   375  		},
   376  		Rows: [][]sqltypes.Value{{
   377  			sqltypes.NewVarBinary("dtid0"),
   378  			sqltypes.NewInt64(int64(querypb.TransactionState_PREPARE)),
   379  			sqltypes.NewVarBinary("1"),
   380  			sqltypes.NewVarBinary("ks01"),
   381  			sqltypes.NewVarBinary("shard01"),
   382  		}},
   383  	})
   384  	got, _, _, err := txe.ReadTwopcInflight()
   385  	require.NoError(t, err)
   386  	want := []*tx.DistributedTx{{
   387  		Dtid:    "dtid0",
   388  		State:   "PREPARE",
   389  		Created: time.Unix(0, 1),
   390  		Participants: []querypb.Target{{
   391  			Keyspace: "ks01",
   392  			Shard:    "shard01",
   393  		}},
   394  	}}
   395  	if !reflect.DeepEqual(got, want) {
   396  		t.Errorf("ReadAllTransactions:\n%s, want\n%s", jsonStr(got), jsonStr(want))
   397  	}
   398  }
   399  
   400  // These vars and types are used only for TestExecutorResolveTransaction
   401  var dtidCh = make(chan string)
   402  
   403  type FakeVTGateConn struct {
   404  	fakerpcvtgateconn.FakeVTGateConn
   405  }
   406  
   407  func (conn *FakeVTGateConn) ResolveTransaction(ctx context.Context, dtid string) error {
   408  	dtidCh <- dtid
   409  	return nil
   410  }
   411  
   412  func TestExecutorResolveTransaction(t *testing.T) {
   413  	protocol := "resolveTest"
   414  	oldValue := vtgateconn.GetVTGateProtocol()
   415  	vtgateconn.SetVTGateProtocol(protocol)
   416  	defer func() {
   417  		vtgateconn.SetVTGateProtocol(oldValue)
   418  	}()
   419  	vtgateconn.RegisterDialer(protocol, func(context.Context, string) (vtgateconn.Impl, error) {
   420  		return &FakeVTGateConn{
   421  			FakeVTGateConn: fakerpcvtgateconn.FakeVTGateConn{},
   422  		}, nil
   423  	})
   424  	_, tsv, db := newShortAgeExecutor(t)
   425  	defer db.Close()
   426  	defer tsv.StopService()
   427  	want := "aa"
   428  	db.AddQueryPattern(
   429  		"select dtid, time_created from _vt\\.dt_state where time_created.*",
   430  		&sqltypes.Result{
   431  			Fields: []*querypb.Field{
   432  				{Type: sqltypes.VarChar},
   433  				{Type: sqltypes.Int64},
   434  			},
   435  			Rows: [][]sqltypes.Value{{
   436  				sqltypes.NewVarBinary(want),
   437  				sqltypes.NewVarBinary("1"),
   438  			}},
   439  		})
   440  	got := <-dtidCh
   441  	if got != want {
   442  		t.Errorf("ResolveTransaction: %s, want %s", got, want)
   443  	}
   444  }
   445  
   446  func TestNoTwopc(t *testing.T) {
   447  	txe, tsv, db := newNoTwopcExecutor(t)
   448  	defer db.Close()
   449  	defer tsv.StopService()
   450  
   451  	testcases := []struct {
   452  		desc string
   453  		fun  func() error
   454  	}{{
   455  		desc: "Prepare",
   456  		fun:  func() error { return txe.Prepare(1, "aa") },
   457  	}, {
   458  		desc: "CommitPrepared",
   459  		fun:  func() error { return txe.CommitPrepared("aa") },
   460  	}, {
   461  		desc: "RollbackPrepared",
   462  		fun:  func() error { return txe.RollbackPrepared("aa", 1) },
   463  	}, {
   464  		desc: "CreateTransaction",
   465  		fun:  func() error { return txe.CreateTransaction("aa", nil) },
   466  	}, {
   467  		desc: "StartCommit",
   468  		fun:  func() error { return txe.StartCommit(1, "aa") },
   469  	}, {
   470  		desc: "SetRollback",
   471  		fun:  func() error { return txe.SetRollback("aa", 1) },
   472  	}, {
   473  		desc: "ConcludeTransaction",
   474  		fun:  func() error { return txe.ConcludeTransaction("aa") },
   475  	}, {
   476  		desc: "ReadTransaction",
   477  		fun: func() error {
   478  			_, err := txe.ReadTransaction("aa")
   479  			return err
   480  		},
   481  	}, {
   482  		desc: "ReadAllTransactions",
   483  		fun: func() error {
   484  			_, _, _, err := txe.ReadTwopcInflight()
   485  			return err
   486  		},
   487  	}}
   488  
   489  	want := "2pc is not enabled"
   490  	for _, tc := range testcases {
   491  		err := tc.fun()
   492  		require.EqualError(t, err, want)
   493  	}
   494  }
   495  
   496  func newTestTxExecutor(t *testing.T) (txe *TxExecutor, tsv *TabletServer, db *fakesqldb.DB) {
   497  	db = setUpQueryExecutorTest(t)
   498  	logStats := tabletenv.NewLogStats(ctx, "TestTxExecutor")
   499  	tsv = newTestTabletServer(ctx, smallTxPool, db)
   500  	db.AddQueryPattern("insert into _vt\\.redo_state\\(dtid, state, time_created\\) values \\('aa', 1,.*", &sqltypes.Result{})
   501  	db.AddQueryPattern("insert into _vt\\.redo_statement.*", &sqltypes.Result{})
   502  	db.AddQuery("delete from _vt.redo_state where dtid = 'aa'", &sqltypes.Result{})
   503  	db.AddQuery("delete from _vt.redo_statement where dtid = 'aa'", &sqltypes.Result{})
   504  	db.AddQuery("update test_table set `name` = 2 where pk = 1 limit 10001", &sqltypes.Result{})
   505  	return &TxExecutor{
   506  		ctx:      ctx,
   507  		logStats: logStats,
   508  		te:       tsv.te,
   509  	}, tsv, db
   510  }
   511  
   512  // newShortAgeExecutor is same as newTestTxExecutor, but shorter transaction abandon age.
   513  func newShortAgeExecutor(t *testing.T) (txe *TxExecutor, tsv *TabletServer, db *fakesqldb.DB) {
   514  	db = setUpQueryExecutorTest(t)
   515  	logStats := tabletenv.NewLogStats(ctx, "TestTxExecutor")
   516  	tsv = newTestTabletServer(ctx, smallTxPool|shortTwopcAge, db)
   517  	db.AddQueryPattern("insert into _vt\\.redo_state\\(dtid, state, time_created\\) values \\('aa', 1,.*", &sqltypes.Result{})
   518  	db.AddQueryPattern("insert into _vt\\.redo_statement.*", &sqltypes.Result{})
   519  	db.AddQuery("delete from _vt.redo_state where dtid = 'aa'", &sqltypes.Result{})
   520  	db.AddQuery("delete from _vt.redo_statement where dtid = 'aa'", &sqltypes.Result{})
   521  	db.AddQuery("update test_table set `name` = 2 where pk = 1 limit 10001", &sqltypes.Result{})
   522  	return &TxExecutor{
   523  		ctx:      ctx,
   524  		logStats: logStats,
   525  		te:       tsv.te,
   526  	}, tsv, db
   527  }
   528  
   529  // newNoTwopcExecutor is same as newTestTxExecutor, but 2pc disabled.
   530  func newNoTwopcExecutor(t *testing.T) (txe *TxExecutor, tsv *TabletServer, db *fakesqldb.DB) {
   531  	db = setUpQueryExecutorTest(t)
   532  	logStats := tabletenv.NewLogStats(ctx, "TestTxExecutor")
   533  	tsv = newTestTabletServer(ctx, noTwopc, db)
   534  	return &TxExecutor{
   535  		ctx:      ctx,
   536  		logStats: logStats,
   537  		te:       tsv.te,
   538  	}, tsv, db
   539  }
   540  
   541  // newTxForPrep creates a non-empty transaction.
   542  func newTxForPrep(tsv *TabletServer) int64 {
   543  	txid := newTransaction(tsv, nil)
   544  	target := querypb.Target{TabletType: topodatapb.TabletType_PRIMARY}
   545  	_, err := tsv.Execute(ctx, &target, "update test_table set name = 2 where pk = 1", nil, txid, 0, nil)
   546  	if err != nil {
   547  		panic(err)
   548  	}
   549  	return txid
   550  }