Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

infoschema: add information_schema.tidb_cluster_info to retrieve cluster topology. #13035

Merged
merged 10 commits into from
Nov 4, 2019
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ require (
github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8
github.com/pingcap/errors v0.11.4
github.com/pingcap/failpoint v0.0.0-20190512135322-30cc7431d99c
github.com/pingcap/fn v0.0.0-20191016082858-07623b84a47d
github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e
github.com/pingcap/kvproto v0.0.0-20191025022903-62abb760d9b1
github.com/pingcap/log v0.0.0-20191012051959-b742a5d432e9
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pingcap/failpoint v0.0.0-20190512135322-30cc7431d99c h1:hvQd3aOLKLF7xvRV6DzvPkKY4QXzfVbjU1BhW0d9yL8=
github.com/pingcap/failpoint v0.0.0-20190512135322-30cc7431d99c/go.mod h1:DNS3Qg7bEDhU6EXNHF+XSv/PGznQaMJ5FWvctpm6pQI=
github.com/pingcap/fn v0.0.0-20191016082858-07623b84a47d h1:rCmRK0lCRrHMUbS99BKFYhK9YxJDNw0xB033cQbYo0s=
github.com/pingcap/fn v0.0.0-20191016082858-07623b84a47d/go.mod h1:fMRU1BA1y+r89AxUoaAar4JjrhUkVDt0o0Np6V8XbDQ=
github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e h1:P73/4dPCL96rGrobssy1nVy2VaVpNCuLpCbr+FEaTA8=
github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e/go.mod h1:O17XtbryoCJhkKGbT62+L2OlrniwqiGLSqrmdHCMzZw=
github.com/pingcap/kvproto v0.0.0-20190822090350-11ea838aedf7/go.mod h1:QMdbTAXCHzzygQzqcG9uVUgU2fKeSN1GmfMiykdSzzY=
Expand Down
124 changes: 124 additions & 0 deletions infoschema/tables.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,18 @@ import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"sort"
"strings"
"sync"
"time"

"github.com/pingcap/errors"
"github.com/pingcap/parser/charset"
"github.com/pingcap/parser/model"
"github.com/pingcap/parser/mysql"
"github.com/pingcap/parser/terror"
"github.com/pingcap/tidb/domain/infosync"
"github.com/pingcap/tidb/kv"
"github.com/pingcap/tidb/meta/autoid"
Expand Down Expand Up @@ -84,6 +88,7 @@ const (
tableTiKVRegionStatus = "TIKV_REGION_STATUS"
tableTiKVRegionPeers = "TIKV_REGION_PEERS"
tableTiDBServersInfo = "TIDB_SERVERS_INFO"
tableTiDBClusterInfo = "TIDB_CLUSTER_INFO"
)

type columnInfo struct {
Expand Down Expand Up @@ -1013,6 +1018,16 @@ var filesCols = []columnInfo{
{"EXTRA", mysql.TypeVarchar, 255, 0, nil, nil},
}

var tableTiDBClusterInfoCols = []columnInfo{
{"ID", mysql.TypeLonglong, 21, 0, nil, nil},
{"TYPE", mysql.TypeVarchar, 64, 0, nil, nil},
{"NAME", mysql.TypeVarchar, 64, 0, nil, nil},
{"ADDRESS", mysql.TypeVarchar, 64, 0, nil, nil},
{"STATUS_ADDRESS", mysql.TypeVarchar, 64, 0, nil, nil},
{"VERSION", mysql.TypeVarchar, 64, 0, nil, nil},
{"GIT_HASH", mysql.TypeVarchar, 64, 0, nil, nil},
}

func dataForSchemata(schemas []*model.DBInfo) [][]types.Datum {

rows := make([][]types.Datum, 0, len(schemas))
Expand Down Expand Up @@ -1828,6 +1843,112 @@ func dataForServersInfo() ([][]types.Datum, error) {
return rows, nil
}

func dataForTiDBClusterInfo(ctx sessionctx.Context) ([][]types.Datum, error) {
// get TiDB servers info.
lonng marked this conversation as resolved.
Show resolved Hide resolved
tidbNodes, err := infosync.GetAllServerInfo(context.Background())
if err != nil {
return nil, errors.Trace(err)
}

rows := make([][]types.Datum, 0, len(tidbNodes))
for _, node := range tidbNodes {
tp := "tidb"
name := fmt.Sprintf("%s-%d", tp, len(rows))
addr := fmt.Sprintf("%s:%d", node.IP, node.Port)
statusAddr := fmt.Sprintf("%s:%d", node.IP, node.StatusPort)
row := types.MakeDatums(
len(rows)+1,
tp,
name,
addr,
statusAddr,
node.Version,
node.GitHash,
)
rows = append(rows, row)
}

// get PD servers info.
store := ctx.GetStore()
etcd, ok := store.(tikv.EtcdBackend)
if !ok {
return nil, errors.Errorf("%T not an etcd backend", store)
}
for i, addr := range etcd.EtcdAddrs() {
addr = strings.TrimSpace(addr)

// get pd version
lonng marked this conversation as resolved.
Show resolved Hide resolved
url := fmt.Sprintf("http://%s/pd/api/v1/config/cluster-version", addr)
lonng marked this conversation as resolved.
Show resolved Hide resolved
lonng marked this conversation as resolved.
Show resolved Hide resolved
resp, err := http.Get(url)
if err != nil {
return nil, errors.Trace(err)
}
pdVersion, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, errors.Trace(err)
}
terror.Log(resp.Body.Close())
Copy link
Contributor

@djshow832 djshow832 Nov 1, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this way, it always calls an extra function.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

terror.Log it a utility function equals if err != nil {...}.

version := strings.Trim(strings.Trim(string(pdVersion), "\n"), "\"")

// get pd git_hash
url = fmt.Sprintf("http://%s/pd/api/v1/status", addr)
lonng marked this conversation as resolved.
Show resolved Hide resolved
resp, err = http.Get(url)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The request will always handle by PD leader, should also change PD?
image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch, seems PD should not redirect all request to leader.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if err != nil {
return nil, errors.Trace(err)
}
var content = struct {
GitHash string `json:"git_hash"`
}{}
if err := json.NewDecoder(resp.Body).Decode(&content); err != nil {
return nil, errors.Trace(err)
}
terror.Log(resp.Body.Close())

tp := "pd"
name := fmt.Sprintf("%s-%d", tp, i)
row := types.MakeDatums(
len(rows)+1,
tp,
name,
addr,
addr,
version,
content.GitHash,
)
rows = append(rows, row)
}

// get TiKV servers info.
tikvStore, ok := store.(tikv.Storage)
if !ok {
return nil, errors.Errorf("%T is not an TiKV store instance", store)
}
tikvHelper := &helper.Helper{
Store: tikvStore,
RegionCache: tikvStore.GetRegionCache(),
}

storesStat, err := tikvHelper.GetStoresStat()
if err != nil {
return nil, errors.Trace(err)
}
for i, storeStat := range storesStat.Stores {
tp := "tikv"
name := fmt.Sprintf("%s-%d", tp, i)
row := types.MakeDatums(
len(rows)+1,
tp,
name,
storeStat.Store.Address,
storeStat.Store.StatusAddress,
storeStat.Store.Version,
storeStat.Store.GitHash,
)
rows = append(rows, row)
}
return rows, nil
}

var tableNameToColumns = map[string][]columnInfo{
tableSchemata: schemataCols,
tableTables: tablesCols,
Expand Down Expand Up @@ -1869,6 +1990,7 @@ var tableNameToColumns = map[string][]columnInfo{
tableTiKVRegionStatus: tableTiKVRegionStatusCols,
tableTiKVRegionPeers: tableTiKVRegionPeersCols,
tableTiDBServersInfo: tableTiDBServersInfoCols,
tableTiDBClusterInfo: tableTiDBClusterInfoCols,
}

func createInfoSchemaTable(handle *Handle, meta *model.TableInfo) *infoschemaTable {
Expand Down Expand Up @@ -1974,6 +2096,8 @@ func (it *infoschemaTable) getRows(ctx sessionctx.Context, cols []*table.Column)
fullRows, err = dataForTikVRegionPeers(ctx)
case tableTiDBServersInfo:
fullRows, err = dataForServersInfo()
case tableTiDBClusterInfo:
fullRows, err = dataForTiDBClusterInfo(ctx)
}
if err != nil {
return nil, err
Expand Down
67 changes: 67 additions & 0 deletions infoschema/tables_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,17 @@ package infoschema_test

import (
"context"
"crypto/tls"
"fmt"
"net/http/httptest"
"os"
"strconv"
"strings"
"time"

"github.com/gorilla/mux"
. "github.com/pingcap/check"
"github.com/pingcap/fn"
"github.com/pingcap/parser/auth"
"github.com/pingcap/parser/model"
"github.com/pingcap/parser/mysql"
Expand All @@ -33,8 +37,11 @@ import (
"github.com/pingcap/tidb/session"
"github.com/pingcap/tidb/statistics"
"github.com/pingcap/tidb/statistics/handle"
"github.com/pingcap/tidb/store/helper"
"github.com/pingcap/tidb/store/mockstore"
"github.com/pingcap/tidb/store/tikv"
"github.com/pingcap/tidb/util"
"github.com/pingcap/tidb/util/pdapi"
"github.com/pingcap/tidb/util/testkit"
"github.com/pingcap/tidb/util/testleak"
"github.com/pingcap/tidb/util/testutil"
Expand Down Expand Up @@ -566,6 +573,66 @@ func (s *testTableSuite) TestColumnStatistics(c *C) {
tk.MustQuery("select * from information_schema.column_statistics").Check(testkit.Rows())
}

type mockStore struct {
tikv.Storage
host string
}

func (s *mockStore) EtcdAddrs() []string { return []string{s.host} }
func (s *mockStore) TLSConfig() *tls.Config { panic("not implemented") }
func (s *mockStore) StartGCWorker() error { panic("not implemented") }

func (s *testTableSuite) TestTiDBClusterInfo(c *C) {
tk := testkit.NewTestKit(c, s.store)
err := tk.QueryToErr("select * from information_schema.tidb_cluster_info")
c.Assert(err, NotNil)
// mocktikv cannot retrieve cluster info
c.Assert(err.Error(), Equals, "pd unavailable")

// mock PD http server
router := mux.NewRouter()
server := httptest.NewServer(router)
defer server.Close()
// mock store stats stat
router.Handle(pdapi.Stores, fn.Wrap(func() (*helper.StoresStat, error) {
return &helper.StoresStat{
Count: 1,
Stores: []helper.StoreStat{
{
Store: helper.StoreBaseStat{
ID: 1,
Address: "127.0.0.1:20160",
State: 0,
StateName: "Up",
Version: "4.0.0-alpha",
StatusAddress: "127.0.0.1:20160",
GitHash: "mock-tikv-githash",
},
},
},
}, nil
}))
// mock PD API
router.Handle(pdapi.ClusterVersion, fn.Wrap(func() (string, error) { return "4.0.0-alpha", nil }))
router.Handle(pdapi.Status, fn.Wrap(func() (interface{}, error) {
return struct {
GitHash string `json:"git_hash"`
}{GitHash: "mock-pd-githash"}, nil
}))

pdAddr := strings.TrimPrefix(server.URL, "http://")
store := &mockStore{
s.store.(tikv.Storage),
pdAddr,
}
tk = testkit.NewTestKit(c, store)
tk.MustQuery("select * from information_schema.tidb_cluster_info").Check(testkit.Rows(
"1 tidb tidb-0 :4000 :10080 5.7.25-TiDB-None None",
"2 pd pd-0 "+pdAddr+" "+pdAddr+" 4.0.0-alpha mock-pd-githash",
"3 tikv tikv-0 127.0.0.1:20160 127.0.0.1:20160 4.0.0-alpha mock-tikv-githash",
))
}

func (s *testTableSuite) TestReloadDropDatabase(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("create database test_dbs")
Expand Down
14 changes: 8 additions & 6 deletions store/helper/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -638,12 +638,14 @@ type StoreStat struct {

// StoreBaseStat stores the basic information of one store.
type StoreBaseStat struct {
ID int64 `json:"id"`
Address string `json:"address"`
State int64 `json:"state"`
StateName string `json:"state_name"`
Version string `json:"version"`
Labels []StoreLabel `json:"labels"`
ID int64 `json:"id"`
Address string `json:"address"`
State int64 `json:"state"`
StateName string `json:"state_name"`
Version string `json:"version"`
Labels []StoreLabel `json:"labels"`
StatusAddress string `json:"status_address"`
GitHash string `json:"git_hash"`
}

// StoreLabel stores the information of one store label.
Expand Down
2 changes: 1 addition & 1 deletion store/helper/helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func (s *HelperTestSuite) TestTiKVStoresStat(c *C) {
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":{"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"}}]}`)
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":""},"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) {
Expand Down
12 changes: 7 additions & 5 deletions util/pdapi/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@ package pdapi

// The following constants are the APIs of PD server.
const (
HotRead = "/pd/api/v1/hotspot/regions/read"
HotWrite = "/pd/api/v1/hotspot/regions/write"
Regions = "/pd/api/v1/regions"
RegionByID = "/pd/api/v1//region/id/"
Stores = "/pd/api/v1/stores"
HotRead = "/pd/api/v1/hotspot/regions/read"
HotWrite = "/pd/api/v1/hotspot/regions/write"
Regions = "/pd/api/v1/regions"
RegionByID = "/pd/api/v1//region/id/"
Stores = "/pd/api/v1/stores"
ClusterVersion = "/pd/api/v1/config/cluster-version"
Status = "/pd/api/v1/status"
)