vitess.io/vitess@v0.16.2/go/vt/vtgate/tabletgateway_flaky_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 vtgate 18 19 import ( 20 "context" 21 "testing" 22 "time" 23 24 "github.com/stretchr/testify/require" 25 26 "vitess.io/vitess/go/sqltypes" 27 "vitess.io/vitess/go/vt/discovery" 28 querypb "vitess.io/vitess/go/vt/proto/query" 29 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 30 "vitess.io/vitess/go/vt/vtgate/buffer" 31 ) 32 33 // TestGatewayBufferingWhenPrimarySwitchesServingState is used to test that the buffering mechanism buffers the queries when a primary goes to a non serving state and 34 // stops buffering when the primary is healthy again 35 func TestGatewayBufferingWhenPrimarySwitchesServingState(t *testing.T) { 36 bufferImplementation = "keyspace_events" 37 buffer.SetBufferingModeInTestingEnv(true) 38 defer func() { 39 buffer.SetBufferingModeInTestingEnv(false) 40 bufferImplementation = "healthcheck" 41 }() 42 43 keyspace := "ks1" 44 shard := "-80" 45 tabletType := topodatapb.TabletType_PRIMARY 46 host := "1.1.1.1" 47 port := int32(1001) 48 target := &querypb.Target{ 49 Keyspace: keyspace, 50 Shard: shard, 51 TabletType: tabletType, 52 } 53 54 ts := &fakeTopoServer{} 55 // create a new fake health check. We want to check the buffering code which uses Subscribe, so we must also pass a channel 56 hc := discovery.NewFakeHealthCheck(make(chan *discovery.TabletHealth)) 57 // create a new tablet gateway 58 tg := NewTabletGateway(context.Background(), hc, ts, "cell") 59 60 // add a primary tabelt which is serving 61 sbc := hc.AddTestTablet("cell", host, port, keyspace, shard, tabletType, true, 10, nil) 62 63 // add a result to the sandbox connection 64 sqlResult1 := &sqltypes.Result{ 65 Fields: []*querypb.Field{{ 66 Name: "col1", 67 Type: sqltypes.VarChar, 68 }}, 69 RowsAffected: 1, 70 Rows: [][]sqltypes.Value{{ 71 sqltypes.MakeTrusted(sqltypes.VarChar, []byte("bb")), 72 }}, 73 } 74 sbc.SetResults([]*sqltypes.Result{sqlResult1}) 75 76 // run a query that we indeed get the result added to the sandbox connection back 77 res, err := tg.Execute(context.Background(), target, "query", nil, 0, 0, nil) 78 require.NoError(t, err) 79 require.Equal(t, res, sqlResult1) 80 81 // get the primary tablet from the fake health check 82 primaryTablet := hc.GetPrimaryTablet() 83 require.NotNil(t, primaryTablet) 84 hc.Broadcast(primaryTablet) 85 86 // set the serving type for the primary tablet false and broadcast it so that the buffering code registers this change 87 hc.SetServing(primaryTablet, false) 88 hc.Broadcast(primaryTablet) 89 // add another result to the sandbox connection 90 sbc.SetResults([]*sqltypes.Result{sqlResult1}) 91 92 // execute the query in a go routine since it should be buffered, and check that it eventually succeed 93 queryChan := make(chan struct{}) 94 go func() { 95 res, err = tg.Execute(context.Background(), target, "query", nil, 0, 0, nil) 96 queryChan <- struct{}{} 97 }() 98 99 // set the serving type for the primary tablet true and broadcast it so that the buffering code registers this change 100 // this should stop the buffering and the query executed in the go routine should work. This should be done with some delay so 101 // that we know that the query was buffered 102 time.Sleep(1 * time.Second) 103 hc.SetServing(primaryTablet, true) 104 hc.Broadcast(primaryTablet) 105 106 // wait for the query to execute before checking for results 107 select { 108 case <-queryChan: 109 require.NoError(t, err) 110 require.Equal(t, res, sqlResult1) 111 case <-time.After(15 * time.Second): 112 t.Fatalf("timed out waiting for query to execute") 113 } 114 } 115 116 // TestGatewayBufferingWhileReparenting is used to test that the buffering mechanism buffers the queries when a PRS happens 117 // the healthchecks that happen during a PRS are simulated in this test 118 func TestGatewayBufferingWhileReparenting(t *testing.T) { 119 bufferImplementation = "keyspace_events" 120 buffer.SetBufferingModeInTestingEnv(true) 121 defer func() { 122 buffer.SetBufferingModeInTestingEnv(false) 123 bufferImplementation = "healthcheck" 124 }() 125 126 keyspace := "ks1" 127 shard := "-80" 128 tabletType := topodatapb.TabletType_PRIMARY 129 host := "1.1.1.1" 130 hostReplica := "1.1.1.2" 131 port := int32(1001) 132 portReplica := int32(1002) 133 target := &querypb.Target{ 134 Keyspace: keyspace, 135 Shard: shard, 136 TabletType: tabletType, 137 } 138 139 ts := &fakeTopoServer{} 140 // create a new fake health check. We want to check the buffering code which uses Subscribe, so we must also pass a channel 141 hc := discovery.NewFakeHealthCheck(make(chan *discovery.TabletHealth)) 142 // create a new tablet gateway 143 tg := NewTabletGateway(context.Background(), hc, ts, "cell") 144 145 // add a primary tabelt which is serving 146 sbc := hc.AddTestTablet("cell", host, port, keyspace, shard, tabletType, true, 10, nil) 147 // also add a replica which is serving 148 sbcReplica := hc.AddTestTablet("cell", hostReplica, portReplica, keyspace, shard, topodatapb.TabletType_REPLICA, true, 0, nil) 149 150 // add a result to the sandbox connection 151 sqlResult1 := &sqltypes.Result{ 152 Fields: []*querypb.Field{{ 153 Name: "col1", 154 Type: sqltypes.VarChar, 155 }}, 156 RowsAffected: 1, 157 Rows: [][]sqltypes.Value{{ 158 sqltypes.MakeTrusted(sqltypes.VarChar, []byte("bb")), 159 }}, 160 } 161 sbc.SetResults([]*sqltypes.Result{sqlResult1}) 162 163 // run a query that we indeed get the result added to the sandbox connection back 164 // this also checks that the query reaches the primary tablet and not the replica 165 res, err := tg.Execute(context.Background(), target, "query", nil, 0, 0, nil) 166 require.NoError(t, err) 167 require.Equal(t, res, sqlResult1) 168 169 // get the primary and replica tablet from the fake health check 170 tablets := hc.GetAllTablets() 171 var primaryTablet *topodatapb.Tablet 172 var replicaTablet *topodatapb.Tablet 173 174 for _, tablet := range tablets { 175 if tablet.Type == topodatapb.TabletType_PRIMARY { 176 primaryTablet = tablet 177 } else { 178 replicaTablet = tablet 179 } 180 } 181 require.NotNil(t, primaryTablet) 182 require.NotNil(t, replicaTablet) 183 184 // broadcast its state initially 185 hc.Broadcast(primaryTablet) 186 // set the serving type for the primary tablet false and broadcast it so that the buffering code registers this change 187 hc.SetServing(primaryTablet, false) 188 hc.Broadcast(primaryTablet) 189 190 // add a result to the sandbox connection of the new primary 191 sbcReplica.SetResults([]*sqltypes.Result{sqlResult1}) 192 193 // execute the query in a go routine since it should be buffered, and check that it eventually succeed 194 queryChan := make(chan struct{}) 195 go func() { 196 res, err = tg.Execute(context.Background(), target, "query", nil, 0, 0, nil) 197 queryChan <- struct{}{} 198 }() 199 200 // set the serving type for the new primary tablet true and broadcast it so that the buffering code registers this change 201 // this should stop the buffering and the query executed in the go routine should work. This should be done with some delay so 202 // that we know that the query was buffered 203 time.Sleep(1 * time.Second) 204 // change the tablets types to simulate a PRS. 205 hc.SetTabletType(primaryTablet, topodatapb.TabletType_REPLICA) 206 hc.Broadcast(primaryTablet) 207 hc.SetTabletType(replicaTablet, topodatapb.TabletType_PRIMARY) 208 hc.SetServing(replicaTablet, true) 209 hc.Broadcast(replicaTablet) 210 211 // wait for the query to execute before checking for results 212 select { 213 case <-queryChan: 214 require.NoError(t, err) 215 require.Equal(t, res, sqlResult1) 216 case <-time.After(15 * time.Second): 217 t.Fatalf("timed out waiting for query to execute") 218 } 219 }