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 }