vitess.io/vitess@v0.16.2/go/test/endtoend/tabletmanager/replication_manager/tablet_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 tabletmanager 18 19 import ( 20 "context" 21 "flag" 22 "fmt" 23 "os" 24 "testing" 25 "time" 26 27 "vitess.io/vitess/go/vt/sidecardb" 28 29 "github.com/stretchr/testify/require" 30 31 "vitess.io/vitess/go/test/endtoend/cluster" 32 replicationdatapb "vitess.io/vitess/go/vt/proto/replicationdata" 33 tabletpb "vitess.io/vitess/go/vt/proto/topodata" 34 tmc "vitess.io/vitess/go/vt/vttablet/grpctmclient" 35 ) 36 37 var ( 38 clusterInstance *cluster.LocalProcessCluster 39 tmClient *tmc.Client 40 primaryTablet cluster.Vttablet 41 replicaTablet cluster.Vttablet 42 hostname = "localhost" 43 keyspaceName = "ks" 44 cell = "zone1" 45 sqlSchema = ` 46 create table t1( 47 id bigint, 48 value varchar(16), 49 primary key(id) 50 ) Engine=InnoDB DEFAULT CHARSET=utf8; 51 CREATE VIEW v1 AS SELECT id, value FROM t1; 52 ` 53 54 vSchema = ` 55 { 56 "sharded": false, 57 "vindexes": { 58 "hash": { 59 "type": "hash" 60 } 61 }, 62 "tables": { 63 "t1": { 64 "column_vindexes": [ 65 { 66 "column": "id", 67 "name": "hash" 68 } 69 ] 70 } 71 } 72 }` 73 ) 74 75 func TestMain(m *testing.M) { 76 defer cluster.PanicHandler(nil) 77 flag.Parse() 78 79 exitCode := func() int { 80 clusterInstance = cluster.NewCluster(cell, hostname) 81 defer clusterInstance.Teardown() 82 83 // Start topo server 84 err := clusterInstance.StartTopo() 85 if err != nil { 86 return 1 87 } 88 89 // Start keyspace 90 keyspace := &cluster.Keyspace{ 91 Name: keyspaceName, 92 SchemaSQL: sqlSchema, 93 VSchema: vSchema, 94 } 95 96 if err = clusterInstance.StartUnshardedKeyspace(*keyspace, 1, false); err != nil { 97 return 1 98 } 99 100 // Collect table paths and ports 101 tablets := clusterInstance.Keyspaces[0].Shards[0].Vttablets 102 for _, tablet := range tablets { 103 if tablet.Type == "primary" { 104 primaryTablet = *tablet 105 } else { 106 replicaTablet = *tablet 107 } 108 } 109 110 // create tablet manager client 111 tmClient = tmc.NewClient() 112 113 return m.Run() 114 }() 115 os.Exit(exitCode) 116 } 117 118 func tmcGetReplicationStatus(ctx context.Context, tabletGrpcPort int) (*replicationdatapb.Status, error) { 119 vtablet := getTablet(tabletGrpcPort) 120 return tmClient.ReplicationStatus(ctx, vtablet) 121 } 122 123 func getTablet(tabletGrpcPort int) *tabletpb.Tablet { 124 portMap := make(map[string]int32) 125 portMap["grpc"] = int32(tabletGrpcPort) 126 return &tabletpb.Tablet{Hostname: hostname, PortMap: portMap} 127 } 128 129 // resurrectTablet is used to resurrect the given tablet 130 func resurrectTablet(t *testing.T, tab cluster.Vttablet) { 131 // initialize config again to regenerate the my.cnf file which has the port to use 132 _, err := tab.MysqlctlProcess.ExecuteCommandWithOutput("--log_dir", tab.MysqlctlProcess.LogDirectory, 133 "--tablet_uid", fmt.Sprintf("%d", tab.MysqlctlProcess.TabletUID), 134 "--mysql_port", fmt.Sprintf("%d", tab.MysqlctlProcess.MySQLPort), 135 "init_config") 136 require.NoError(t, err) 137 138 tab.MysqlctlProcess.InitMysql = false 139 err = tab.MysqlctlProcess.Start() 140 require.NoError(t, err) 141 142 // Start the tablet 143 tab.VttabletProcess.ServingStatus = "SERVING" 144 err = tab.VttabletProcess.Setup() 145 require.NoError(t, err) 146 } 147 148 // stopTablet stops the tablet 149 func stopTablet(t *testing.T, tab cluster.Vttablet) { 150 err := tab.VttabletProcess.TearDownWithTimeout(30 * time.Second) 151 require.NoError(t, err) 152 err = tab.MysqlctlProcess.Stop() 153 require.NoError(t, err) 154 } 155 156 func waitForSourcePort(ctx context.Context, t *testing.T, tablet cluster.Vttablet, expectedPort int32) error { 157 timeout := time.Now().Add(15 * time.Second) 158 for time.Now().Before(timeout) { 159 // Check that initially replication is setup correctly on the replica tablet 160 replicaStatus, err := tmcGetReplicationStatus(ctx, tablet.GrpcPort) 161 if err == nil && replicaStatus.SourcePort == expectedPort { 162 return nil 163 } 164 time.Sleep(300 * time.Millisecond) 165 } 166 return fmt.Errorf("time out before source port became %v for %v", expectedPort, tablet.Alias) 167 } 168 169 func getSidecarDBDDLQueryCount(tablet *cluster.VttabletProcess) (int64, error) { 170 vars := tablet.GetVars() 171 key := sidecardb.StatsKeyQueryCount 172 val, ok := vars[key] 173 if !ok { 174 return 0, fmt.Errorf("%s not found in debug/vars", key) 175 } 176 return int64(val.(float64)), nil 177 } 178 func TestReplicationRepairAfterPrimaryTabletChange(t *testing.T) { 179 ctx := context.Background() 180 // Check that initially replication is setup correctly on the replica tablet 181 err := waitForSourcePort(ctx, t, replicaTablet, int32(primaryTablet.MySQLPort)) 182 require.NoError(t, err) 183 184 sidecarDDLCount, err := getSidecarDBDDLQueryCount(primaryTablet.VttabletProcess) 185 require.NoError(t, err) 186 // sidecar db should create all _vt tables when vttablet started 187 require.Greater(t, sidecarDDLCount, int64(0)) 188 189 // Stop the primary tablet 190 stopTablet(t, primaryTablet) 191 // Change the MySQL port of the primary tablet 192 newMysqlPort := clusterInstance.GetAndReservePort() 193 primaryTablet.MySQLPort = newMysqlPort 194 primaryTablet.MysqlctlProcess.MySQLPort = newMysqlPort 195 196 // Start the primary tablet again 197 resurrectTablet(t, primaryTablet) 198 199 // Let replication manager repair replication 200 err = waitForSourcePort(ctx, t, replicaTablet, int32(newMysqlPort)) 201 require.NoError(t, err) 202 203 sidecarDDLCount, err = getSidecarDBDDLQueryCount(primaryTablet.VttabletProcess) 204 require.NoError(t, err) 205 // sidecardb should find the desired _vt schema and not apply any new creates or upgrades when the tablet comes up again 206 require.Equal(t, sidecarDDLCount, int64(0)) 207 }