You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
394 lines
11 KiB
394 lines
11 KiB
// Copyright 2019 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,
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package helper_test
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gorilla/mux"
|
|
. "github.com/pingcap/check"
|
|
"github.com/pingcap/log"
|
|
"github.com/pingcap/parser/model"
|
|
"github.com/pingcap/tidb/store/helper"
|
|
"github.com/pingcap/tidb/store/mockstore"
|
|
"github.com/pingcap/tidb/store/mockstore/mocktikv"
|
|
"github.com/pingcap/tidb/store/tikv"
|
|
"github.com/pingcap/tidb/util/pdapi"
|
|
"go.uber.org/zap"
|
|
)
|
|
|
|
type HelperTestSuite struct {
|
|
store tikv.Storage
|
|
}
|
|
|
|
var _ = Suite(new(HelperTestSuite))
|
|
|
|
func TestT(t *testing.T) {
|
|
CustomVerboseFlag = true
|
|
TestingT(t)
|
|
}
|
|
|
|
type mockStore struct {
|
|
tikv.Storage
|
|
pdAddrs []string
|
|
}
|
|
|
|
func (s *mockStore) EtcdAddrs() ([]string, error) {
|
|
return s.pdAddrs, nil
|
|
}
|
|
|
|
func (s *mockStore) StartGCWorker() error {
|
|
panic("not implemented")
|
|
}
|
|
|
|
func (s *mockStore) TLSConfig() *tls.Config {
|
|
panic("not implemented")
|
|
}
|
|
|
|
func (s *HelperTestSuite) SetUpSuite(c *C) {
|
|
url := s.mockPDHTTPServer(c)
|
|
time.Sleep(100 * time.Millisecond)
|
|
mvccStore := mocktikv.MustNewMVCCStore()
|
|
mockTikvStore, err := mockstore.NewMockTikvStore(mockstore.WithMVCCStore(mvccStore))
|
|
s.store = &mockStore{
|
|
mockTikvStore.(tikv.Storage),
|
|
[]string{url[len("http://"):]},
|
|
}
|
|
c.Assert(err, IsNil)
|
|
}
|
|
|
|
func (s *HelperTestSuite) TestHotRegion(c *C) {
|
|
h := helper.Helper{
|
|
Store: s.store,
|
|
RegionCache: s.store.GetRegionCache(),
|
|
}
|
|
regionMetric, err := h.FetchHotRegion(pdapi.HotRead)
|
|
c.Assert(err, IsNil, Commentf("err: %+v", err))
|
|
expected := make(map[uint64]helper.RegionMetric)
|
|
expected[1] = helper.RegionMetric{
|
|
FlowBytes: 100,
|
|
MaxHotDegree: 1,
|
|
Count: 0,
|
|
}
|
|
c.Assert(regionMetric, DeepEquals, expected)
|
|
dbInfo := &model.DBInfo{
|
|
Name: model.NewCIStr("test"),
|
|
}
|
|
c.Assert(err, IsNil)
|
|
_, err = h.FetchRegionTableIndex(regionMetric, []*model.DBInfo{dbInfo})
|
|
c.Assert(err, IsNil, Commentf("err: %+v", err))
|
|
}
|
|
|
|
func (s *HelperTestSuite) TestGetRegionsTableInfo(c *C) {
|
|
h := helper.NewHelper(s.store)
|
|
regionsInfo := getMockTiKVRegionsInfo()
|
|
schemas := getMockRegionsTableInfoSchema()
|
|
tableInfos := h.GetRegionsTableInfo(regionsInfo, schemas)
|
|
c.Assert(tableInfos, DeepEquals, getRegionsTableInfoAns(schemas))
|
|
}
|
|
|
|
func (s *HelperTestSuite) TestTiKVRegionsInfo(c *C) {
|
|
h := helper.Helper{
|
|
Store: s.store,
|
|
RegionCache: s.store.GetRegionCache(),
|
|
}
|
|
regionsInfo, err := h.GetRegionsInfo()
|
|
c.Assert(err, IsNil, Commentf("err: %+v", err))
|
|
c.Assert(regionsInfo, DeepEquals, getMockTiKVRegionsInfo())
|
|
}
|
|
|
|
func (s *HelperTestSuite) TestTiKVStoresStat(c *C) {
|
|
h := helper.Helper{
|
|
Store: s.store,
|
|
RegionCache: s.store.GetRegionCache(),
|
|
}
|
|
stat, err := h.GetStoresStat()
|
|
c.Assert(err, IsNil, Commentf("err: %+v", err))
|
|
data, err := json.Marshal(stat)
|
|
c.Assert(err, IsNil)
|
|
c.Assert(string(data), Equals, `{"count":1,"stores":[{"store":{"id":1,"address":"127.0.0.1:20160","state":0,"state_name":"Up","version":"3.0.0-beta","labels":[{"key":"test","value":"test"}],"status_address":"","git_hash":"","start_timestamp":0},"status":{"capacity":"60 GiB","available":"100 GiB","leader_count":10,"leader_weight":999999.999999,"leader_score":999999.999999,"leader_size":1000,"region_count":200,"region_weight":999999.999999,"region_score":999999.999999,"region_size":1000,"start_ts":"2019-04-23T19:30:30+08:00","last_heartbeat_ts":"2019-04-23T19:31:30+08:00","uptime":"1h30m"}}]}`)
|
|
}
|
|
|
|
func (s *HelperTestSuite) mockPDHTTPServer(c *C) (url string) {
|
|
router := mux.NewRouter()
|
|
router.HandleFunc(pdapi.HotRead, s.mockHotRegionResponse)
|
|
router.HandleFunc(pdapi.Regions, s.mockTiKVRegionsInfoResponse)
|
|
router.HandleFunc(pdapi.Stores, s.mockStoreStatResponse)
|
|
serverMux := http.NewServeMux()
|
|
serverMux.Handle("/", router)
|
|
server := httptest.NewServer(serverMux)
|
|
return server.URL
|
|
}
|
|
|
|
func (s *HelperTestSuite) mockHotRegionResponse(w http.ResponseWriter, req *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
regionsStat := helper.HotRegionsStat{
|
|
RegionsStat: []helper.RegionStat{
|
|
{
|
|
FlowBytes: 100,
|
|
RegionID: 1,
|
|
HotDegree: 1,
|
|
},
|
|
},
|
|
}
|
|
resp := helper.StoreHotRegionInfos{
|
|
AsLeader: make(map[uint64]*helper.HotRegionsStat),
|
|
}
|
|
resp.AsLeader[0] = ®ionsStat
|
|
data, err := json.MarshalIndent(resp, "", " ")
|
|
if err != nil {
|
|
log.Panic("json marshal failed", zap.Error(err))
|
|
}
|
|
_, err = w.Write(data)
|
|
if err != nil {
|
|
log.Panic("write http response failed", zap.Error(err))
|
|
}
|
|
|
|
}
|
|
|
|
func getMockRegionsTableInfoSchema() []*model.DBInfo {
|
|
return []*model.DBInfo{
|
|
{
|
|
Name: model.NewCIStr("test"),
|
|
Tables: []*model.TableInfo{
|
|
{
|
|
ID: 41,
|
|
Indices: []*model.IndexInfo{{ID: 1}},
|
|
},
|
|
{
|
|
ID: 63,
|
|
Indices: []*model.IndexInfo{{ID: 1}, {ID: 2}},
|
|
},
|
|
{
|
|
ID: 66,
|
|
Indices: []*model.IndexInfo{{ID: 1}, {ID: 2}, {ID: 3}},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func getRegionsTableInfoAns(dbs []*model.DBInfo) map[int64][]helper.TableInfo {
|
|
ans := make(map[int64][]helper.TableInfo)
|
|
db := dbs[0]
|
|
ans[1] = []helper.TableInfo{}
|
|
ans[2] = []helper.TableInfo{
|
|
{db, db.Tables[0], true, db.Tables[0].Indices[0]},
|
|
{db, db.Tables[0], false, nil},
|
|
}
|
|
ans[3] = []helper.TableInfo{
|
|
{db, db.Tables[1], true, db.Tables[1].Indices[0]},
|
|
{db, db.Tables[1], true, db.Tables[1].Indices[1]},
|
|
{db, db.Tables[1], false, nil},
|
|
}
|
|
ans[4] = []helper.TableInfo{
|
|
{db, db.Tables[2], false, nil},
|
|
}
|
|
ans[5] = []helper.TableInfo{
|
|
{db, db.Tables[2], true, db.Tables[2].Indices[2]},
|
|
{db, db.Tables[2], false, nil},
|
|
}
|
|
ans[6] = []helper.TableInfo{
|
|
{db, db.Tables[2], true, db.Tables[2].Indices[0]},
|
|
}
|
|
ans[7] = []helper.TableInfo{
|
|
{db, db.Tables[2], true, db.Tables[2].Indices[1]},
|
|
}
|
|
ans[8] = []helper.TableInfo{
|
|
{db, db.Tables[2], true, db.Tables[2].Indices[1]},
|
|
{db, db.Tables[2], true, db.Tables[2].Indices[2]},
|
|
{db, db.Tables[2], false, nil},
|
|
}
|
|
return ans
|
|
}
|
|
|
|
func getMockTiKVRegionsInfo() *helper.RegionsInfo {
|
|
regions := []helper.RegionInfo{
|
|
{
|
|
ID: 1,
|
|
StartKey: "",
|
|
EndKey: "12341234",
|
|
Epoch: helper.RegionEpoch{
|
|
ConfVer: 1,
|
|
Version: 1,
|
|
},
|
|
Peers: []helper.RegionPeer{
|
|
{ID: 2, StoreID: 1},
|
|
{ID: 15, StoreID: 51},
|
|
{ID: 66, StoreID: 99, IsLearner: true},
|
|
{ID: 123, StoreID: 111, IsLearner: true},
|
|
},
|
|
Leader: helper.RegionPeer{
|
|
ID: 2,
|
|
StoreID: 1,
|
|
},
|
|
DownPeers: []helper.RegionPeerStat{
|
|
{
|
|
helper.RegionPeer{ID: 66, StoreID: 99, IsLearner: true},
|
|
120,
|
|
},
|
|
},
|
|
PendingPeers: []helper.RegionPeer{
|
|
{ID: 15, StoreID: 51},
|
|
},
|
|
WrittenBytes: 100,
|
|
ReadBytes: 1000,
|
|
ApproximateKeys: 200,
|
|
ApproximateSize: 500,
|
|
},
|
|
// table: 41, record + index: 1
|
|
{
|
|
ID: 2,
|
|
StartKey: "7480000000000000FF295F698000000000FF0000010000000000FA",
|
|
EndKey: "7480000000000000FF2B5F698000000000FF0000010000000000FA",
|
|
Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1},
|
|
Peers: []helper.RegionPeer{{ID: 3, StoreID: 1}},
|
|
Leader: helper.RegionPeer{ID: 3, StoreID: 1},
|
|
},
|
|
// table: 63, record + index: 1, 2
|
|
{
|
|
ID: 3,
|
|
StartKey: "7480000000000000FF3F5F698000000000FF0000010000000000FA",
|
|
EndKey: "7480000000000000FF425F698000000000FF0000010000000000FA",
|
|
Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1},
|
|
Peers: []helper.RegionPeer{{ID: 4, StoreID: 1}},
|
|
Leader: helper.RegionPeer{ID: 4, StoreID: 1},
|
|
},
|
|
// table: 66, record
|
|
{
|
|
ID: 4,
|
|
StartKey: "7480000000000000FF425F72C000000000FF0000000000000000FA",
|
|
EndKey: "",
|
|
Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1},
|
|
Peers: []helper.RegionPeer{{ID: 5, StoreID: 1}},
|
|
Leader: helper.RegionPeer{ID: 5, StoreID: 1},
|
|
},
|
|
// table: 66, record + index: 3
|
|
{
|
|
ID: 5,
|
|
StartKey: "7480000000000000FF425F698000000000FF0000030000000000FA",
|
|
EndKey: "7480000000000000FF425F72C000000000FF0000000000000000FA",
|
|
Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1},
|
|
Peers: []helper.RegionPeer{{ID: 6, StoreID: 1}},
|
|
Leader: helper.RegionPeer{ID: 6, StoreID: 1},
|
|
},
|
|
// table: 66, index: 1
|
|
{
|
|
ID: 6,
|
|
StartKey: "7480000000000000FF425F698000000000FF0000010000000000FA",
|
|
EndKey: "7480000000000000FF425F698000000000FF0000020000000000FA",
|
|
Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1},
|
|
Peers: []helper.RegionPeer{{ID: 7, StoreID: 1}},
|
|
Leader: helper.RegionPeer{ID: 7, StoreID: 1},
|
|
},
|
|
// table: 66, index: 2
|
|
{
|
|
ID: 7,
|
|
StartKey: "7480000000000000FF425F698000000000FF0000020000000000FA",
|
|
EndKey: "7480000000000000FF425F698000000000FF0000030000000000FA",
|
|
Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1},
|
|
Peers: []helper.RegionPeer{{ID: 8, StoreID: 1}},
|
|
Leader: helper.RegionPeer{ID: 8, StoreID: 1},
|
|
},
|
|
// merge region 7, 5
|
|
{
|
|
ID: 8,
|
|
StartKey: "7480000000000000FF425F698000000000FF0000020000000000FA",
|
|
EndKey: "7480000000000000FF425F72C000000000FF0000000000000000FA",
|
|
Epoch: helper.RegionEpoch{ConfVer: 1, Version: 1},
|
|
Peers: []helper.RegionPeer{{ID: 9, StoreID: 1}},
|
|
Leader: helper.RegionPeer{ID: 9, StoreID: 1},
|
|
},
|
|
}
|
|
return &helper.RegionsInfo{
|
|
Count: int64(len(regions)),
|
|
Regions: regions,
|
|
}
|
|
}
|
|
|
|
func (s *HelperTestSuite) mockTiKVRegionsInfoResponse(w http.ResponseWriter, req *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
resp := getMockTiKVRegionsInfo()
|
|
data, err := json.MarshalIndent(resp, "", " ")
|
|
if err != nil {
|
|
log.Panic("json marshal failed", zap.Error(err))
|
|
}
|
|
_, err = w.Write(data)
|
|
if err != nil {
|
|
log.Panic("write http response failed", zap.Error(err))
|
|
}
|
|
}
|
|
|
|
func (s *HelperTestSuite) mockStoreStatResponse(w http.ResponseWriter, req *http.Request) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(http.StatusOK)
|
|
startTs, err := time.Parse(time.RFC3339, "2019-04-23T19:30:30+08:00")
|
|
if err != nil {
|
|
log.Panic("mock tikv store api response failed", zap.Error(err))
|
|
}
|
|
lastHeartbeatTs, err := time.Parse(time.RFC3339, "2019-04-23T19:31:30+08:00")
|
|
if err != nil {
|
|
log.Panic("mock tikv store api response failed", zap.Error(err))
|
|
}
|
|
storesStat := helper.StoresStat{
|
|
Count: 1,
|
|
Stores: []helper.StoreStat{
|
|
{
|
|
Store: helper.StoreBaseStat{
|
|
ID: 1,
|
|
Address: "127.0.0.1:20160",
|
|
State: 0,
|
|
StateName: "Up",
|
|
Version: "3.0.0-beta",
|
|
Labels: []helper.StoreLabel{
|
|
{
|
|
Key: "test",
|
|
Value: "test",
|
|
},
|
|
},
|
|
},
|
|
Status: helper.StoreDetailStat{
|
|
Capacity: "60 GiB",
|
|
Available: "100 GiB",
|
|
LeaderCount: 10,
|
|
LeaderWeight: 999999.999999,
|
|
LeaderScore: 999999.999999,
|
|
LeaderSize: 1000,
|
|
RegionCount: 200,
|
|
RegionWeight: 999999.999999,
|
|
RegionScore: 999999.999999,
|
|
RegionSize: 1000,
|
|
StartTs: startTs,
|
|
LastHeartbeatTs: lastHeartbeatTs,
|
|
Uptime: "1h30m",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
data, err := json.MarshalIndent(storesStat, "", " ")
|
|
if err != nil {
|
|
log.Panic("json marshal failed", zap.Error(err))
|
|
}
|
|
_, err = w.Write(data)
|
|
if err != nil {
|
|
log.Panic("write http response failed", zap.Error(err))
|
|
}
|
|
}
|
|
|