vitess.io/vitess@v0.16.2/go/vt/vtgate/autocommit_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 vtgate
    18  
    19  import (
    20  	"context"
    21  	"testing"
    22  
    23  	"github.com/stretchr/testify/require"
    24  
    25  	"vitess.io/vitess/go/sqltypes"
    26  
    27  	querypb "vitess.io/vitess/go/vt/proto/query"
    28  	vtgatepb "vitess.io/vitess/go/vt/proto/vtgate"
    29  	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
    30  )
    31  
    32  // This file contains tests for all the autocommit code paths
    33  // to make sure that single round-trip commits are executed
    34  // correctly whenever possible.
    35  
    36  // TestAutocommitUpdateSharded: instant-commit.
    37  func TestAutocommitUpdateSharded(t *testing.T) {
    38  	executor, sbc1, sbc2, _ := createExecutorEnv()
    39  
    40  	_, err := autocommitExec(executor, "update user set a=2 where id = 1")
    41  	require.NoError(t, err)
    42  
    43  	assertQueries(t, sbc1, []*querypb.BoundQuery{{
    44  		Sql:           "update `user` set a = 2 where id = 1",
    45  		BindVariables: map[string]*querypb.BindVariable{},
    46  	}})
    47  	testCommitCount(t, "sbc1", sbc1, 0)
    48  
    49  	assertQueries(t, sbc2, nil)
    50  	testCommitCount(t, "sbc1", sbc1, 0)
    51  }
    52  
    53  // TestAutocommitUpdateLookup: transaction: select before update.
    54  func TestAutocommitUpdateLookup(t *testing.T) {
    55  	executor, sbc1, _, sbclookup := createExecutorEnv()
    56  	sbclookup.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult(
    57  		sqltypes.MakeTestFields("b|a", "int64|varbinary"),
    58  		"2|1",
    59  	)})
    60  
    61  	_, err := autocommitExec(executor, "update music set a=2 where id = 2")
    62  	require.NoError(t, err)
    63  
    64  	vars, err := sqltypes.BuildBindVariable([]any{sqltypes.NewInt64(2)})
    65  	require.NoError(t, err)
    66  
    67  	assertQueries(t, sbclookup, []*querypb.BoundQuery{{
    68  		Sql: "select music_id, user_id from music_user_map where music_id in ::music_id for update",
    69  		BindVariables: map[string]*querypb.BindVariable{
    70  			"music_id": vars,
    71  		},
    72  	}})
    73  	testCommitCount(t, "sbclookup", sbclookup, 1)
    74  
    75  	assertQueries(t, sbc1, []*querypb.BoundQuery{{
    76  		Sql:           "update music set a = 2 where id = 2",
    77  		BindVariables: map[string]*querypb.BindVariable{},
    78  	}})
    79  	testCommitCount(t, "sbc1", sbc1, 1)
    80  }
    81  
    82  // TestAutocommitUpdateVindexChange: transaction: select & update before final update.
    83  func TestAutocommitUpdateVindexChange(t *testing.T) {
    84  	executor, sbc, _, sbclookup := createExecutorEnv()
    85  	sbc.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult(
    86  		sqltypes.MakeTestFields("id|name|lastname|name_lastname_keyspace_id_map", "int64|int32|varchar|int64"),
    87  		"1|1|foo|0",
    88  	),
    89  	})
    90  
    91  	_, err := autocommitExec(executor, "update user2 set name='myname', lastname='mylastname' where id = 1")
    92  	require.NoError(t, err)
    93  
    94  	assertQueries(t, sbclookup, []*querypb.BoundQuery{{
    95  		Sql: "delete from name_lastname_keyspace_id_map where `name` = :name and lastname = :lastname and keyspace_id = :keyspace_id",
    96  		BindVariables: map[string]*querypb.BindVariable{
    97  			"lastname":    sqltypes.StringBindVariable("foo"),
    98  			"name":        sqltypes.Int32BindVariable(1),
    99  			"keyspace_id": sqltypes.BytesBindVariable([]byte("\x16k@\xb4J\xbaK\xd6")),
   100  		},
   101  	}, {
   102  		Sql: "insert into name_lastname_keyspace_id_map(`name`, lastname, keyspace_id) values (:name_0, :lastname_0, :keyspace_id_0)",
   103  		BindVariables: map[string]*querypb.BindVariable{
   104  			"name_0":        sqltypes.StringBindVariable("myname"),
   105  			"lastname_0":    sqltypes.StringBindVariable("mylastname"),
   106  			"keyspace_id_0": sqltypes.BytesBindVariable([]byte("\x16k@\xb4J\xbaK\xd6")),
   107  		},
   108  	}})
   109  	testCommitCount(t, "sbclookup", sbclookup, 1)
   110  
   111  	assertQueries(t, sbc, []*querypb.BoundQuery{{
   112  		Sql:           "select id, `name`, lastname, `name` = 'myname' and lastname = 'mylastname' from user2 where id = 1 for update",
   113  		BindVariables: map[string]*querypb.BindVariable{},
   114  	}, {
   115  		Sql:           "update user2 set `name` = 'myname', lastname = 'mylastname' where id = 1",
   116  		BindVariables: map[string]*querypb.BindVariable{},
   117  	}})
   118  	testCommitCount(t, "sbc", sbc, 1)
   119  }
   120  
   121  // TestAutocommitDeleteSharded: instant-commit.
   122  func TestAutocommitDeleteSharded(t *testing.T) {
   123  	executor, sbc1, sbc2, _ := createExecutorEnv()
   124  
   125  	_, err := autocommitExec(executor, "delete from user_extra where user_id = 1")
   126  	require.NoError(t, err)
   127  
   128  	assertQueries(t, sbc1, []*querypb.BoundQuery{{
   129  		Sql:           "delete from user_extra where user_id = 1",
   130  		BindVariables: map[string]*querypb.BindVariable{},
   131  	}})
   132  	testCommitCount(t, "sbc1", sbc1, 0)
   133  
   134  	assertQueries(t, sbc2, nil)
   135  	testCommitCount(t, "sbc1", sbc1, 0)
   136  }
   137  
   138  // TestAutocommitDeleteLookup: transaction: select before update.
   139  func TestAutocommitDeleteLookup(t *testing.T) {
   140  	executor, sbc1, _, sbclookup := createExecutorEnv()
   141  	sbc1.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult(
   142  		sqltypes.MakeTestFields("id|name|lastname", "int64|int32|varchar"),
   143  		"1|1|foo",
   144  	),
   145  	})
   146  	sbclookup.SetResults([]*sqltypes.Result{sqltypes.MakeTestResult(
   147  		sqltypes.MakeTestFields("b|a", "int64|varbinary"),
   148  		"1|1",
   149  	)})
   150  
   151  	_, err := autocommitExec(executor, "delete from music where id = 1")
   152  	require.NoError(t, err)
   153  	vars, err := sqltypes.BuildBindVariable([]any{sqltypes.NewInt64(1)})
   154  	require.NoError(t, err)
   155  
   156  	assertQueries(t, sbclookup, []*querypb.BoundQuery{{
   157  		Sql: "select music_id, user_id from music_user_map where music_id in ::music_id for update",
   158  		BindVariables: map[string]*querypb.BindVariable{
   159  			"music_id": vars,
   160  		},
   161  	}, {
   162  		Sql: "delete from music_user_map where music_id = :music_id and user_id = :user_id",
   163  		BindVariables: map[string]*querypb.BindVariable{
   164  			"music_id": sqltypes.Int32BindVariable(1),
   165  			"user_id":  sqltypes.Uint64BindVariable(1),
   166  		},
   167  	}})
   168  	testCommitCount(t, "sbclookup", sbclookup, 1)
   169  
   170  	assertQueries(t, sbc1, []*querypb.BoundQuery{{
   171  		Sql:           "select user_id, id from music where id = 1 for update",
   172  		BindVariables: map[string]*querypb.BindVariable{},
   173  	}, {
   174  		Sql:           "delete from music where id = 1",
   175  		BindVariables: map[string]*querypb.BindVariable{},
   176  	}})
   177  	testCommitCount(t, "sbc1", sbc1, 1)
   178  }
   179  
   180  // TestAutocommitDeleteIn: instant-commit.
   181  func TestAutocommitDeleteIn(t *testing.T) {
   182  	executor, sbc1, sbc2, _ := createExecutorEnv()
   183  
   184  	_, err := autocommitExec(executor, "delete from user_extra where user_id in (1, 2)")
   185  	require.NoError(t, err)
   186  
   187  	assertQueries(t, sbc1, []*querypb.BoundQuery{{
   188  		Sql:           "delete from user_extra where user_id in (1, 2)",
   189  		BindVariables: map[string]*querypb.BindVariable{},
   190  	}})
   191  	testCommitCount(t, "sbc1", sbc1, 0)
   192  
   193  	assertQueries(t, sbc2, nil)
   194  	testCommitCount(t, "sbc2", sbc2, 0)
   195  }
   196  
   197  // TestAutocommitDeleteMultiShard: instant-commit.
   198  func TestAutocommitDeleteMultiShard(t *testing.T) {
   199  	executor, sbc1, sbc2, _ := createExecutorEnv()
   200  
   201  	_, err := autocommitExec(executor, "delete from user_extra where user_id = user_id + 1")
   202  	require.NoError(t, err)
   203  
   204  	assertQueries(t, sbc1, []*querypb.BoundQuery{{
   205  		Sql:           "delete from user_extra where user_id = user_id + 1",
   206  		BindVariables: map[string]*querypb.BindVariable{},
   207  	}})
   208  	testCommitCount(t, "sbc1", sbc1, 1)
   209  
   210  	assertQueries(t, sbc2, []*querypb.BoundQuery{{
   211  		Sql:           "delete from user_extra where user_id = user_id + 1",
   212  		BindVariables: map[string]*querypb.BindVariable{},
   213  	}})
   214  	testCommitCount(t, "sbc2", sbc2, 1)
   215  }
   216  
   217  // TestAutocommitDeleteMultiShardAutoCommit: instant-commit.
   218  func TestAutocommitDeleteMultiShardAutoCommit(t *testing.T) {
   219  	executor, sbc1, sbc2, _ := createExecutorEnv()
   220  
   221  	_, err := autocommitExec(executor, "delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from user_extra where user_id = user_id + 1")
   222  	require.NoError(t, err)
   223  
   224  	assertQueries(t, sbc1, []*querypb.BoundQuery{{
   225  		Sql:           "delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from user_extra where user_id = user_id + 1",
   226  		BindVariables: map[string]*querypb.BindVariable{},
   227  	}})
   228  	testCommitCount(t, "sbc1", sbc1, 0)
   229  
   230  	assertQueries(t, sbc2, []*querypb.BoundQuery{{
   231  		Sql:           "delete /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ from user_extra where user_id = user_id + 1",
   232  		BindVariables: map[string]*querypb.BindVariable{},
   233  	}})
   234  	testCommitCount(t, "sbc1", sbc1, 0)
   235  }
   236  
   237  // TestAutocommitInsertSharded: instant-commit.
   238  func TestAutocommitInsertSharded(t *testing.T) {
   239  	executor, sbc1, sbc2, _ := createExecutorEnv()
   240  
   241  	_, err := autocommitExec(executor, "insert into user_extra(user_id, v) values (1, 2)")
   242  	require.NoError(t, err)
   243  
   244  	assertQueries(t, sbc1, []*querypb.BoundQuery{{
   245  		Sql: "insert into user_extra(user_id, v) values (:_user_id_0, 2)",
   246  		BindVariables: map[string]*querypb.BindVariable{
   247  			"_user_id_0": sqltypes.Int64BindVariable(1),
   248  		},
   249  	}})
   250  	testCommitCount(t, "sbc1", sbc1, 0)
   251  
   252  	assertQueries(t, sbc2, nil)
   253  	testCommitCount(t, "sbc1", sbc1, 0)
   254  }
   255  
   256  // TestAutocommitInsertLookup: transaction: select before update.
   257  func TestAutocommitInsertLookup(t *testing.T) {
   258  	executor, sbc1, _, sbclookup := createExecutorEnv()
   259  
   260  	_, err := autocommitExec(executor, "insert into user(id, v, name) values (1, 2, 'myname')")
   261  	require.NoError(t, err)
   262  
   263  	assertQueries(t, sbclookup, []*querypb.BoundQuery{{
   264  		Sql: "insert into name_user_map(`name`, user_id) values (:name_0, :user_id_0)",
   265  		BindVariables: map[string]*querypb.BindVariable{
   266  			"name_0":    sqltypes.StringBindVariable("myname"),
   267  			"user_id_0": sqltypes.Uint64BindVariable(1),
   268  		},
   269  	}})
   270  	testCommitCount(t, "sbclookup", sbclookup, 1)
   271  
   272  	assertQueries(t, sbc1, []*querypb.BoundQuery{{
   273  		Sql: "insert into `user`(id, v, `name`) values (:_Id_0, 2, :_name_0)",
   274  		BindVariables: map[string]*querypb.BindVariable{
   275  			"_Id_0":   sqltypes.Int64BindVariable(1),
   276  			"_name_0": sqltypes.StringBindVariable("myname"),
   277  			"__seq0":  sqltypes.Int64BindVariable(1),
   278  		},
   279  	}})
   280  	testCommitCount(t, "sbc1", sbc1, 1)
   281  }
   282  
   283  // TestAutocommitInsertShardAutoCommit: instant-commit.
   284  func TestAutocommitInsertMultishardAutoCommit(t *testing.T) {
   285  	executor, sbc1, sbc2, _ := createExecutorEnv()
   286  
   287  	_, err := autocommitExec(executor, "insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ into user_extra(user_id, v) values (1, 2), (3, 4)")
   288  	require.NoError(t, err)
   289  
   290  	assertQueries(t, sbc1, []*querypb.BoundQuery{{
   291  		Sql: "insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ into user_extra(user_id, v) values (:_user_id_0, 2)",
   292  		BindVariables: map[string]*querypb.BindVariable{
   293  			"_user_id_0": sqltypes.Int64BindVariable(1),
   294  			"_user_id_1": sqltypes.Int64BindVariable(3),
   295  		},
   296  	}})
   297  	testCommitCount(t, "sbc1", sbc1, 0)
   298  
   299  	assertQueries(t, sbc2, []*querypb.BoundQuery{{
   300  		Sql: "insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ into user_extra(user_id, v) values (:_user_id_1, 4)",
   301  		BindVariables: map[string]*querypb.BindVariable{
   302  			"_user_id_0": sqltypes.Int64BindVariable(1),
   303  			"_user_id_1": sqltypes.Int64BindVariable(3),
   304  		},
   305  	}})
   306  	testCommitCount(t, "sbc2", sbc2, 0)
   307  
   308  	executor, sbc1, sbc2, _ = createExecutorEnv()
   309  	// Make the first shard fail - the second completes anyway
   310  	sbc1.MustFailCodes[vtrpcpb.Code_INVALID_ARGUMENT] = 1
   311  	_, err = autocommitExec(executor, "insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ into user_extra(user_id, v) values (1, 2), (3, 4)")
   312  	require.Error(t, err)
   313  	require.Contains(t, err.Error(), "INVALID_ARGUMENT", "expected invalid argument error")
   314  
   315  	testCommitCount(t, "sbc1", sbc1, 0)
   316  
   317  	assertQueries(t, sbc2, []*querypb.BoundQuery{{
   318  		Sql: "insert /*vt+ MULTI_SHARD_AUTOCOMMIT=1 */ into user_extra(user_id, v) values (:_user_id_1, 4)",
   319  		BindVariables: map[string]*querypb.BindVariable{
   320  			"_user_id_0": sqltypes.Int64BindVariable(1),
   321  			"_user_id_1": sqltypes.Int64BindVariable(3),
   322  		},
   323  	}})
   324  	testCommitCount(t, "sbc2", sbc2, 0)
   325  
   326  }
   327  
   328  func TestAutocommitInsertMultishard(t *testing.T) {
   329  	executor, sbc1, sbc2, _ := createExecutorEnv()
   330  
   331  	_, err := autocommitExec(executor, "insert into user_extra(user_id, v) values (1, 2), (3, 4)")
   332  	require.NoError(t, err)
   333  
   334  	assertQueries(t, sbc1, []*querypb.BoundQuery{{
   335  		Sql: "insert into user_extra(user_id, v) values (:_user_id_0, 2)",
   336  		BindVariables: map[string]*querypb.BindVariable{
   337  			"_user_id_0": sqltypes.Int64BindVariable(1),
   338  			"_user_id_1": sqltypes.Int64BindVariable(3),
   339  		},
   340  	}})
   341  	testCommitCount(t, "sbc1", sbc1, 1)
   342  
   343  	assertQueries(t, sbc2, []*querypb.BoundQuery{{
   344  		Sql: "insert into user_extra(user_id, v) values (:_user_id_1, 4)",
   345  		BindVariables: map[string]*querypb.BindVariable{
   346  			"_user_id_0": sqltypes.Int64BindVariable(1),
   347  			"_user_id_1": sqltypes.Int64BindVariable(3),
   348  		},
   349  	}})
   350  	testCommitCount(t, "sbc2", sbc2, 1)
   351  }
   352  
   353  // TestAutocommitInsertAutoinc: instant-commit: sequence fetch is not transactional.
   354  func TestAutocommitInsertAutoinc(t *testing.T) {
   355  	executor, _, _, sbclookup := createExecutorEnv()
   356  
   357  	_, err := autocommitExec(executor, "insert into main1(id, name) values (null, 'myname')")
   358  	require.NoError(t, err)
   359  
   360  	assertQueries(t, sbclookup, []*querypb.BoundQuery{{
   361  		Sql:           "select next :n values from user_seq",
   362  		BindVariables: map[string]*querypb.BindVariable{"n": sqltypes.Int64BindVariable(1)},
   363  	}, {
   364  		Sql: "insert into main1(id, `name`) values (:__seq0, 'myname')",
   365  		BindVariables: map[string]*querypb.BindVariable{
   366  			"__seq0": sqltypes.Int64BindVariable(1),
   367  		},
   368  	}})
   369  	testCommitCount(t, "sbclookup", sbclookup, 0)
   370  }
   371  
   372  // TestAutocommitTransactionStarted: no instant-commit.
   373  func TestAutocommitTransactionStarted(t *testing.T) {
   374  	executor, sbc1, _, _ := createExecutorEnv()
   375  
   376  	session := &vtgatepb.Session{
   377  		TargetString:    "@primary",
   378  		Autocommit:      true,
   379  		InTransaction:   true,
   380  		TransactionMode: vtgatepb.TransactionMode_MULTI,
   381  	}
   382  
   383  	// single shard query - no savepoint needed
   384  	sql := "update `user` set a = 2 where id = 1"
   385  	_, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{})
   386  	require.NoError(t, err)
   387  	require.Len(t, sbc1.Queries, 1)
   388  	require.Equal(t, sql, sbc1.Queries[0].Sql)
   389  	testCommitCount(t, "sbc1", sbc1, 0)
   390  
   391  	sbc1.Queries = nil
   392  	sbc1.CommitCount.Set(0)
   393  
   394  	// multi shard query - savepoint needed
   395  	sql = "update `user` set a = 2 where id in (1, 4)"
   396  	_, err = executor.Execute(context.Background(), "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{})
   397  	require.NoError(t, err)
   398  	require.Len(t, sbc1.Queries, 2)
   399  	require.Contains(t, sbc1.Queries[0].Sql, "savepoint")
   400  	require.Equal(t, sql, sbc1.Queries[1].Sql)
   401  	testCommitCount(t, "sbc1", sbc1, 0)
   402  }
   403  
   404  // TestAutocommitDirectTarget: instant-commit.
   405  func TestAutocommitDirectTarget(t *testing.T) {
   406  	executor, _, _, sbclookup := createExecutorEnv()
   407  
   408  	session := &vtgatepb.Session{
   409  		TargetString:    "TestUnsharded/0@primary",
   410  		Autocommit:      true,
   411  		TransactionMode: vtgatepb.TransactionMode_MULTI,
   412  	}
   413  	sql := "insert into `simple`(val) values ('val')"
   414  
   415  	_, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{})
   416  	require.NoError(t, err)
   417  
   418  	assertQueries(t, sbclookup, []*querypb.BoundQuery{{
   419  		Sql:           sql,
   420  		BindVariables: map[string]*querypb.BindVariable{},
   421  	}})
   422  	testCommitCount(t, "sbclookup", sbclookup, 0)
   423  }
   424  
   425  // TestAutocommitDirectRangeTarget: no instant-commit.
   426  func TestAutocommitDirectRangeTarget(t *testing.T) {
   427  	executor, sbc1, _, _ := createExecutorEnv()
   428  
   429  	session := &vtgatepb.Session{
   430  		TargetString:    "TestExecutor[-]@primary",
   431  		Autocommit:      true,
   432  		TransactionMode: vtgatepb.TransactionMode_MULTI,
   433  	}
   434  	sql := "delete from sharded_user_msgs limit 1000"
   435  
   436  	_, err := executor.Execute(context.Background(), "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{})
   437  	require.NoError(t, err)
   438  
   439  	assertQueries(t, sbc1, []*querypb.BoundQuery{{
   440  		Sql:           sql,
   441  		BindVariables: map[string]*querypb.BindVariable{},
   442  	}})
   443  	testCommitCount(t, "sbc1", sbc1, 1)
   444  }
   445  
   446  func autocommitExec(executor *Executor, sql string) (*sqltypes.Result, error) {
   447  	session := &vtgatepb.Session{
   448  		TargetString:    "@primary",
   449  		Autocommit:      true,
   450  		TransactionMode: vtgatepb.TransactionMode_MULTI,
   451  	}
   452  
   453  	return executor.Execute(context.Background(), "TestExecute", NewSafeSession(session), sql, map[string]*querypb.BindVariable{})
   454  }