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  }