vitess.io/vitess@v0.16.2/go/test/endtoend/vtgate/errors_as_warnings/main_test.go (about)

     1  /*
     2  Copyright 2021 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  	"flag"
    22  	"fmt"
    23  	"os"
    24  	"strings"
    25  	"testing"
    26  
    27  	"vitess.io/vitess/go/test/endtoend/utils"
    28  
    29  	"github.com/stretchr/testify/require"
    30  
    31  	"vitess.io/vitess/go/mysql"
    32  	"vitess.io/vitess/go/test/endtoend/cluster"
    33  )
    34  
    35  var (
    36  	clusterInstance *cluster.LocalProcessCluster
    37  	vtParams        mysql.ConnParams
    38  	KeyspaceName    = "ks"
    39  	Cell            = "test"
    40  	SchemaSQL       = `create table t1(
    41  	id1 bigint,
    42  	id2 bigint,
    43  	primary key(id1)
    44  ) Engine=InnoDB;`
    45  
    46  	VSchema = `
    47  {
    48    "sharded": true,
    49    "vindexes": {
    50      "hash": {
    51        "type": "hash"
    52      }
    53    },
    54    "tables": {
    55      "t1": {
    56        "column_vindexes": [
    57          {
    58            "column": "id1",
    59            "name": "hash"
    60          }
    61        ]
    62      }
    63    }
    64  }`
    65  )
    66  
    67  func TestMain(m *testing.M) {
    68  	defer cluster.PanicHandler(nil)
    69  	flag.Parse()
    70  
    71  	exitCode := func() int {
    72  		clusterInstance = cluster.NewCluster(Cell, "localhost")
    73  		defer clusterInstance.Teardown()
    74  
    75  		// Start topo server
    76  		err := clusterInstance.StartTopo()
    77  		if err != nil {
    78  			return 1
    79  		}
    80  
    81  		// Start keyspace
    82  		keyspace := &cluster.Keyspace{
    83  			Name:      KeyspaceName,
    84  			SchemaSQL: SchemaSQL,
    85  			VSchema:   VSchema,
    86  		}
    87  		if err := clusterInstance.StartKeyspace(*keyspace, []string{"-80", "80-"}, 1, false); err != nil {
    88  			return 1
    89  		}
    90  
    91  		// Start vtgate
    92  		if err := clusterInstance.StartVtgate(); err != nil {
    93  			return 1
    94  		}
    95  
    96  		vtParams = clusterInstance.GetVTParams(KeyspaceName)
    97  		return m.Run()
    98  	}()
    99  	os.Exit(exitCode)
   100  }
   101  
   102  func TestScatterErrsAsWarns(t *testing.T) {
   103  	if clusterInstance.HasPartialKeyspaces {
   104  		t.Skip("test kills primary on source shard, but query will be on target shard so it will be skipped")
   105  	}
   106  	oltp, err := mysql.Connect(context.Background(), &vtParams)
   107  	require.NoError(t, err)
   108  	defer oltp.Close()
   109  
   110  	olap, err := mysql.Connect(context.Background(), &vtParams)
   111  	require.NoError(t, err)
   112  	defer olap.Close()
   113  
   114  	utils.Exec(t, oltp, `insert into t1(id1, id2) values (1, 1), (2, 2), (3, 3), (4, 4), (5, 5)`)
   115  	defer func() {
   116  		utils.Exec(t, oltp, "use @primary")
   117  		utils.Exec(t, oltp, `delete from t1`)
   118  	}()
   119  
   120  	query1 := `select /*vt+ SCATTER_ERRORS_AS_WARNINGS */ id1 from t1`
   121  	query2 := `select /*vt+ SCATTER_ERRORS_AS_WARNINGS */ id1 from t1 order by id1`
   122  	showQ := "show warnings"
   123  
   124  	// stop the mysql on one tablet, query will fail at vttablet level
   125  	require.NoError(t,
   126  		clusterInstance.Keyspaces[0].Shards[0].Replica().MysqlctlProcess.Stop())
   127  
   128  	modes := []struct {
   129  		conn *mysql.Conn
   130  		m    string
   131  	}{
   132  		{m: "oltp", conn: oltp},
   133  		{m: "olap", conn: olap},
   134  	}
   135  
   136  	for _, mode := range modes {
   137  		t.Run(mode.m, func(t *testing.T) {
   138  			// connection setup
   139  			utils.Exec(t, mode.conn, "use @replica")
   140  			utils.Exec(t, mode.conn, fmt.Sprintf("set workload = %s", mode.m))
   141  
   142  			utils.AssertMatches(t, mode.conn, query1, `[[INT64(4)]]`)
   143  			assertContainsOneOf(t, mode.conn, showQ, "no valid tablet", "no healthy tablet", "mysql.sock: connect: no such file or directory")
   144  			utils.AssertMatches(t, mode.conn, query2, `[[INT64(4)]]`)
   145  			assertContainsOneOf(t, mode.conn, showQ, "no valid tablet", "no healthy tablet", "mysql.sock: connect: no such file or directory")
   146  
   147  			// invalid_field should throw error and not warning
   148  			_, err = mode.conn.ExecuteFetch("SELECT /*vt+ PLANNER=Gen4 SCATTER_ERRORS_AS_WARNINGS */ invalid_field from t1;", 1, false)
   149  			require.Error(t, err)
   150  			serr := mysql.NewSQLErrorFromError(err).(*mysql.SQLError)
   151  			require.Equal(t, 1054, serr.Number(), serr.Error())
   152  		})
   153  	}
   154  }
   155  
   156  func assertContainsOneOf(t *testing.T, conn *mysql.Conn, query string, expected ...string) {
   157  	t.Helper()
   158  	qr := utils.Exec(t, conn, query)
   159  	got := fmt.Sprintf("%v", qr.Rows)
   160  	for _, s := range expected {
   161  		if strings.Contains(got, s) {
   162  			return
   163  		}
   164  	}
   165  
   166  	t.Errorf("%s\n did not match any of %v", got, expected)
   167  }