vitess.io/vitess@v0.16.2/go/test/endtoend/vtgate/transaction/single/main_test.go (about) 1 /* 2 Copyright 2022 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 _ "embed" 22 "flag" 23 "os" 24 "testing" 25 26 "github.com/stretchr/testify/require" 27 28 "vitess.io/vitess/go/test/endtoend/utils" 29 30 "vitess.io/vitess/go/mysql" 31 "vitess.io/vitess/go/test/endtoend/cluster" 32 ) 33 34 var ( 35 clusterInstance *cluster.LocalProcessCluster 36 vtParams mysql.ConnParams 37 KeyspaceName = "ks" 38 Cell = "test" 39 40 //go:embed schema.sql 41 SchemaSQL string 42 43 //go:embed vschema.json 44 VSchema string 45 ) 46 47 func TestMain(m *testing.M) { 48 defer cluster.PanicHandler(nil) 49 flag.Parse() 50 51 exitCode := func() int { 52 clusterInstance = cluster.NewCluster(Cell, "localhost") 53 defer clusterInstance.Teardown() 54 55 // Start topo server 56 err := clusterInstance.StartTopo() 57 if err != nil { 58 return 1 59 } 60 61 // Start keyspace 62 keyspace := &cluster.Keyspace{ 63 Name: KeyspaceName, 64 SchemaSQL: SchemaSQL, 65 VSchema: VSchema, 66 } 67 err = clusterInstance.StartKeyspace(*keyspace, []string{"-80", "80-"}, 0, false) 68 if err != nil { 69 return 1 70 } 71 72 // Start vtgate 73 clusterInstance.VtGateExtraArgs = []string{"--transaction_mode", "SINGLE"} 74 err = clusterInstance.StartVtgate() 75 if err != nil { 76 return 1 77 } 78 79 vtParams = clusterInstance.GetVTParams(KeyspaceName) 80 return m.Run() 81 }() 82 os.Exit(exitCode) 83 } 84 85 func TestSingleOneWay(t *testing.T) { 86 conn, err := mysql.Connect(context.Background(), &vtParams) 87 require.NoError(t, err) 88 defer conn.Close() 89 defer func() { 90 utils.Exec(t, conn, `delete from txn_unique_constraints where txn_id = 'txn1'`) 91 utils.Exec(t, conn, `delete from txn_unique_constraints where txn_id = 'txn3'`) 92 }() 93 94 utils.Exec(t, conn, `begin`) 95 utils.Exec(t, conn, `INSERT INTO txn_unique_constraints(id, txn_id, unique_constraint) VALUES (UUID(), 'txn1', 'txn_info_txn_id_txn1')`) 96 utils.Exec(t, conn, `INSERT INTO txn_unique_constraints(id, txn_id, unique_constraint) VALUES (UUID(), 'txn1', 'txn_info_mti_mc___mti1_mc1')`) 97 utils.Exec(t, conn, `commit`) 98 99 utils.Exec(t, conn, `begin`) 100 utils.Exec(t, conn, `INSERT INTO txn_unique_constraints(id, txn_id, unique_constraint) VALUES (UUID(), 'txn3', 'txn_info_txn_id_txn3')`) 101 // should fail with duplicate key error and not with multi-db transaction 102 utils.AssertContainsError(t, conn, `INSERT INTO txn_unique_constraints(id, txn_id, unique_constraint) VALUES (UUID(), 'txn3', 'txn_info_mti_mc___mti1_mc1')`, `Duplicate entry 'txn_info_mti_mc___mti1_mc1'`) 103 utils.Exec(t, conn, `rollback`) 104 } 105 106 func TestSingleReverseWay(t *testing.T) { 107 conn, err := mysql.Connect(context.Background(), &vtParams) 108 require.NoError(t, err) 109 defer conn.Close() 110 defer func() { 111 utils.Exec(t, conn, `delete from txn_unique_constraints where txn_id = 'txn1'`) 112 utils.Exec(t, conn, `delete from txn_unique_constraints where txn_id = 'txn3'`) 113 }() 114 115 utils.Exec(t, conn, `begin`) 116 utils.Exec(t, conn, `INSERT INTO txn_unique_constraints(id, txn_id, unique_constraint) VALUES (UUID(), 'txn3', 'txn_info_txn_id_txn3')`) 117 utils.Exec(t, conn, `INSERT INTO txn_unique_constraints(id, txn_id, unique_constraint) VALUES (UUID(), 'txn3', 'txn_info_mti_mc___mti1_mc1')`) 118 utils.Exec(t, conn, `commit`) 119 120 utils.Exec(t, conn, `begin`) 121 utils.Exec(t, conn, `INSERT INTO txn_unique_constraints(id, txn_id, unique_constraint) VALUES (UUID(), 'txn1', 'txn_info_txn_id_txn1')`) 122 // should fail with duplicate key error and not with multi-db transaction 123 utils.AssertContainsError(t, conn, `INSERT INTO txn_unique_constraints(id, txn_id, unique_constraint) VALUES (UUID(), 'txn1', 'txn_info_mti_mc___mti1_mc1')`, `Duplicate entry 'txn_info_mti_mc___mti1_mc1'`) 124 utils.Exec(t, conn, `rollback`) 125 } 126 127 func TestSingleLookupDangleRow(t *testing.T) { 128 conn, err := mysql.Connect(context.Background(), &vtParams) 129 require.NoError(t, err) 130 defer conn.Close() 131 defer func() { 132 utils.Exec(t, conn, `delete from txn_unique_constraints where txn_id = 'txn3'`) 133 }() 134 135 // insert a dangling row in lookup table 136 utils.Exec(t, conn, `INSERT INTO uniqueConstraint_vdx(unique_constraint, keyspace_id) VALUES ('txn_info_mti_mc___mti1_mc1', 'J\xda\xf0p\x0e\xcc(\x8fਁ\xa7P\x86\xa5=')`) 137 138 utils.Exec(t, conn, `begin`) 139 // should succeed by validating that the original row does not exist for the unique_constraint, so this should succeed. 140 utils.Exec(t, conn, `INSERT INTO txn_unique_constraints(id, txn_id, unique_constraint) VALUES (UUID(), 'txn3', 'txn_info_mti_mc___mti1_mc1')`) 141 utils.Exec(t, conn, `commit`) 142 143 utils.AssertMatches(t, conn, `select txn_id, unique_constraint from txn_unique_constraints where txn_id = 'txn3'`, `[[VARCHAR("txn3") VARCHAR("txn_info_mti_mc___mti1_mc1")]]`) 144 } 145 146 func TestLookupDangleRowLaterMultiDB(t *testing.T) { 147 conn, err := mysql.Connect(context.Background(), &vtParams) 148 require.NoError(t, err) 149 defer conn.Close() 150 defer func() { 151 utils.Exec(t, conn, `delete from uniqueConstraint_vdx where unique_constraint = 'foo'`) 152 utils.Exec(t, conn, `delete from uniqueConstraint_vdx where unique_constraint = 'bar'`) 153 }() 154 155 // insert a dangling row in lookup table 156 utils.Exec(t, conn, `INSERT INTO uniqueConstraint_vdx(unique_constraint, keyspace_id) VALUES ('foo', 'J\xda\xf0p\x0e\xcc(\x8fਁ\xa7P\x86\xa5=')`) 157 utils.Exec(t, conn, `INSERT INTO uniqueConstraint_vdx(unique_constraint, keyspace_id) VALUES ('bar', '\x86\xc8\xc5\x1ac\xfb\x8c+6\xe4\x1f\x03\xd8ϝB')`) 158 // 159 utils.Exec(t, conn, `begin`) 160 // should succeed by validating that the original row does not exist for the unique_constraint, so this should succeed. 161 utils.Exec(t, conn, `INSERT INTO txn_unique_constraints(id, txn_id, unique_constraint) VALUES (UUID(), 'txn1', 'foo')`) 162 // this fails as it starts a transaction on another shard. so complete transaction is aborted. 163 utils.AssertContainsError(t, conn, `INSERT INTO txn_unique_constraints(id, txn_id, unique_constraint) VALUES (UUID(), 'txn3', 'bar')`, `multi-db transaction attempted`) 164 utils.Exec(t, conn, `commit`) 165 166 // this row should not exist. 167 utils.AssertMatches(t, conn, `select txn_id from txn_unique_constraints where txn_id = 'txn1' and unique_constraint = 'foo'`, `[]`) 168 } 169 170 func TestLookupDangleRowRecordInSameShard(t *testing.T) { 171 conn, err := mysql.Connect(context.Background(), &vtParams) 172 require.NoError(t, err) 173 defer conn.Close() 174 defer func() { 175 utils.Exec(t, conn, `delete from txn_unique_constraints where txn_id = 'txn1'`) 176 }() 177 178 // insert a dangling row in lookup table 179 utils.Exec(t, conn, `INSERT INTO uniqueConstraint_vdx(unique_constraint, keyspace_id) VALUES ('foo', 'J\xda\xf0p\x0e\xcc(\x8fਁ\xa7P\x86\xa5=')`) 180 utils.Exec(t, conn, `INSERT INTO uniqueConstraint_vdx(unique_constraint, keyspace_id) VALUES ('bar', '\x86\xc8\xc5\x1ac\xfb\x8c+6\xe4\x1f\x03\xd8ϝB')`) 181 // 182 utils.Exec(t, conn, `begin`) 183 // should succeed by validating that the original row does not exist for the unique_constraint, so this should succeed. 184 utils.Exec(t, conn, `INSERT INTO txn_unique_constraints(id, txn_id, unique_constraint) VALUES (UUID(), 'txn1', 'foo')`) 185 // this also passes as it goes to same shard (no multi-shard transaction). 186 utils.Exec(t, conn, `INSERT INTO txn_unique_constraints(id, txn_id, unique_constraint) VALUES (UUID(), 'txn1', 'bar')`) 187 utils.Exec(t, conn, `commit`) 188 189 utils.AssertMatches(t, conn, `select txn_id, unique_constraint from txn_unique_constraints where txn_id = 'txn1' order by unique_constraint`, `[[VARCHAR("txn1") VARCHAR("bar")] [VARCHAR("txn1") VARCHAR("foo")]]`) 190 } 191 192 func TestMultiDbSecondRecordLookupDangle(t *testing.T) { 193 conn, err := mysql.Connect(context.Background(), &vtParams) 194 require.NoError(t, err) 195 defer conn.Close() 196 defer func() { 197 utils.Exec(t, conn, `delete from uniqueConstraint_vdx where unique_constraint = 'bar'`) 198 }() 199 200 // insert a dangling row in lookup table 201 utils.Exec(t, conn, `INSERT INTO uniqueConstraint_vdx(unique_constraint, keyspace_id) VALUES ('bar', '\x86\xc8\xc5\x1ac\xfb\x8c+6\xe4\x1f\x03\xd8ϝB')`) 202 203 utils.Exec(t, conn, `begin`) 204 // normal query goes to -80. 205 utils.Exec(t, conn, `INSERT INTO txn_unique_constraints(id, txn_id, unique_constraint) VALUES (UUID(), 'txn1', 'foo')`) 206 // dangling row query goes to -80 (where tx already exists). actual query goes to 80- so multi-shard transaction error. 207 utils.AssertContainsError(t, conn, `INSERT INTO txn_unique_constraints(id, txn_id, unique_constraint) VALUES (UUID(), 'txn3', 'txn_info_txn_id_txn1')`, `multi-db transaction attempted`) 208 utils.Exec(t, conn, `commit`) 209 210 // no row should exist. 211 utils.AssertMatches(t, conn, `select txn_id from txn_unique_constraints`, `[]`) 212 213 utils.Exec(t, conn, `begin`) 214 // normal query goes to -80 215 utils.Exec(t, conn, `INSERT INTO txn_unique_constraints(id, txn_id, unique_constraint) VALUES (UUID(), 'txn1', 'foo')`) 216 // dangling row query goes to 80- (no issue there). actual query goes to 80- so multi-shard transaction error. 217 utils.AssertContainsError(t, conn, `INSERT INTO txn_unique_constraints(id, txn_id, unique_constraint) VALUES (UUID(), 'txn3', 'bar')`, `multi-db transaction attempted`) 218 utils.Exec(t, conn, `commit`) 219 220 // no row should exist. 221 utils.AssertMatches(t, conn, `select txn_id from txn_unique_constraints`, `[]`) 222 }