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

     1  /*
     2  Copyright 2021 The Vitess Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  	http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  package prefixfanout
    16  
    17  import (
    18  	"context"
    19  	"flag"
    20  	"fmt"
    21  	"os"
    22  	"testing"
    23  
    24  	"vitess.io/vitess/go/test/endtoend/utils"
    25  
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/require"
    28  
    29  	"vitess.io/vitess/go/mysql"
    30  	"vitess.io/vitess/go/test/endtoend/cluster"
    31  )
    32  
    33  var (
    34  	clusterInstance *cluster.LocalProcessCluster
    35  	cell            = "zone1"
    36  	hostname        = "localhost"
    37  
    38  	sKs     = "cfc_testing"
    39  	sSchema = `
    40  CREATE TABLE t1 (
    41  c1 VARCHAR(20) NOT NULL,
    42  c2 varchar(40) NOT NULL,
    43  PRIMARY KEY (c1)
    44  ) ENGINE=Innodb;
    45  `
    46  	sVSchema = `
    47  {
    48      "sharded": true,
    49      "vindexes": {
    50          "cfc": {
    51            "type": "cfc"
    52  		}
    53  	},
    54      "tables": {
    55          "t1": {
    56              "column_vindexes": [
    57                  {
    58                      "column": "c1",
    59                      "name": "cfc"
    60                  }
    61  			],
    62  			"columns": [
    63  				{
    64  					"name": "c2",
    65  					"type": "VARCHAR"
    66  				}
    67  			]
    68  		}
    69      }
    70  }`
    71  
    72  	sKsMD5     = `cfc_testing_md5`
    73  	sSchemaMD5 = `
    74  CREATE TABLE t2 (
    75  c1 VARCHAR(20) NOT NULL,
    76  c2 varchar(40) NOT NULL,
    77  PRIMARY KEY (c1)
    78  ) ENGINE=Innodb;`
    79  
    80  	sVSchemaMD5 = `
    81  {
    82      "sharded": true,
    83      "vindexes": {
    84     	"cfc_md5": {
    85  		  "type": "cfc",
    86  		  "params": {
    87  			  "hash": "md5",
    88  			  "offsets": "[2]"
    89  		  }
    90  		}
    91  	},
    92      "tables": {
    93        	"t2": {
    94              "column_vindexes": [
    95                  {
    96                      "column": "c1",
    97                      "name": "cfc_md5"
    98                  }
    99  			],
   100  			"columns": [
   101  				{
   102  					"name": "c2",
   103  					"type": "VARCHAR"
   104  				}
   105  			]
   106          }
   107      }
   108  }`
   109  )
   110  
   111  func TestMain(m *testing.M) {
   112  	defer cluster.PanicHandler(nil)
   113  	flag.Parse()
   114  
   115  	exitCode := func() int {
   116  		clusterInstance = cluster.NewCluster(cell, hostname)
   117  		defer clusterInstance.Teardown()
   118  
   119  		// Start topo server
   120  		if err := clusterInstance.StartTopo(); err != nil {
   121  			return 1
   122  		}
   123  
   124  		// Start keyspace
   125  		sKeyspace := &cluster.Keyspace{
   126  			Name:      sKs,
   127  			SchemaSQL: sSchema,
   128  			VSchema:   sVSchema,
   129  		}
   130  		// cfc_testing
   131  		if err := clusterInstance.StartKeyspace(*sKeyspace, []string{"-41", "41-4180", "4180-42", "42-"}, 0, false); err != nil {
   132  			return 1
   133  		}
   134  		// cfc_testing_md5
   135  		if err := clusterInstance.StartKeyspace(
   136  			cluster.Keyspace{
   137  				Name:      sKsMD5,
   138  				SchemaSQL: sSchemaMD5,
   139  				VSchema:   sVSchemaMD5,
   140  			}, []string{"-c2", "c2-c20a80", "c20a80-d0", "d0-"}, 0, false); err != nil {
   141  			return 1
   142  		}
   143  
   144  		// Start vtgate
   145  		// This waits for the vtgate process to be healthy
   146  		if err := clusterInstance.StartVtgate(); err != nil {
   147  			return 1
   148  		}
   149  
   150  		// Wait for the cluster to be running and healthy
   151  		if err := clusterInstance.WaitForTabletsToHealthyInVtgate(); err != nil {
   152  			return 1
   153  		}
   154  
   155  		return m.Run()
   156  	}()
   157  	os.Exit(exitCode)
   158  }
   159  
   160  func TestCFCPrefixQueryNoHash(t *testing.T) {
   161  	defer cluster.PanicHandler(t)
   162  	ctx := context.Background()
   163  	vtParams := clusterInstance.GetVTParams(sKs)
   164  	conn, err := mysql.Connect(ctx, &vtParams)
   165  	require.Nil(t, err)
   166  	defer conn.Close()
   167  
   168  	utils.Exec(t, conn, "delete from t1")
   169  	defer utils.Exec(t, conn, "delete from t1")
   170  	// prepare the sentinel rows, i.e. every shard stores a row begins with letter A.
   171  	// hex ascii code of 'A' is 41. For a given primary key, e.g. 'AA' here, it should
   172  	// only legally belong to a single shard. We insert into all shards with different
   173  	// `c2` value so that we can test if a query fans out to all or not. Based on the
   174  	// following shard layout only "41-4180", "4180-42" should serve the rows staring with 'A'.
   175  	shards := []string{"-41", "41-4180", "4180-42", "42-"}
   176  	for i, s := range shards {
   177  		utils.Exec(t, conn, fmt.Sprintf("use `%s:%s`", sKs, s))
   178  		utils.Exec(t, conn, fmt.Sprintf("insert into t1 values('AA', 'shard-%d')", i))
   179  	}
   180  	utils.Exec(t, conn, "use cfc_testing")
   181  	qr := utils.Exec(t, conn, "select c2 from t1 where c1 like 'A%' order by c2")
   182  	assert.Equal(t, 2, len(qr.Rows))
   183  	// should only target a subset of shards serving rows starting with 'A'.
   184  	assert.EqualValues(t, `[[VARCHAR("shard-1")] [VARCHAR("shard-2")]]`, fmt.Sprintf("%v", qr.Rows))
   185  	// should only target a subset of shards serving rows starting with 'AA',
   186  	// the shards to which 'AA' maps to.
   187  	qr = utils.Exec(t, conn, "select c2 from t1 where c1 like 'AA'")
   188  	assert.Equal(t, 1, len(qr.Rows))
   189  	assert.EqualValues(t, `[[VARCHAR("shard-1")]]`, fmt.Sprintf("%v", qr.Rows))
   190  	// fan out to all when there is no prefix
   191  	qr = utils.Exec(t, conn, "select c2 from t1 where c1 like '%A' order by c2")
   192  	assert.Equal(t, 4, len(qr.Rows))
   193  	for i, r := range qr.Rows {
   194  		assert.Equal(t, fmt.Sprintf("shard-%d", i), r[0].ToString())
   195  	}
   196  }
   197  
   198  func TestCFCPrefixQueryWithHash(t *testing.T) {
   199  	defer cluster.PanicHandler(t)
   200  	ctx := context.Background()
   201  	vtParams := clusterInstance.GetVTParams(sKsMD5)
   202  
   203  	conn, err := mysql.Connect(ctx, &vtParams)
   204  	require.Nil(t, err)
   205  	defer conn.Close()
   206  
   207  	utils.Exec(t, conn, "delete from t2")
   208  	defer utils.Exec(t, conn, "delete from t2")
   209  
   210  	shards := []string{"-c2", "c2-c20a80", "c20a80-d0", "d0-"}
   211  	// same idea of sentinel rows as above. Even though each row legally belongs to
   212  	// only one shard, we insert into all shards with different info to test our fan out.
   213  	for i, s := range shards {
   214  		utils.Exec(t, conn, fmt.Sprintf("use `%s:%s`", sKsMD5, s))
   215  		utils.Exec(t, conn, fmt.Sprintf("insert into t2 values('12AX', 'shard-%d')", i))
   216  		utils.Exec(t, conn, fmt.Sprintf("insert into t2 values('12BX', 'shard-%d')", i))
   217  		utils.Exec(t, conn, fmt.Sprintf("insert into t2 values('27CX', 'shard-%d')", i))
   218  	}
   219  
   220  	utils.Exec(t, conn, fmt.Sprintf("use `%s`", sKsMD5))
   221  	// The prefix is ('12', 'A')
   222  	// md5('12') -> c20ad4d76fe97759aa27a0c99bff6710
   223  	// md5('A') -> 7fc56270e7a70fa81a5935b72eacbe29
   224  	// so keyspace id is c20a7f, which means shards "c2-c20a80"
   225  	qr := utils.Exec(t, conn, "select c2 from t2 where c1 like '12A%' order by c2")
   226  	assert.Equal(t, 1, len(qr.Rows))
   227  	assert.Equal(t, `[[VARCHAR("shard-1")]]`, fmt.Sprintf("%v", qr.Rows))
   228  	// The prefix is ('12')
   229  	// md5('12') -> c20ad4d76fe97759aa27a0c99bff6710 so the corresponding
   230  	// so keyspace id is c20a, which means shards "c2-c20a80", "c20a80-d0"
   231  	qr = utils.Exec(t, conn, "select c2 from t2 where c1 like '12%' order by c2")
   232  	assert.Equal(t, 4, len(qr.Rows))
   233  	assert.Equal(t, `[[VARCHAR("shard-1")] [VARCHAR("shard-1")] [VARCHAR("shard-2")] [VARCHAR("shard-2")]]`, fmt.Sprintf("%v", qr.Rows))
   234  	// in vschema the prefix length is defined as 2 bytes however only 1 byte
   235  	// is provided here so the query fans out to all.
   236  	qr = utils.Exec(t, conn, "select c2 from t2 where c1 like '2%' order by c2")
   237  	assert.Equal(t, 4, len(qr.Rows))
   238  	assert.Equal(t, `[[VARCHAR("shard-0")] [VARCHAR("shard-1")] [VARCHAR("shard-2")] [VARCHAR("shard-3")]]`, fmt.Sprintf("%v", qr.Rows))
   239  }
   240  
   241  func TestCFCInsert(t *testing.T) {
   242  	defer cluster.PanicHandler(t)
   243  	ctx := context.Background()
   244  
   245  	vtParams := clusterInstance.GetVTParams(sKs)
   246  	conn, err := mysql.Connect(ctx, &vtParams)
   247  	require.Nil(t, err)
   248  	defer conn.Close()
   249  
   250  	utils.Exec(t, conn, "delete from t1")
   251  	defer utils.Exec(t, conn, "delete from t1")
   252  
   253  	utils.Exec(t, conn, "insert into t1 (c1, c2) values ('AAA', 'BBB')")
   254  	qr := utils.Exec(t, conn, "select c2 from t1 where c1 like 'A%'")
   255  	assert.Equal(t, 1, len(qr.Rows))
   256  	shards := []string{"-41", "4180-42", "42-"}
   257  	for _, s := range shards {
   258  		utils.Exec(t, conn, fmt.Sprintf("use `cfc_testing:%s`", s))
   259  		qr = utils.Exec(t, conn, "select * from t1")
   260  		assert.Equal(t, 0, len(qr.Rows))
   261  	}
   262  	// 'AAA' belongs to 41-4180
   263  	utils.Exec(t, conn, "use `cfc_testing:41-4180`")
   264  	qr = utils.Exec(t, conn, "select c2 from t1")
   265  	assert.Equal(t, 1, len(qr.Rows))
   266  	assert.Equal(t, `[[VARCHAR("BBB")]]`, fmt.Sprintf("%v", qr.Rows))
   267  }