vitess.io/vitess@v0.16.2/go/test/endtoend/vtgate/consolidator/main_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 vtgate 18 19 import ( 20 "context" 21 "flag" 22 "fmt" 23 "os" 24 "testing" 25 26 "github.com/google/go-cmp/cmp" 27 "github.com/stretchr/testify/require" 28 29 "vitess.io/vitess/go/mysql" 30 "vitess.io/vitess/go/sqltypes" 31 "vitess.io/vitess/go/test/endtoend/cluster" 32 "vitess.io/vitess/go/test/endtoend/utils" 33 ) 34 35 type consolidatorTestCase struct { 36 tabletType string 37 tabletProcess *cluster.VttabletProcess 38 query string 39 expectConsolidations bool 40 } 41 42 var ( 43 clusterInstance *cluster.LocalProcessCluster 44 vtParams mysql.ConnParams 45 KeyspaceName = "ks" 46 Cell = "test" 47 SchemaSQL = `create table t1( 48 id1 bigint, 49 id2 bigint, 50 primary key(id1) 51 ) Engine=InnoDB;` 52 53 VSchema = ` 54 { 55 "sharded": false, 56 "vindexes": { 57 "hash": { 58 "type": "hash" 59 } 60 }, 61 "tables": { 62 "t1": {} 63 } 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, "localhost") 73 defer clusterInstance.Teardown() 74 75 // Start topo server 76 err := clusterInstance.StartTopo() 77 if err != nil { 78 return 1 79 } 80 81 // Start keyspace 82 keyspace := &cluster.Keyspace{ 83 Name: KeyspaceName, 84 SchemaSQL: SchemaSQL, 85 VSchema: VSchema, 86 } 87 if err := clusterInstance.StartKeyspace( 88 *keyspace, 89 []string{"-"}, 90 1, /*creates 1 replica tablet in addition to primary*/ 91 false, 92 ); err != nil { 93 return 1 94 } 95 96 // Start vtgate 97 if err := clusterInstance.StartVtgate(); err != nil { 98 return 1 99 } 100 101 vtParams = mysql.ConnParams{ 102 Host: clusterInstance.Hostname, 103 Port: clusterInstance.VtgateMySQLPort, 104 } 105 106 conn, err := mysql.Connect(context.Background(), &vtParams) 107 if err != nil { 108 return 1 109 } 110 defer conn.Close() 111 112 // Insert some test data. 113 _, err = conn.ExecuteFetch(`insert into t1(id1, id2) values (1, 1)`, 1000, true) 114 if err != nil { 115 return 1 116 } 117 defer func() { 118 conn.ExecuteFetch(`use @primary`, 1000, true) 119 conn.ExecuteFetch(`delete from t1`, 1000, true) 120 }() 121 122 return m.Run() 123 }() 124 os.Exit(exitCode) 125 } 126 127 func TestConsolidatorEnabledByDefault(t *testing.T) { 128 testConsolidator(t, []consolidatorTestCase{ 129 { 130 "@primary", 131 clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet().VttabletProcess, 132 `select id2 from t1 where sleep(2) = 0 order by id1 asc limit 1`, 133 true, 134 }, 135 { 136 "@replica", 137 clusterInstance.Keyspaces[0].Shards[0].Replica().VttabletProcess, 138 `select id2 from t1 where sleep(2) = 0 order by id1 asc limit 1`, 139 true, 140 }, 141 }) 142 } 143 144 func TestConsolidatorEnabledWithDirective(t *testing.T) { 145 testConsolidator(t, []consolidatorTestCase{ 146 { 147 "@primary", 148 clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet().VttabletProcess, 149 `select /*vt+ CONSOLIDATOR=enabled */ id2 from t1 where sleep(2) = 0 order by id1 asc limit 1`, 150 true, 151 }, 152 { 153 "@replica", 154 clusterInstance.Keyspaces[0].Shards[0].Replica().VttabletProcess, 155 `select /*vt+ CONSOLIDATOR=enabled */ id2 from t1 where sleep(2) = 0 order by id1 asc limit 1`, 156 true, 157 }, 158 }) 159 } 160 161 func TestConsolidatorDisabledWithDirective(t *testing.T) { 162 testConsolidator(t, []consolidatorTestCase{ 163 { 164 "@primary", 165 clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet().VttabletProcess, 166 `select /*vt+ CONSOLIDATOR=disabled */ id2 from t1 where sleep(2) = 0 order by id1 asc limit 1`, 167 false, 168 }, 169 { 170 "@replica", 171 clusterInstance.Keyspaces[0].Shards[0].Replica().VttabletProcess, 172 `select /*vt+ CONSOLIDATOR=disabled */ id2 from t1 where sleep(2) = 0 order by id1 asc limit 1`, 173 false, 174 }, 175 }) 176 } 177 178 func TestConsolidatorEnabledReplicasWithDirective(t *testing.T) { 179 testConsolidator(t, []consolidatorTestCase{ 180 { 181 "@primary", 182 clusterInstance.Keyspaces[0].Shards[0].PrimaryTablet().VttabletProcess, 183 `select /*vt+ CONSOLIDATOR=enabled_replicas */ id2 from t1 where sleep(2) = 0 order by id1 asc limit 1`, 184 false, 185 }, 186 { 187 "@replica", 188 clusterInstance.Keyspaces[0].Shards[0].Replica().VttabletProcess, 189 `select /*vt+ CONSOLIDATOR=enabled_replicas */ id2 from t1 where sleep(2) = 0 order by id1 asc limit 1`, 190 true, 191 }, 192 }) 193 } 194 195 func testConsolidator(t *testing.T, testCases []consolidatorTestCase) { 196 for _, testCase := range testCases { 197 t.Run(fmt.Sprintf("%s%s", testCase.query, testCase.tabletType), func(t *testing.T) { 198 // Create a connection. 199 conn1, err := mysql.Connect(context.Background(), &vtParams) 200 require.NoError(t, err) 201 utils.Exec(t, conn1, fmt.Sprintf("use %s", testCase.tabletType)) 202 defer conn1.Close() 203 204 // Create another connection. 205 conn2, err := mysql.Connect(context.Background(), &vtParams) 206 require.NoError(t, err) 207 utils.Exec(t, conn2, fmt.Sprintf("use %s", testCase.tabletType)) 208 defer conn2.Close() 209 210 // Create a channel for query results. 211 qrCh := make(chan *sqltypes.Result, 2) 212 defer close(qrCh) 213 214 execAsync := func(conn *mysql.Conn, query string, qrCh chan *sqltypes.Result) { 215 go func() { 216 qrCh <- utils.Exec(t, conn, query) 217 }() 218 } 219 220 // Check initial consolidations. 221 consolidations, err := testCase.tabletProcess.GetConsolidations() 222 require.NoError(t, err, "Failed to get consolidations.") 223 count := consolidations[testCase.query] 224 225 // Send two identical async queries in quick succession. 226 execAsync(conn1, testCase.query, qrCh) 227 execAsync(conn2, testCase.query, qrCh) 228 229 // Wait for results, verify they are the same. 230 qr1 := <-qrCh 231 qr2 := <-qrCh 232 diff := cmp.Diff(fmt.Sprintf("%v", qr1.Rows), fmt.Sprintf("%v", qr2.Rows)) 233 require.Empty(t, diff, "Expected query results to be equal but they are different.") 234 235 // Verify the query was (or was not) consolidated. 236 consolidations, err = testCase.tabletProcess.GetConsolidations() 237 require.NoError(t, err, "Failed to get consolidations.") 238 if testCase.expectConsolidations { 239 require.Greater( 240 t, 241 consolidations[testCase.query], 242 count, 243 "Expected query `%s` to be consolidated on %s tablet.", 244 testCase.query, 245 testCase.tabletType, 246 ) 247 } else { 248 require.Equal( 249 t, 250 count, 251 consolidations[testCase.query], 252 "Did not expect query `%s` to be consolidated on %s tablet.", 253 testCase.query, 254 testCase.tabletType, 255 ) 256 } 257 }) 258 } 259 }