181 lines
7.2 KiB
Go
181 lines
7.2 KiB
Go
// Copyright 2021 PingCAP, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package planreplayer
|
|
|
|
import (
|
|
"archive/zip"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/pingcap/failpoint"
|
|
"github.com/pingcap/tidb/pkg/config"
|
|
"github.com/pingcap/tidb/pkg/testkit"
|
|
"github.com/pingcap/tidb/pkg/testkit/testdata"
|
|
"github.com/pingcap/tidb/pkg/util/replayer"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func checkFileName(s string) bool {
|
|
files := []string{
|
|
"config.toml",
|
|
"debug_trace/debug_trace0.json",
|
|
"meta.txt",
|
|
"stats/test.t_dump_single.json",
|
|
"schema/test.t_dump_single.schema.txt",
|
|
"schema/schema_meta.txt",
|
|
"table_tiflash_replica.txt",
|
|
"variables.toml",
|
|
"session_bindings.sql",
|
|
"global_bindings.sql",
|
|
"sql/sql0.sql",
|
|
"explain.txt",
|
|
"statsMem/test.t_dump_single.txt",
|
|
"sql_meta.toml",
|
|
}
|
|
for _, f := range files {
|
|
if strings.Compare(f, s) == 0 {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func TestPlanReplayer(t *testing.T) {
|
|
require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount", `return(true)`))
|
|
defer func() {
|
|
require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/infoschema/mockTiFlashStoreCount"))
|
|
}()
|
|
store := testkit.CreateMockStore(t)
|
|
tk := testkit.NewTestKit(t, store)
|
|
tk.MustExec("use test")
|
|
tk.MustExec("drop table if exists t")
|
|
tk.MustExec("create table t(a int, b int, index idx_a(a))")
|
|
tk.MustExec("alter table t set tiflash replica 1")
|
|
tk.MustQuery("plan replayer dump explain select * from t where a=10")
|
|
defer os.RemoveAll(replayer.GetPlanReplayerDirName())
|
|
tk.MustQuery("plan replayer dump explain select /*+ read_from_storage(tiflash[t]) */ * from t")
|
|
|
|
tk.MustExec("create table t1 (a int)")
|
|
tk.MustExec("create table t2 (a int)")
|
|
tk.MustExec("create definer=`root`@`127.0.0.1` view v1 as select * from t1")
|
|
tk.MustExec("create definer=`root`@`127.0.0.1` view v2 as select * from v1")
|
|
tk.MustQuery("plan replayer dump explain with tmp as (select a from t1 group by t1.a) select * from tmp, t2 where t2.a=tmp.a;")
|
|
tk.MustQuery("plan replayer dump explain select * from t1 where t1.a > (with cte1 as (select 1) select count(1) from cte1);")
|
|
tk.MustQuery("plan replayer dump explain select * from v1")
|
|
tk.MustQuery("plan replayer dump explain select * from v2")
|
|
require.True(t, len(tk.Session().GetSessionVars().LastPlanReplayerToken) > 0)
|
|
|
|
// clear the status table and assert
|
|
tk.MustExec("delete from mysql.plan_replayer_status")
|
|
tk.MustQuery("plan replayer dump explain select * from v2")
|
|
token := tk.Session().GetSessionVars().LastPlanReplayerToken
|
|
rows := tk.MustQuery(fmt.Sprintf("select * from mysql.plan_replayer_status where token = '%v'", token)).Rows()
|
|
require.Len(t, rows, 1)
|
|
}
|
|
|
|
func TestPlanReplayerCaptureSEM(t *testing.T) {
|
|
originSEM := config.GetGlobalConfig().Security.EnableSEM
|
|
defer func() {
|
|
config.GetGlobalConfig().Security.EnableSEM = originSEM
|
|
}()
|
|
store := testkit.CreateMockStore(t)
|
|
tk := testkit.NewTestKit(t, store)
|
|
tk.MustExec("use test")
|
|
tk.MustExec("plan replayer capture '123' '123';")
|
|
tk.MustExec("create table t(id int)")
|
|
tk.MustQuery("plan replayer dump explain select * from t")
|
|
defer os.RemoveAll(replayer.GetPlanReplayerDirName())
|
|
tk.MustQuery("select count(*) from mysql.plan_replayer_status").Check(testkit.Rows("1"))
|
|
}
|
|
|
|
func TestPlanReplayerCapture(t *testing.T) {
|
|
store, dom := testkit.CreateMockStoreAndDomain(t)
|
|
tk := testkit.NewTestKit(t, store)
|
|
tk.MustExec("use test")
|
|
tk.MustExec("plan replayer capture '123' '123';")
|
|
tk.MustQuery("select sql_digest, plan_digest from mysql.plan_replayer_task;").Check(testkit.Rows("123 123"))
|
|
tk.MustGetErrMsg("plan replayer capture '123' '123';", "plan replayer capture task already exists")
|
|
tk.MustExec("plan replayer capture remove '123' '123'")
|
|
tk.MustQuery("select count(*) from mysql.plan_replayer_task;").Check(testkit.Rows("0"))
|
|
tk.MustExec("create table t(id int)")
|
|
tk.MustExec("prepare stmt from 'update t set id = ? where id = ? + 1';")
|
|
tk.MustExec("SET @number = 5;")
|
|
tk.MustExec("execute stmt using @number,@number")
|
|
_, sqlDigest := tk.Session().GetSessionVars().StmtCtx.SQLDigest()
|
|
_, planDigest := tk.Session().GetSessionVars().StmtCtx.GetPlanDigest()
|
|
tk.MustExec("SET @@tidb_enable_plan_replayer_capture = ON;")
|
|
tk.MustExec("SET @@global.tidb_enable_historical_stats_for_capture='ON'")
|
|
tk.MustExec(fmt.Sprintf("plan replayer capture '%v' '%v'", sqlDigest.String(), planDigest.String()))
|
|
err := dom.GetPlanReplayerHandle().CollectPlanReplayerTask()
|
|
require.NoError(t, err)
|
|
require.NoError(t, failpoint.Enable("github.com/pingcap/tidb/pkg/domain/shouldDumpStats", "return(true)"))
|
|
defer require.NoError(t, failpoint.Disable("github.com/pingcap/tidb/pkg/domain/shouldDumpStats"))
|
|
tk.MustExec("execute stmt using @number,@number")
|
|
task := dom.GetPlanReplayerHandle().DrainTask()
|
|
require.NotNil(t, task)
|
|
}
|
|
|
|
func TestPlanReplayerContinuesCapture(t *testing.T) {
|
|
store, dom := testkit.CreateMockStoreAndDomain(t)
|
|
tk := testkit.NewTestKit(t, store)
|
|
|
|
tk.MustExec("set @@global.tidb_enable_historical_stats='OFF'")
|
|
_, err := tk.Exec("set @@global.tidb_enable_plan_replayer_continuous_capture='ON'")
|
|
require.Error(t, err)
|
|
require.Equal(t, err.Error(), "tidb_enable_historical_stats should be enabled before enabling tidb_enable_plan_replayer_continuous_capture")
|
|
|
|
tk.MustExec("set @@global.tidb_enable_historical_stats='ON'")
|
|
tk.MustExec("set @@global.tidb_enable_plan_replayer_continuous_capture='ON'")
|
|
|
|
prHandle := dom.GetPlanReplayerHandle()
|
|
tk.MustExec("delete from mysql.plan_replayer_status;")
|
|
tk.MustExec("use test")
|
|
tk.MustExec("create table t(id int);")
|
|
tk.MustExec("set @@tidb_enable_plan_replayer_continuous_capture = 'ON'")
|
|
tk.MustQuery("select * from t;")
|
|
task := prHandle.DrainTask()
|
|
require.NotNil(t, task)
|
|
worker := prHandle.GetWorker()
|
|
success := worker.HandleTask(task)
|
|
defer os.RemoveAll(replayer.GetPlanReplayerDirName())
|
|
require.True(t, success)
|
|
tk.MustQuery("select count(*) from mysql.plan_replayer_status").Check(testkit.Rows("1"))
|
|
}
|
|
|
|
func TestPlanReplayerDumpSingle(t *testing.T) {
|
|
dir := t.TempDir()
|
|
logFile := filepath.Join(dir, "tidb.log")
|
|
config.UpdateGlobal(func(conf *config.Config) {
|
|
conf.Log.File.Filename = logFile
|
|
})
|
|
store := testkit.CreateMockStore(t)
|
|
tk := testkit.NewTestKit(t, store)
|
|
tk.MustExec("use test")
|
|
tk.MustExec("drop table if exists t_dump_single")
|
|
tk.MustExec("create table t_dump_single(a int)")
|
|
res := tk.MustQuery("plan replayer dump explain select * from t_dump_single")
|
|
path := testdata.ConvertRowsToStrings(res.Rows())
|
|
|
|
reader, err := zip.OpenReader(filepath.Join(replayer.GetPlanReplayerDirName(), path[0]))
|
|
require.NoError(t, err)
|
|
defer func() { require.NoError(t, reader.Close()) }()
|
|
for _, file := range reader.File {
|
|
require.True(t, checkFileName(file.Name), file.Name)
|
|
}
|
|
}
|