vitess.io/vitess@v0.16.2/go/test/endtoend/reparent/prscomplex/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 misc
    18  
    19  import (
    20  	"context"
    21  	_ "embed"
    22  	"flag"
    23  	"os"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/stretchr/testify/assert"
    28  	"github.com/stretchr/testify/require"
    29  
    30  	"vitess.io/vitess/go/mysql"
    31  	"vitess.io/vitess/go/test/endtoend/cluster"
    32  	rutils "vitess.io/vitess/go/test/endtoend/reparent/utils"
    33  	"vitess.io/vitess/go/test/endtoend/utils"
    34  )
    35  
    36  var (
    37  	clusterInstance *cluster.LocalProcessCluster
    38  	vtParams        mysql.ConnParams
    39  	keyspaceName    = "ks"
    40  	cell            = "test"
    41  
    42  	//go:embed schema.sql
    43  	schemaSQL string
    44  )
    45  
    46  func TestMain(m *testing.M) {
    47  	defer cluster.PanicHandler(nil)
    48  	flag.Parse()
    49  
    50  	exitCode := func() int {
    51  		clusterInstance = cluster.NewCluster(cell, "localhost")
    52  		defer clusterInstance.Teardown()
    53  
    54  		// Start topo server
    55  		err := clusterInstance.StartTopo()
    56  		if err != nil {
    57  			return 1
    58  		}
    59  
    60  		// Start keyspace
    61  		keyspace := &cluster.Keyspace{
    62  			Name:      keyspaceName,
    63  			SchemaSQL: schemaSQL,
    64  		}
    65  		clusterInstance.VtTabletExtraArgs = append(clusterInstance.VtTabletExtraArgs,
    66  			"--queryserver-config-query-timeout=9000",
    67  			"--queryserver-config-pool-size=3",
    68  			"--queryserver-config-stream-pool-size=3",
    69  			"--queryserver-config-transaction-cap=2",
    70  			"--queryserver-config-transaction-timeout=20",
    71  			"--shutdown_grace_period=3",
    72  			"--queryserver-config-schema-change-signal=false")
    73  		err = clusterInstance.StartUnshardedKeyspace(*keyspace, 1, false)
    74  		if err != nil {
    75  			return 1
    76  		}
    77  
    78  		// Start vtgate
    79  		clusterInstance.VtGateExtraArgs = append(clusterInstance.VtGateExtraArgs,
    80  			"--planner-version=gen4",
    81  			"--mysql_default_workload=olap",
    82  			"--schema_change_signal=false")
    83  		err = clusterInstance.StartVtgate()
    84  		if err != nil {
    85  			return 1
    86  		}
    87  
    88  		vtParams = mysql.ConnParams{
    89  			Host: clusterInstance.Hostname,
    90  			Port: clusterInstance.VtgateMySQLPort,
    91  		}
    92  		return m.Run()
    93  	}()
    94  	os.Exit(exitCode)
    95  }
    96  
    97  /*
    98  TestAcquireSameConnID tests that a query started on a connection gets reconnected with a new connection.
    99  Another query acquires the old connection ID and does not override the query list maintained by the vttablet process.
   100  PRS should not fail as the query list is maintained appropriately.
   101  */
   102  func TestAcquireSameConnID(t *testing.T) {
   103  	defer func() {
   104  		err := recover()
   105  		if err != nil {
   106  			require.Equal(t, "Fail in goroutine after TestAcquireSameConnID has completed", err)
   107  		}
   108  	}()
   109  	ctx := context.Background()
   110  	conn, err := mysql.Connect(ctx, &vtParams)
   111  	require.NoError(t, err)
   112  	defer conn.Close()
   113  
   114  	// start a reserved connection
   115  	utils.Exec(t, conn, "set sql_mode=''")
   116  	_ = utils.Exec(t, conn, "select connection_id()")
   117  
   118  	// restart the mysql to trigger reconnect on next query.
   119  	primTablet := clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet()
   120  	err = primTablet.MysqlctlProcess.Stop()
   121  	require.NoError(t, err)
   122  	err = primTablet.MysqlctlProcess.StartProvideInit(false)
   123  	require.NoError(t, err)
   124  
   125  	go func() {
   126  		// this will trigger reconnect with a new connection id, which will be lower than the origin connection id.
   127  		_, _ = utils.ExecAllowError(t, conn, "select connection_id(), sleep(4000)")
   128  	}()
   129  	time.Sleep(5 * time.Second)
   130  
   131  	totalErrCount := 0
   132  	// run through 100 times to acquire new connection, this might override the original connection id.
   133  	var conn2 *mysql.Conn
   134  	for i := 0; i < 100; i++ {
   135  		conn2, err = mysql.Connect(ctx, &vtParams)
   136  		require.NoError(t, err)
   137  
   138  		utils.Exec(t, conn2, "set sql_mode=''")
   139  		// ReserveExecute
   140  		_, err = utils.ExecAllowError(t, conn2, "select connection_id()")
   141  		if err != nil {
   142  			totalErrCount++
   143  		}
   144  		// Execute
   145  		_, err = utils.ExecAllowError(t, conn2, "select connection_id()")
   146  		if err != nil {
   147  			totalErrCount++
   148  		}
   149  	}
   150  
   151  	// We run the above loop 100 times so we execute 200 queries, of which only some should fail due to MySQL restart.
   152  	assert.Less(t, totalErrCount, 10, "MySQL restart can cause some errors, but not too many.")
   153  
   154  	// prs should happen without any error.
   155  	text, err := rutils.Prs(t, clusterInstance, clusterInstance.Keyspaces[0].Shards[0].Replica())
   156  	require.NoError(t, err, text)
   157  }