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  }