From c5bbf6df13b96a0bf3065550423beda81e6c9241 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Mon, 6 Jul 2020 18:57:12 +0800 Subject: [PATCH] cherry pick #2599 to release-4.0 Signed-off-by: ti-srebot --- server/api/region.go | 70 +++++++++++++++++++++++++++++++++++++++ server/api/region_test.go | 16 +++++++++ server/api/router.go | 1 + 3 files changed, 87 insertions(+) diff --git a/server/api/region.go b/server/api/region.go index 7d2b632dcbb..06565c9bd46 100644 --- a/server/api/region.go +++ b/server/api/region.go @@ -15,6 +15,8 @@ package api import ( "container/heap" + "encoding/hex" + "fmt" "net/http" "net/url" "sort" @@ -24,6 +26,7 @@ import ( "github.com/pingcap/kvproto/pkg/metapb" "github.com/pingcap/kvproto/pkg/pdpb" "github.com/pingcap/kvproto/pkg/replication_modepb" + "github.com/pingcap/pd/v4/pkg/apiutil" "github.com/pingcap/pd/v4/server" "github.com/pingcap/pd/v4/server/core" "github.com/unrolled/render" @@ -557,6 +560,73 @@ func (h *regionsHandler) GetTopSize(w http.ResponseWriter, r *http.Request) { }) } +// @Tags region +// @Summary Accelerate regions scheduling a in given range, only receive hex format for keys +// @Accept json +// @Param body body object true "json params" +// @Param limit query integer false "Limit count" default(256) +// @Produce json +// @Success 200 {string} string "Accelerate regions scheduling in a given range[startKey,endKey)" +// @Failure 400 {string} string "The input is invalid." +// @Router /regions/accelerate-schedule [post] +func (h *regionsHandler) AccelerateRegionsScheduleInRange(w http.ResponseWriter, r *http.Request) { + rc := getCluster(r.Context()) + var input map[string]interface{} + if err := apiutil.ReadJSONRespondError(h.rd, w, r.Body, &input); err != nil { + return + } + parseKey := func(name string, input map[string]interface{}) (string, string, error) { + k, ok := input[name] + if !ok { + return "", "", fmt.Errorf("missing %s", name) + } + rawKey, ok := k.(string) + if !ok { + return "", "", fmt.Errorf("bad format %s", name) + } + returned, err := hex.DecodeString(rawKey) + if err != nil { + return "", "", fmt.Errorf("split key %s is not in hex format", name) + } + return string(returned), rawKey, nil + } + + startKey, rawStartKey, err := parseKey("start_key", input) + if err != nil { + h.rd.JSON(w, http.StatusBadRequest, err.Error()) + return + } + + endKey, rawEndKey, err := parseKey("end_key", input) + if err != nil { + h.rd.JSON(w, http.StatusBadRequest, err.Error()) + return + } + + limit := 256 + if limitStr := r.URL.Query().Get("limit"); limitStr != "" { + var err error + limit, err = strconv.Atoi(limitStr) + if err != nil { + h.rd.JSON(w, http.StatusBadRequest, err.Error()) + return + } + } + if limit > maxRegionLimit { + limit = maxRegionLimit + } + + regions := rc.ScanRegions([]byte(startKey), []byte(endKey), limit) + if len(regions) > 0 { + regionsIDList := make([]uint64, 0, len(regions)) + for _, region := range regions { + regionsIDList = append(regionsIDList, region.GetID()) + } + rc.AddSuspectRegions(regionsIDList...) + } + h.rd.Text(w, http.StatusOK, fmt.Sprintf("Accelerate regions scheduling in a given range [%s,%s)", rawStartKey, rawEndKey)) +} + func (h *regionsHandler) GetTopNRegions(w http.ResponseWriter, r *http.Request, less func(a, b *core.RegionInfo) bool) { rc := getCluster(r.Context()) limit := defaultRegionLimit diff --git a/server/api/region_test.go b/server/api/region_test.go index 633d9a7d2de..cc6833877c5 100644 --- a/server/api/region_test.go +++ b/server/api/region_test.go @@ -15,6 +15,7 @@ package api import ( "bytes" + "encoding/hex" "fmt" "math/rand" "net/url" @@ -258,6 +259,21 @@ func (s *testRegionSuite) TestTopSize(c *C) { s.checkTopRegions(c, fmt.Sprintf("%s/regions/size?limit=%d", s.urlPrefix, 2), []uint64{7, 8}) } +func (s *testRegionSuite) TestAccelerateRegionsScheduleInRange(c *C) { + r1 := newTestRegionInfo(557, 13, []byte("a1"), []byte("a2")) + r2 := newTestRegionInfo(558, 14, []byte("a2"), []byte("a3")) + r3 := newTestRegionInfo(559, 15, []byte("a3"), []byte("a4")) + mustRegionHeartbeat(c, s.svr, r1) + mustRegionHeartbeat(c, s.svr, r2) + mustRegionHeartbeat(c, s.svr, r3) + body := fmt.Sprintf(`{"start_key":"%s", "end_key": "%s"}`, hex.EncodeToString([]byte("a1")), hex.EncodeToString([]byte("a3"))) + + err := postJSON(testDialClient, fmt.Sprintf("%s/regions/accelerate-schedule", s.urlPrefix), []byte(body)) + c.Assert(err, IsNil) + idList := s.svr.GetRaftCluster().GetSuspectRegions() + c.Assert(len(idList), Equals, 2) +} + func (s *testRegionSuite) checkTopRegions(c *C, url string, regionIDs []uint64) { regions := &RegionsInfo{} err := readJSON(testDialClient, url, regions) diff --git a/server/api/router.go b/server/api/router.go index b09e7bd629e..a7e08f2d066 100644 --- a/server/api/router.go +++ b/server/api/router.go @@ -148,6 +148,7 @@ func createRouter(ctx context.Context, prefix string, svr *server.Server) *mux.R clusterRouter.HandleFunc("/regions/check/hist-size", regionsHandler.GetSizeHistogram).Methods("GET") clusterRouter.HandleFunc("/regions/check/hist-keys", regionsHandler.GetKeysHistogram).Methods("GET") clusterRouter.HandleFunc("/regions/sibling/{id}", regionsHandler.GetRegionSiblings).Methods("GET") + clusterRouter.HandleFunc("/regions/accelerate-schedule", regionsHandler.AccelerateRegionsScheduleInRange).Methods("POST") apiRouter.Handle("/version", newVersionHandler(rd)).Methods("GET") apiRouter.Handle("/status", newStatusHandler(svr, rd)).Methods("GET")