vitess.io/vitess@v0.16.2/go/vt/vtgate/queryz_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 17 package vtgate 18 19 import ( 20 "io" 21 "net/http" 22 "net/http/httptest" 23 "regexp" 24 "strings" 25 "testing" 26 "time" 27 28 "github.com/stretchr/testify/require" 29 30 "vitess.io/vitess/go/sqltypes" 31 "vitess.io/vitess/go/vt/vtgate/engine" 32 33 querypb "vitess.io/vitess/go/vt/proto/query" 34 ) 35 36 func TestQueryzHandler(t *testing.T) { 37 resp := httptest.NewRecorder() 38 req, _ := http.NewRequest("GET", "/schemaz", nil) 39 40 executor, _, _, _ := createExecutorEnv() 41 42 // single shard query 43 sql := "select id from user where id = 1" 44 _, err := executorExec(executor, sql, nil) 45 require.NoError(t, err) 46 executor.plans.Wait() 47 plan1, ok := executor.debugGetPlan("@primary:" + sql) 48 if !ok { 49 t.Fatalf("couldn't get plan from cache") 50 } 51 plan1.ExecTime = uint64(1 * time.Millisecond) 52 53 // scatter 54 sql = "select id from user" 55 _, err = executorExec(executor, sql, nil) 56 require.NoError(t, err) 57 executor.plans.Wait() 58 plan2, ok := executor.debugGetPlan("@primary:" + sql) 59 if !ok { 60 t.Fatalf("couldn't get plan from cache") 61 } 62 plan2.ExecTime = uint64(1 * time.Second) 63 64 sql = "insert into user (id, name) values (:id, :name)" 65 _, err = executorExec(executor, sql, map[string]*querypb.BindVariable{ 66 "id": sqltypes.Uint64BindVariable(1), 67 "name": sqltypes.BytesBindVariable([]byte("myname")), 68 }) 69 require.NoError(t, err) 70 executor.plans.Wait() 71 plan3, ok := executor.debugGetPlan("@primary:" + sql) 72 if !ok { 73 t.Fatalf("couldn't get plan from cache") 74 } 75 76 // vindex insert from above execution 77 plan4, ok := executor.debugGetPlan("@primary:" + "insert into name_user_map(name, user_id) values(:name_0, :user_id_0)") 78 require.True(t, ok, "couldn't get plan from cache") 79 80 // same query again should add query counts to existing plans 81 sql = "insert into user (id, name) values (:id, :name)" 82 _, err = executorExec(executor, sql, map[string]*querypb.BindVariable{ 83 "id": sqltypes.Uint64BindVariable(1), 84 "name": sqltypes.BytesBindVariable([]byte("myname")), 85 }) 86 87 require.NoError(t, err) 88 89 plan3.ExecTime = uint64(100 * time.Millisecond) 90 plan4.ExecTime = uint64(200 * time.Millisecond) 91 92 queryzHandler(executor, resp, req) 93 body, _ := io.ReadAll(resp.Body) 94 planPattern1 := []string{ 95 `<tr class="low">`, 96 `<td>select id from user where id = 1</td>`, 97 `<td>1</td>`, 98 `<td>0.001000</td>`, 99 `<td>1</td>`, 100 `<td>0</td>`, 101 `<td>1</td>`, 102 `<td>0</td>`, 103 `<td>0.001000</td>`, 104 `<td>1.000000</td>`, 105 `<td>0.000000</td>`, 106 `<td>1.000000</td>`, 107 `<td>0.000000</td>`, 108 `</tr>`, 109 } 110 checkQueryzHasPlan(t, planPattern1, plan1, body) 111 planPattern2 := []string{ 112 `<tr class="high">`, 113 `<td>select id from user</td>`, 114 `<td>1</td>`, 115 `<td>1.000000</td>`, 116 `<td>8</td>`, 117 `<td>0</td>`, 118 `<td>8</td>`, 119 `<td>0</td>`, 120 `<td>1.000000</td>`, 121 `<td>8.000000</td>`, 122 `<td>0.000000</td>`, 123 `<td>8.000000</td>`, 124 `<td>0.000000</td>`, 125 `</tr>`, 126 } 127 checkQueryzHasPlan(t, planPattern2, plan2, body) 128 planPattern3 := []string{ 129 `<tr class="medium">`, 130 `<td>insert into user.*</td>`, 131 `<td>2</td>`, 132 `<td>0.100000</td>`, 133 `<td>2</td>`, 134 `<td>2</td>`, 135 `<td>0</td>`, 136 `<td>0</td>`, 137 `<td>0.050000</td>`, 138 `<td>1.000000</td>`, 139 `<td>1.000000</td>`, 140 `<td>0.000000</td>`, 141 `<td>0.000000</td>`, 142 `</tr>`, 143 } 144 checkQueryzHasPlan(t, planPattern3, plan3, body) 145 planPattern4 := []string{ 146 `<tr class="high">`, 147 `<td>insert into name_user_map.*</td>`, 148 `<td>2</td>`, 149 `<td>0.200000</td>`, 150 `<td>2</td>`, 151 `<td>2</td>`, 152 `<td>0</td>`, 153 `<td>0</td>`, 154 `<td>0.100000</td>`, 155 `<td>1.000000</td>`, 156 `<td>1.000000</td>`, 157 `<td>0.000000</td>`, 158 `<td>0.000000</td>`, 159 `</tr>`, 160 } 161 checkQueryzHasPlan(t, planPattern4, plan4, body) 162 } 163 164 func checkQueryzHasPlan(t *testing.T, planPattern []string, plan *engine.Plan, page []byte) { 165 t.Helper() 166 matcher := regexp.MustCompile(strings.Join(planPattern, `\s*`)) 167 if !matcher.Match(page) { 168 t.Fatalf("queryz page does not contain\nplan:\n%v\npattern:\n%v\npage:\n%s", plan, strings.Join(planPattern, `\s*`), string(page)) 169 } 170 }