vitess.io/vitess@v0.16.2/go/test/endtoend/vtgate/consolidator/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  	"flag"
    22  	"fmt"
    23  	"os"
    24  	"testing"
    25  
    26  	"github.com/google/go-cmp/cmp"
    27  	"github.com/stretchr/testify/require"
    28  
    29  	"vitess.io/vitess/go/mysql"
    30  	"vitess.io/vitess/go/sqltypes"
    31  	"vitess.io/vitess/go/test/endtoend/cluster"
    32  	"vitess.io/vitess/go/test/endtoend/utils"
    33  )
    34  
    35  type consolidatorTestCase struct {
    36  	tabletType           string
    37  	tabletProcess        *cluster.VttabletProcess
    38  	query                string
    39  	expectConsolidations bool
    40  }
    41  
    42  var (
    43  	clusterInstance *cluster.LocalProcessCluster
    44  	vtParams        mysql.ConnParams
    45  	KeyspaceName    = "ks"
    46  	Cell            = "test"
    47  	SchemaSQL       = `create table t1(
    48  	id1 bigint,
    49  	id2 bigint,
    50  	primary key(id1)
    51  ) Engine=InnoDB;`
    52  
    53  	VSchema = `
    54  {
    55    "sharded": false,
    56    "vindexes": {
    57      "hash": {
    58        "type": "hash"
    59      }
    60    },
    61    "tables": {
    62      "t1": {}
    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(
    88  			*keyspace,
    89  			[]string{"-"},
    90  			1, /*creates 1 replica tablet in addition to primary*/
    91  			false,
    92  		); err != nil {
    93  			return 1
    94  		}
    95  
    96  		// Start vtgate
    97  		if err := clusterInstance.StartVtgate(); err != nil {
    98  			return 1
    99  		}
   100  
   101  		vtParams = mysql.ConnParams{
   102  			Host: clusterInstance.Hostname,
   103  			Port: clusterInstance.VtgateMySQLPort,
   104  		}
   105  
   106  		conn, err := mysql.Connect(context.Background(), &vtParams)
   107  		if err != nil {
   108  			return 1
   109  		}
   110  		defer conn.Close()
   111  
   112  		// Insert some test data.
   113  		_, err = conn.ExecuteFetch(`insert into t1(id1, id2) values (1, 1)`, 1000, true)
   114  		if err != nil {
   115  			return 1
   116  		}
   117  		defer func() {
   118  			conn.ExecuteFetch(`use @primary`, 1000, true)
   119  			conn.ExecuteFetch(`delete from t1`, 1000, true)
   120  		}()
   121  
   122  		return m.Run()
   123  	}()
   124  	os.Exit(exitCode)
   125  }
   126  
   127  func TestConsolidatorEnabledByDefault(t *testing.T) {
   128  	testConsolidator(t, []consolidatorTestCase{
   129  		{
   130  			"@primary",
   131  			clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet().VttabletProcess,
   132  			`select id2 from t1 where sleep(2) = 0 order by id1 asc limit 1`,
   133  			true,
   134  		},
   135  		{
   136  			"@replica",
   137  			clusterInstance.Keyspaces[0].Shards[0].Replica().VttabletProcess,
   138  			`select id2 from t1 where sleep(2) = 0 order by id1 asc limit 1`,
   139  			true,
   140  		},
   141  	})
   142  }
   143  
   144  func TestConsolidatorEnabledWithDirective(t *testing.T) {
   145  	testConsolidator(t, []consolidatorTestCase{
   146  		{
   147  			"@primary",
   148  			clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet().VttabletProcess,
   149  			`select /*vt+ CONSOLIDATOR=enabled */ id2 from t1 where sleep(2) = 0 order by id1 asc limit 1`,
   150  			true,
   151  		},
   152  		{
   153  			"@replica",
   154  			clusterInstance.Keyspaces[0].Shards[0].Replica().VttabletProcess,
   155  			`select /*vt+ CONSOLIDATOR=enabled */ id2 from t1 where sleep(2) = 0 order by id1 asc limit 1`,
   156  			true,
   157  		},
   158  	})
   159  }
   160  
   161  func TestConsolidatorDisabledWithDirective(t *testing.T) {
   162  	testConsolidator(t, []consolidatorTestCase{
   163  		{
   164  			"@primary",
   165  			clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet().VttabletProcess,
   166  			`select /*vt+ CONSOLIDATOR=disabled */ id2 from t1 where sleep(2) = 0 order by id1 asc limit 1`,
   167  			false,
   168  		},
   169  		{
   170  			"@replica",
   171  			clusterInstance.Keyspaces[0].Shards[0].Replica().VttabletProcess,
   172  			`select /*vt+ CONSOLIDATOR=disabled */ id2 from t1 where sleep(2) = 0 order by id1 asc limit 1`,
   173  			false,
   174  		},
   175  	})
   176  }
   177  
   178  func TestConsolidatorEnabledReplicasWithDirective(t *testing.T) {
   179  	testConsolidator(t, []consolidatorTestCase{
   180  		{
   181  			"@primary",
   182  			clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet().VttabletProcess,
   183  			`select /*vt+ CONSOLIDATOR=enabled_replicas */ id2 from t1 where sleep(2) = 0 order by id1 asc limit 1`,
   184  			false,
   185  		},
   186  		{
   187  			"@replica",
   188  			clusterInstance.Keyspaces[0].Shards[0].Replica().VttabletProcess,
   189  			`select /*vt+ CONSOLIDATOR=enabled_replicas */ id2 from t1 where sleep(2) = 0 order by id1 asc limit 1`,
   190  			true,
   191  		},
   192  	})
   193  }
   194  
   195  func testConsolidator(t *testing.T, testCases []consolidatorTestCase) {
   196  	for _, testCase := range testCases {
   197  		t.Run(fmt.Sprintf("%s%s", testCase.query, testCase.tabletType), func(t *testing.T) {
   198  			// Create a connection.
   199  			conn1, err := mysql.Connect(context.Background(), &vtParams)
   200  			require.NoError(t, err)
   201  			utils.Exec(t, conn1, fmt.Sprintf("use %s", testCase.tabletType))
   202  			defer conn1.Close()
   203  
   204  			// Create another connection.
   205  			conn2, err := mysql.Connect(context.Background(), &vtParams)
   206  			require.NoError(t, err)
   207  			utils.Exec(t, conn2, fmt.Sprintf("use %s", testCase.tabletType))
   208  			defer conn2.Close()
   209  
   210  			// Create a channel for query results.
   211  			qrCh := make(chan *sqltypes.Result, 2)
   212  			defer close(qrCh)
   213  
   214  			execAsync := func(conn *mysql.Conn, query string, qrCh chan *sqltypes.Result) {
   215  				go func() {
   216  					qrCh <- utils.Exec(t, conn, query)
   217  				}()
   218  			}
   219  
   220  			// Check initial consolidations.
   221  			consolidations, err := testCase.tabletProcess.GetConsolidations()
   222  			require.NoError(t, err, "Failed to get consolidations.")
   223  			count := consolidations[testCase.query]
   224  
   225  			// Send two identical async queries in quick succession.
   226  			execAsync(conn1, testCase.query, qrCh)
   227  			execAsync(conn2, testCase.query, qrCh)
   228  
   229  			// Wait for results, verify they are the same.
   230  			qr1 := <-qrCh
   231  			qr2 := <-qrCh
   232  			diff := cmp.Diff(fmt.Sprintf("%v", qr1.Rows), fmt.Sprintf("%v", qr2.Rows))
   233  			require.Empty(t, diff, "Expected query results to be equal but they are different.")
   234  
   235  			// Verify the query was (or was not) consolidated.
   236  			consolidations, err = testCase.tabletProcess.GetConsolidations()
   237  			require.NoError(t, err, "Failed to get consolidations.")
   238  			if testCase.expectConsolidations {
   239  				require.Greater(
   240  					t,
   241  					consolidations[testCase.query],
   242  					count,
   243  					"Expected query `%s` to be consolidated on %s tablet.",
   244  					testCase.query,
   245  					testCase.tabletType,
   246  				)
   247  			} else {
   248  				require.Equal(
   249  					t,
   250  					count,
   251  					consolidations[testCase.query],
   252  					"Did not expect query `%s` to be consolidated on %s tablet.",
   253  					testCase.query,
   254  					testCase.tabletType,
   255  				)
   256  			}
   257  		})
   258  	}
   259  }