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 }