Skip to content

Commit

Permalink
server: migrate test-infra to testify for server/statistics_handler_t…
Browse files Browse the repository at this point in the history
…est.go (#28029)
  • Loading branch information
yedamao committed Sep 16, 2021
1 parent 463e6ba commit c891bf3
Show file tree
Hide file tree
Showing 3 changed files with 291 additions and 258 deletions.
231 changes: 231 additions & 0 deletions server/statistics_handler_serial_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
// Copyright 2018 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 server

import (
"database/sql"
"fmt"
"io"
"os"
"testing"
"time"

"github.com/go-sql-driver/mysql"
"github.com/gorilla/mux"
"github.com/pingcap/tidb/session"
"github.com/pingcap/tidb/statistics/handle"
"github.com/pingcap/tidb/testkit"
"github.com/stretchr/testify/require"
)

func TestDumpStatsAPI(t *testing.T) {
store, clean := testkit.CreateMockStore(t)
defer clean()

driver := NewTiDBDriver(store)
client := newTestServerClient()
cfg := newTestConfig()
cfg.Port = client.port
cfg.Status.StatusPort = client.statusPort
cfg.Status.ReportStatus = true

server, err := NewServer(cfg, driver)
require.NoError(t, err)
defer server.Close()

client.port = getPortFromTCPAddr(server.listener.Addr())
client.statusPort = getPortFromTCPAddr(server.statusListener.Addr())
go func() {
err := server.Run()
require.NoError(t, err)
}()
client.waitUntilServerOnline()

dom, err := session.GetDomain(store)
require.NoError(t, err)
statsHandler := &StatsHandler{dom}

prepareData(t, client, statsHandler)

router := mux.NewRouter()
router.Handle("/stats/dump/{db}/{table}", statsHandler)

resp0, err := client.fetchStatus("/stats/dump/tidb/test")
require.NoError(t, err)
defer func() {
require.NoError(t, resp0.Body.Close())
}()

path := "/tmp/stats.json"
fp, err := os.Create(path)
require.NoError(t, err)
require.NotNil(t, fp)
defer func() {
require.NoError(t, fp.Close())
require.NoError(t, os.Remove(path))
}()

js, err := io.ReadAll(resp0.Body)
require.NoError(t, err)
_, err = fp.Write(js)
require.NoError(t, err)
checkData(t, path, client)
checkCorrelation(t, client)

// sleep for 1 seconds to ensure the existence of tidb.test
time.Sleep(time.Second)
timeBeforeDropStats := time.Now()
snapshot := timeBeforeDropStats.Format("20060102150405")
prepare4DumpHistoryStats(t, client)

// test dump history stats
resp1, err := client.fetchStatus("/stats/dump/tidb/test")
require.NoError(t, err)
defer func() {
require.NoError(t, resp1.Body.Close())
}()
js, err = io.ReadAll(resp1.Body)
require.NoError(t, err)
require.Equal(t, "null", string(js))

path1 := "/tmp/stats_history.json"
fp1, err := os.Create(path1)
require.NoError(t, err)
require.NotNil(t, fp1)
defer func() {
require.NoError(t, fp1.Close())
require.NoError(t, os.Remove(path1))
}()

resp2, err := client.fetchStatus("/stats/dump/tidb/test/" + snapshot)
require.NoError(t, err)
defer func() {
require.NoError(t, resp2.Body.Close())
}()
js, err = io.ReadAll(resp2.Body)
require.NoError(t, err)
_, err = fp1.Write(js)
require.NoError(t, err)
checkData(t, path1, client)
}

func prepareData(t *testing.T, client *testServerClient, statHandle *StatsHandler) {
db, err := sql.Open("mysql", client.getDSN())
require.NoError(t, err, "Error connecting")
defer func() {
err := db.Close()
require.NoError(t, err)
}()
tk := testkit.NewDBTestKit(t, db)

h := statHandle.do.StatsHandle()
tk.MustExec("create database tidb")
tk.MustExec("use tidb")
tk.MustExec("create table test (a int, b varchar(20))")
err = h.HandleDDLEvent(<-h.DDLEventCh())
require.NoError(t, err)
tk.MustExec("create index c on test (a, b)")
tk.MustExec("insert test values (1, 's')")
require.NoError(t, h.DumpStatsDeltaToKV(handle.DumpAll))
tk.MustExec("analyze table test")
tk.MustExec("insert into test(a,b) values (1, 'v'),(3, 'vvv'),(5, 'vv')")
is := statHandle.do.InfoSchema()
require.NoError(t, h.DumpStatsDeltaToKV(handle.DumpAll))
require.NoError(t, h.Update(is))
}

func prepare4DumpHistoryStats(t *testing.T, client *testServerClient) {
db, err := sql.Open("mysql", client.getDSN())
require.NoError(t, err, "Error connecting")
defer func() {
err := db.Close()
require.NoError(t, err)
}()

tk := testkit.NewDBTestKit(t, db)

safePointName := "tikv_gc_safe_point"
safePointValue := "20060102-15:04:05 -0700"
safePointComment := "All versions after safe point can be accessed. (DO NOT EDIT)"
updateSafePoint := fmt.Sprintf(`INSERT INTO mysql.tidb VALUES ('%[1]s', '%[2]s', '%[3]s')
ON DUPLICATE KEY
UPDATE variable_value = '%[2]s', comment = '%[3]s'`, safePointName, safePointValue, safePointComment)
tk.MustExec(updateSafePoint)

tk.MustExec("drop table tidb.test")
tk.MustExec("create table tidb.test (a int, b varchar(20))")
}

func checkCorrelation(t *testing.T, client *testServerClient) {
db, err := sql.Open("mysql", client.getDSN())
require.NoError(t, err, "Error connecting")
tk := testkit.NewDBTestKit(t, db)
defer func() {
err := db.Close()
require.NoError(t, err)
}()

tk.MustExec("use tidb")
rows := tk.MustQuery("SELECT tidb_table_id FROM information_schema.tables WHERE table_name = 'test' AND table_schema = 'tidb'")
var tableID int64
if rows.Next() {
err = rows.Scan(&tableID)
require.NoError(t, err)
require.False(t, rows.Next(), "unexpected data")
} else {
require.FailNow(t, "no data")
}
require.NoError(t, rows.Close())
rows = tk.MustQuery("select correlation from mysql.stats_histograms where table_id = ? and hist_id = 1 and is_index = 0", tableID)
if rows.Next() {
var corr float64
err = rows.Scan(&corr)
require.NoError(t, err)
require.Equal(t, float64(1), corr)
require.False(t, rows.Next(), "unexpected data")
} else {
require.FailNow(t, "no data")
}
require.NoError(t, rows.Close())
}

func checkData(t *testing.T, path string, client *testServerClient) {
db, err := sql.Open("mysql", client.getDSN(func(config *mysql.Config) {
config.AllowAllFiles = true
config.Params["sql_mode"] = "''"
}))
require.NoError(t, err, "Error connecting")
tk := testkit.NewDBTestKit(t, db)
defer func() {
err := db.Close()
require.NoError(t, err)
}()

tk.MustExec("use tidb")
tk.MustExec("drop stats test")
tk.MustExec(fmt.Sprintf("load stats '%s'", path))

rows := tk.MustQuery("show stats_meta")
require.True(t, rows.Next(), "unexpected data")
var dbName, tableName string
var modifyCount, count int64
var other interface{}
err = rows.Scan(&dbName, &tableName, &other, &other, &modifyCount, &count)
require.NoError(t, err)
require.Equal(t, "tidb", dbName)
require.Equal(t, "test", tableName)
require.Equal(t, int64(3), modifyCount)
require.Equal(t, int64(4), count)
}
Loading

0 comments on commit c891bf3

Please sign in to comment.