Skip to content

Commit

Permalink
server: Add table range api to http status server (#20456)
Browse files Browse the repository at this point in the history
Signed-off-by: Xiaoguang Sun <sunxiaoguang@zhihu.com>
  • Loading branch information
sunxiaoguang authored Oct 21, 2020
1 parent 5738191 commit 04d38f5
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 7 deletions.
82 changes: 75 additions & 7 deletions server/http_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,7 @@ type valueHandler struct {

const (
opTableRegions = "regions"
opTableRanges = "ranges"
opTableDiskUsage = "disk-usage"
opTableScatter = "scatter-table"
opStopTableScatter = "stop-scatter-table"
Expand Down Expand Up @@ -521,6 +522,34 @@ type TableRegions struct {
Indices []IndexRegions `json:"indices"`
}

// RangeDetail contains detail information about a particular range
type RangeDetail struct {
StartKey []byte `json:"start_key"`
EndKey []byte `json:"end_key"`
StartKeyHex string `json:"start_key_hex"`
EndKeyHex string `json:"end_key_hex"`
}

func createRangeDetail(start, end []byte) RangeDetail {
return RangeDetail{
StartKey: start,
EndKey: end,
StartKeyHex: hex.EncodeToString(start),
EndKeyHex: hex.EncodeToString(end),
}
}

// TableRanges is the response data for list table's ranges.
// It contains ranges list for record and indices as well as the whole table.
type TableRanges struct {
TableName string `json:"name"`
TableID int64 `json:"id"`
Range RangeDetail `json:"table"`
Record RangeDetail `json:"record"`
Index RangeDetail `json:"index"`
Indices map[string]RangeDetail `json:"indices,omitempty"`
}

// RegionMeta contains a region's peer detail
type RegionMeta struct {
ID uint64 `json:"region_id"`
Expand All @@ -539,10 +568,9 @@ type IndexRegions struct {
// RegionDetail is the response data for get region by ID
// it includes indices and records detail in current region.
type RegionDetail struct {
RegionID uint64 `json:"region_id"`
StartKey []byte `json:"start_key"`
EndKey []byte `json:"end_key"`
Frames []*helper.FrameItem `json:"frames"`
RangeDetail `json:",inline"`
RegionID uint64 `json:"region_id"`
Frames []*helper.FrameItem `json:"frames"`
}

// addTableInRange insert a table into RegionDetail
Expand Down Expand Up @@ -968,6 +996,8 @@ func (h tableHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
switch h.op {
case opTableRegions:
h.handleRegionRequest(schema, tableVal, w, req)
case opTableRanges:
h.handleRangeRequest(schema, tableVal, w, req)
case opTableDiskUsage:
h.handleDiskUsageRequest(tableVal, w)
case opTableScatter:
Expand Down Expand Up @@ -1223,6 +1253,45 @@ func (h tableHandler) handleRegionRequest(schema infoschema.InfoSchema, tbl tabl
writeData(w, tableRegions)
}

func createTableRanges(tblID int64, tblName string, indices []*model.IndexInfo) *TableRanges {
indexPrefix := tablecodec.GenTableIndexPrefix(tblID)
recordPrefix := tablecodec.GenTableRecordPrefix(tblID)
tableEnd := tablecodec.EncodeTablePrefix(tblID + 1)
ranges := &TableRanges{
TableName: tblName,
TableID: tblID,
Range: createRangeDetail(tablecodec.EncodeTablePrefix(tblID), tableEnd),
Record: createRangeDetail(recordPrefix, tableEnd),
Index: createRangeDetail(indexPrefix, recordPrefix),
}
if len(indices) != 0 {
indexRanges := make(map[string]RangeDetail)
for _, index := range indices {
start := tablecodec.EncodeTableIndexPrefix(tblID, index.ID)
end := tablecodec.EncodeTableIndexPrefix(tblID, index.ID+1)
indexRanges[index.Name.String()] = createRangeDetail(start, end)
}
ranges.Indices = indexRanges
}
return ranges
}

func (h tableHandler) handleRangeRequest(schema infoschema.InfoSchema, tbl table.Table, w http.ResponseWriter, req *http.Request) {
meta := tbl.Meta()
pi := meta.GetPartitionInfo()
if pi != nil {
// Partitioned table.
var data []*TableRanges
for _, def := range pi.Definitions {
data = append(data, createTableRanges(def.ID, def.Name.String(), meta.Indices))
}
writeData(w, data)
return
}

writeData(w, createTableRanges(meta.ID, meta.Name.String(), meta.Indices))
}

func (h tableHandler) getRegionsByID(tbl table.Table, id int64, name string) (*TableRegions, error) {
// for record
startKey, endKey := tablecodec.GetTableHandleKeyRange(id)
Expand Down Expand Up @@ -1376,9 +1445,8 @@ func (h regionHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {

// create RegionDetail from RegionFrameRange
regionDetail := &RegionDetail{
RegionID: regionID,
StartKey: region.StartKey,
EndKey: region.EndKey,
RegionID: regionID,
RangeDetail: createRangeDetail(region.StartKey, region.EndKey),
}
schema, err := h.schema()
if err != nil {
Expand Down
43 changes: 43 additions & 0 deletions server/http_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,25 @@ func (ts *HTTPHandlerTestSuite) TestRegionsAPIForClusterIndex(c *C) {
}
}

func (ts *HTTPHandlerTestSuite) TestRangesAPI(c *C) {
ts.startServer(c)
defer ts.stopServer(c)
ts.prepareData(c)
resp, err := ts.fetchStatus("/tables/tidb/t/ranges")
c.Assert(err, IsNil)
c.Assert(resp.StatusCode, Equals, http.StatusOK)
defer resp.Body.Close()
decoder := json.NewDecoder(resp.Body)

var data TableRanges
err = decoder.Decode(&data)
c.Assert(err, IsNil)
c.Assert(data.TableName, Equals, "t")
c.Assert(len(data.Indices), Equals, 1)
_, ok := data.Indices["PRIMARY"]
c.Assert(ok, IsTrue)
}

func (ts *HTTPHandlerTestSuite) regionContainsTable(c *C, regionID uint64, tableID int64) bool {
resp, err := ts.fetchStatus(fmt.Sprintf("/regions/%d", regionID))
c.Assert(err, IsNil)
Expand Down Expand Up @@ -320,6 +339,30 @@ func (ts *HTTPHandlerTestSuite) TestListTableRegions(c *C) {
c.Assert(err, IsNil)
}

func (ts *HTTPHandlerTestSuite) TestListTableRanges(c *C) {
ts.startServer(c)
defer ts.stopServer(c)
ts.prepareData(c)
// Test list table regions with error
resp, err := ts.fetchStatus("/tables/fdsfds/aaa/ranges")
c.Assert(err, IsNil)
defer resp.Body.Close()
c.Assert(resp.StatusCode, Equals, http.StatusBadRequest)

resp, err = ts.fetchStatus("/tables/tidb/pt/ranges")
c.Assert(err, IsNil)
defer resp.Body.Close()

var data []*TableRanges
dec := json.NewDecoder(resp.Body)
err = dec.Decode(&data)
c.Assert(err, IsNil)
c.Assert(len(data), Equals, 3)
for i, partition := range data {
c.Assert(partition.TableName, Equals, fmt.Sprintf("p%d", i))
}
}

func (ts *HTTPHandlerTestSuite) TestGetRegionByIDWithError(c *C) {
ts.startServer(c)
defer ts.stopServer(c)
Expand Down
1 change: 1 addition & 0 deletions server/http_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ func (s *Server) startHTTPServer() {
if s.cfg.Store == "tikv" {
// HTTP path for tikv.
router.Handle("/tables/{db}/{table}/regions", tableHandler{tikvHandlerTool, opTableRegions})
router.Handle("/tables/{db}/{table}/ranges", tableHandler{tikvHandlerTool, opTableRanges})
router.Handle("/tables/{db}/{table}/scatter", tableHandler{tikvHandlerTool, opTableScatter})
router.Handle("/tables/{db}/{table}/stop-scatter", tableHandler{tikvHandlerTool, opStopTableScatter})
router.Handle("/tables/{db}/{table}/disk-usage", tableHandler{tikvHandlerTool, opTableDiskUsage})
Expand Down

0 comments on commit 04d38f5

Please sign in to comment.