vitess.io/vitess@v0.16.2/go/test/endtoend/tabletmanager/primary/tablet_test.go (about)

     1  /*
     2  Copyright 2019 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  package primary
    17  
    18  import (
    19  	"context"
    20  	"flag"
    21  	"fmt"
    22  	"net/http"
    23  	"os"
    24  	"testing"
    25  
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/require"
    28  
    29  	"vitess.io/vitess/go/json2"
    30  	"vitess.io/vitess/go/test/endtoend/cluster"
    31  
    32  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    33  )
    34  
    35  var (
    36  	clusterInstance *cluster.LocalProcessCluster
    37  	primaryTablet   cluster.Vttablet
    38  	replicaTablet   cluster.Vttablet
    39  	hostname        = "localhost"
    40  	keyspaceName    = "ks"
    41  	shardName       = "0"
    42  	cell            = "zone1"
    43  	sqlSchema       = `
    44  	create table t1(
    45  		id bigint,
    46  		value varchar(16),
    47  		primary key(id)
    48  	) Engine=InnoDB;
    49  `
    50  
    51  	vSchema = `
    52  	{
    53      "sharded": true,
    54      "vindexes": {
    55        "hash": {
    56          "type": "hash"
    57        }
    58      },
    59      "tables": {
    60        "t1": {
    61          "column_vindexes": [
    62            {
    63              "column": "id",
    64              "name": "hash"
    65            }
    66          ]
    67        }
    68      }
    69    }`
    70  )
    71  
    72  func TestMain(m *testing.M) {
    73  	defer cluster.PanicHandler(nil)
    74  	flag.Parse()
    75  
    76  	exitCode := func() int {
    77  		clusterInstance = cluster.NewCluster(cell, hostname)
    78  		defer clusterInstance.Teardown()
    79  
    80  		// Start topo server
    81  		err := clusterInstance.StartTopo()
    82  		if err != nil {
    83  			return 1
    84  		}
    85  
    86  		// Set extra tablet args for lock timeout
    87  		clusterInstance.VtTabletExtraArgs = []string{
    88  			"--lock_tables_timeout", "5s",
    89  			"--watch_replication_stream",
    90  			"--enable_replication_reporter",
    91  		}
    92  
    93  		// Start keyspace
    94  		keyspace := &cluster.Keyspace{
    95  			Name:      keyspaceName,
    96  			SchemaSQL: sqlSchema,
    97  			VSchema:   vSchema,
    98  		}
    99  
   100  		if err = clusterInstance.StartUnshardedKeyspace(*keyspace, 1, false); err != nil {
   101  			return 1
   102  		}
   103  
   104  		// Collect table paths and ports
   105  		tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets
   106  		for _, tablet := range tablets {
   107  			if tablet.Type == "primary" {
   108  				primaryTablet = *tablet
   109  			} else if tablet.Type != "rdonly" {
   110  				replicaTablet = *tablet
   111  			}
   112  		}
   113  
   114  		return m.Run()
   115  	}()
   116  	os.Exit(exitCode)
   117  }
   118  
   119  func TestRepeatedInitShardPrimary(t *testing.T) {
   120  	defer cluster.PanicHandler(t)
   121  	// Test that using InitShardPrimary can go back and forth between 2 hosts.
   122  
   123  	// Make replica tablet as primary
   124  	err := clusterInstance.VtctlclientProcess.InitShardPrimary(keyspaceName, shardName, cell, replicaTablet.TabletUID)
   125  	require.NoError(t, err)
   126  
   127  	// Run health check on both, make sure they are both healthy.
   128  	// Also make sure the types are correct.
   129  	err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", primaryTablet.Alias)
   130  	require.NoError(t, err)
   131  	checkHealth(t, primaryTablet.HTTPPort, false)
   132  
   133  	err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", replicaTablet.Alias)
   134  	require.NoError(t, err)
   135  	checkHealth(t, replicaTablet.HTTPPort, false)
   136  
   137  	checkTabletType(t, primaryTablet.Alias, "REPLICA")
   138  	checkTabletType(t, replicaTablet.Alias, "PRIMARY")
   139  
   140  	// Come back to the original tablet.
   141  	err = clusterInstance.VtctlclientProcess.InitShardPrimary(keyspaceName, shardName, cell, primaryTablet.TabletUID)
   142  	require.NoError(t, err)
   143  
   144  	// Run health check on both, make sure they are both healthy.
   145  	// Also make sure the types are correct.
   146  	err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", primaryTablet.Alias)
   147  	require.NoError(t, err)
   148  	checkHealth(t, primaryTablet.HTTPPort, false)
   149  
   150  	err = clusterInstance.VtctlclientProcess.ExecuteCommand("RunHealthCheck", replicaTablet.Alias)
   151  	require.NoError(t, err)
   152  	checkHealth(t, replicaTablet.HTTPPort, false)
   153  
   154  	checkTabletType(t, primaryTablet.Alias, "PRIMARY")
   155  	checkTabletType(t, replicaTablet.Alias, "REPLICA")
   156  }
   157  
   158  func TestPrimaryRestartSetsTERTimestamp(t *testing.T) {
   159  	defer cluster.PanicHandler(t)
   160  	// Test that TER timestamp is set when we restart the PRIMARY vttablet.
   161  	// TER = TabletExternallyReparented.
   162  	// See StreamHealthResponse.tablet_externally_reparented_timestamp for details.
   163  
   164  	// Make replica as primary
   165  	err := clusterInstance.VtctlclientProcess.InitShardPrimary(keyspaceName, shardName, cell, replicaTablet.TabletUID)
   166  	require.NoError(t, err)
   167  
   168  	err = replicaTablet.VttabletProcess.WaitForTabletStatus("SERVING")
   169  	require.NoError(t, err)
   170  
   171  	// Capture the current TER.
   172  	shrs, err := clusterInstance.StreamTabletHealth(context.Background(), &replicaTablet, 1)
   173  	require.NoError(t, err)
   174  
   175  	streamHealthRes1 := shrs[0]
   176  	actualType := streamHealthRes1.GetTarget().GetTabletType()
   177  	tabletType := topodatapb.TabletType_value["PRIMARY"]
   178  	got := fmt.Sprintf("%d", actualType)
   179  	want := fmt.Sprintf("%d", tabletType)
   180  	assert.Equal(t, want, got)
   181  	assert.NotNil(t, streamHealthRes1.GetTabletExternallyReparentedTimestamp())
   182  	assert.True(t, streamHealthRes1.GetTabletExternallyReparentedTimestamp() > 0,
   183  		"TER on PRIMARY must be set after InitShardPrimary")
   184  
   185  	// Restart the PRIMARY vttablet and test again
   186  
   187  	// kill the newly promoted primary tablet
   188  	err = replicaTablet.VttabletProcess.TearDown()
   189  	require.NoError(t, err)
   190  
   191  	// Start Vttablet
   192  	err = clusterInstance.StartVttablet(&replicaTablet, "SERVING", false, cell, keyspaceName, hostname, shardName)
   193  	require.NoError(t, err)
   194  
   195  	// Make sure that the TER did not change
   196  	shrs, err = clusterInstance.StreamTabletHealth(context.Background(), &replicaTablet, 1)
   197  	require.NoError(t, err)
   198  
   199  	streamHealthRes2 := shrs[0]
   200  
   201  	actualType = streamHealthRes2.GetTarget().GetTabletType()
   202  	tabletType = topodatapb.TabletType_value["PRIMARY"]
   203  	got = fmt.Sprintf("%d", actualType)
   204  	want = fmt.Sprintf("%d", tabletType)
   205  	assert.Equal(t, want, got)
   206  
   207  	assert.NotNil(t, streamHealthRes2.GetTabletExternallyReparentedTimestamp())
   208  	assert.True(t, streamHealthRes2.GetTabletExternallyReparentedTimestamp() == streamHealthRes1.GetTabletExternallyReparentedTimestamp(),
   209  		fmt.Sprintf("When the PRIMARY vttablet was restarted, "+
   210  			"the TER timestamp must be set by reading the old value from the tablet record. Old: %d, New: %d",
   211  			streamHealthRes1.GetTabletExternallyReparentedTimestamp(),
   212  			streamHealthRes2.GetTabletExternallyReparentedTimestamp()))
   213  
   214  	// Reset primary
   215  	err = clusterInstance.VtctlclientProcess.InitShardPrimary(keyspaceName, shardName, cell, primaryTablet.TabletUID)
   216  	require.NoError(t, err)
   217  	err = primaryTablet.VttabletProcess.WaitForTabletStatus("SERVING")
   218  	require.NoError(t, err)
   219  
   220  }
   221  
   222  func checkHealth(t *testing.T, port int, shouldError bool) {
   223  	url := fmt.Sprintf("http://localhost:%d/healthz", port)
   224  	resp, err := http.Get(url)
   225  	require.NoError(t, err)
   226  	defer resp.Body.Close()
   227  	if shouldError {
   228  		assert.True(t, resp.StatusCode > 400)
   229  	} else {
   230  		assert.Equal(t, 200, resp.StatusCode)
   231  	}
   232  }
   233  
   234  func checkTabletType(t *testing.T, tabletAlias string, typeWant string) {
   235  	result, err := clusterInstance.VtctlclientProcess.ExecuteCommandWithOutput("GetTablet", tabletAlias)
   236  	require.NoError(t, err)
   237  
   238  	var tablet topodatapb.Tablet
   239  	err = json2.Unmarshal([]byte(result), &tablet)
   240  	require.NoError(t, err)
   241  
   242  	actualType := tablet.GetType()
   243  	got := fmt.Sprintf("%d", actualType)
   244  
   245  	tabletType := topodatapb.TabletType_value[typeWant]
   246  	want := fmt.Sprintf("%d", tabletType)
   247  
   248  	assert.Equal(t, want, got)
   249  }