vitess.io/vitess@v0.16.2/go/test/endtoend/vtgate/schematracker/restarttablet/schema_restart_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 schematracker
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"flag"
    23  	"fmt"
    24  	"net/http"
    25  	"os"
    26  	"testing"
    27  	"time"
    28  
    29  	"vitess.io/vitess/go/test/endtoend/utils"
    30  
    31  	"github.com/stretchr/testify/require"
    32  
    33  	"github.com/stretchr/testify/assert"
    34  
    35  	"vitess.io/vitess/go/mysql"
    36  	"vitess.io/vitess/go/test/endtoend/cluster"
    37  )
    38  
    39  var (
    40  	clusterInstance *cluster.LocalProcessCluster
    41  	vtParams        mysql.ConnParams
    42  	hostname        = "localhost"
    43  	keyspaceName    = "ks"
    44  	cell            = "zone1"
    45  	signalInterval  = 1
    46  	sqlSchema       = `
    47  		create table vt_user (
    48  			id bigint,
    49  			name varchar(64),
    50  			primary key (id)
    51  		) Engine=InnoDB;
    52  			
    53  		create table main (
    54  			id bigint,
    55  			val varchar(128),
    56  			primary key(id)
    57  		) Engine=InnoDB;
    58  
    59  		create table test_table (
    60  			id bigint,
    61  			val varchar(128),
    62  			primary key(id)
    63  		) Engine=InnoDB;
    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, hostname)
    73  		defer clusterInstance.Teardown()
    74  
    75  		// Start topo server
    76  		if err := clusterInstance.StartTopo(); err != nil {
    77  			return 1
    78  		}
    79  
    80  		// List of users authorized to execute vschema ddl operations
    81  		clusterInstance.VtGateExtraArgs = []string{"--schema_change_signal"}
    82  
    83  		// Start keyspace
    84  		keyspace := &cluster.Keyspace{
    85  			Name:      keyspaceName,
    86  			SchemaSQL: sqlSchema,
    87  		}
    88  		if err := clusterInstance.StartUnshardedKeyspace(*keyspace, 1, false); err != nil {
    89  			return 1
    90  		}
    91  
    92  		// restart the tablet so that the schema.Engine gets a chance to start with existing schema
    93  		tablet := clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet()
    94  		tablet.VttabletProcess.ExtraArgs = []string{
    95  			"--queryserver-config-schema-change-signal",
    96  			fmt.Sprintf("--queryserver-config-schema-change-signal-interval=%d", signalInterval),
    97  		}
    98  		if err := tablet.RestartOnlyTablet(); err != nil {
    99  			return 1
   100  		}
   101  
   102  		// Start vtgate
   103  		if err := clusterInstance.StartVtgate(); err != nil {
   104  			clusterInstance.VtgateProcess = cluster.VtgateProcess{}
   105  			return 1
   106  		}
   107  		vtParams = mysql.ConnParams{
   108  			Host: clusterInstance.Hostname,
   109  			Port: clusterInstance.VtgateMySQLPort,
   110  		}
   111  		return m.Run()
   112  	}()
   113  	os.Exit(exitcode)
   114  }
   115  
   116  func TestVSchemaTrackerInit(t *testing.T) {
   117  	defer cluster.PanicHandler(t)
   118  	ctx := context.Background()
   119  	conn, err := mysql.Connect(ctx, &vtParams)
   120  	require.NoError(t, err)
   121  	defer conn.Close()
   122  
   123  	qr := utils.Exec(t, conn, "SHOW VSCHEMA TABLES")
   124  	got := fmt.Sprintf("%v", qr.Rows)
   125  	want := `[[VARCHAR("dual")] [VARCHAR("main")] [VARCHAR("test_table")] [VARCHAR("vt_user")]]`
   126  	assert.Equal(t, want, got)
   127  }
   128  
   129  // TestVSchemaTrackerKeyspaceReInit tests that the vschema tracker
   130  // properly handles primary tablet restarts -- meaning that we maintain
   131  // the exact same vschema state as before the restart.
   132  func TestVSchemaTrackerKeyspaceReInit(t *testing.T) {
   133  	defer cluster.PanicHandler(t)
   134  
   135  	primaryTablet := clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet()
   136  
   137  	// get the vschema prior to the restarts
   138  	var originalResults any
   139  	readVSchema(t, &clusterInstance.VtgateProcess, &originalResults)
   140  	assert.NotNil(t, originalResults)
   141  
   142  	// restart the primary tablet so that the vschema gets reloaded for the keyspace
   143  	for i := 0; i < 5; i++ {
   144  		err := primaryTablet.VttabletProcess.TearDownWithTimeout(30 * time.Second)
   145  		require.NoError(t, err)
   146  		err = primaryTablet.VttabletProcess.Setup()
   147  		require.NoError(t, err)
   148  		err = clusterInstance.WaitForTabletsToHealthyInVtgate()
   149  		require.NoError(t, err)
   150  
   151  		utils.TimeoutAction(t, 1*time.Minute, "timeout - could not find the updated vschema in VTGate", func() bool {
   152  			var newResults any
   153  			readVSchema(t, &clusterInstance.VtgateProcess, &newResults)
   154  			return assert.ObjectsAreEqual(originalResults, newResults)
   155  		})
   156  	}
   157  }
   158  
   159  func readVSchema(t *testing.T, vtgate *cluster.VtgateProcess, results *any) {
   160  	httpClient := &http.Client{Timeout: 5 * time.Second}
   161  	resp, err := httpClient.Get(vtgate.VSchemaURL)
   162  	require.NoError(t, err)
   163  	defer resp.Body.Close()
   164  	assert.Equal(t, 200, resp.StatusCode)
   165  	json.NewDecoder(resp.Body).Decode(results)
   166  }