Skip to content

Commit e88372a

Browse files
rleungxnolouch
authored andcommitted
Improve the performance of the HTTP API for getting all regions (#1970) (#1986)
* /api/v1/regions optimization (#1970) Signed-off-by: Ryan Leung <[email protected]>
1 parent 7a5909e commit e88372a

File tree

5 files changed

+163
-24
lines changed

5 files changed

+163
-24
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ travis_coverage: export GO111MODULE=on
9999
travis_coverage:
100100
ifeq ("$(TRAVIS_COVERAGE)", "1")
101101
@$(FAILPOINT_ENABLE)
102-
CGO_ENABLED=1 ./scripts/retool do $(OVERALLS) -project=github.com/pingcap/pd -covermode=count -ignore='.git,vendor' -- -coverpkg=./... || { $(FAILPOINT_DISABLE); exit 1; }
102+
CGO_ENABLED=1 ./scripts/retool do $(OVERALLS) -concurrency=8 -project=github.com/pingcap/pd -covermode=count -ignore='.git,vendor' -- -coverpkg=./... || { $(FAILPOINT_DISABLE); exit 1; }
103103
@$(FAILPOINT_DISABLE)
104104
else
105105
@echo "coverage only runs in travis."

server/api/region.go

+28-17
Original file line numberDiff line numberDiff line change
@@ -45,23 +45,29 @@ type RegionInfo struct {
4545

4646
// NewRegionInfo create a new api RegionInfo.
4747
func NewRegionInfo(r *core.RegionInfo) *RegionInfo {
48+
return InitRegion(r, &RegionInfo{})
49+
}
50+
51+
// InitRegion init a new api RegionInfo from the core.RegionInfo.
52+
func InitRegion(r *core.RegionInfo, s *RegionInfo) *RegionInfo {
4853
if r == nil {
4954
return nil
5055
}
51-
return &RegionInfo{
52-
ID: r.GetID(),
53-
StartKey: string(core.HexRegionKey(r.GetStartKey())),
54-
EndKey: string(core.HexRegionKey(r.GetEndKey())),
55-
RegionEpoch: r.GetRegionEpoch(),
56-
Peers: r.GetPeers(),
57-
Leader: r.GetLeader(),
58-
DownPeers: r.GetDownPeers(),
59-
PendingPeers: r.GetPendingPeers(),
60-
WrittenBytes: r.GetBytesWritten(),
61-
ReadBytes: r.GetBytesRead(),
62-
ApproximateSize: r.GetApproximateSize(),
63-
ApproximateKeys: r.GetApproximateKeys(),
64-
}
56+
57+
s.ID = r.GetID()
58+
s.StartKey = core.HexRegionKeyStr(r.GetStartKey())
59+
s.EndKey = core.HexRegionKeyStr(r.GetEndKey())
60+
s.RegionEpoch = r.GetRegionEpoch()
61+
s.Peers = r.GetPeers()
62+
s.Leader = r.GetLeader()
63+
s.DownPeers = r.GetDownPeers()
64+
s.PendingPeers = r.GetPendingPeers()
65+
s.WrittenBytes = r.GetBytesWritten()
66+
s.ReadBytes = r.GetBytesRead()
67+
s.ApproximateSize = r.GetApproximateSize()
68+
s.ApproximateKeys = r.GetApproximateKeys()
69+
70+
return s
6571
}
6672

6773
// RegionsInfo contains some regions with the detailed region info.
@@ -126,13 +132,18 @@ func newRegionsHandler(svr *server.Server, rd *render.Render) *regionsHandler {
126132
}
127133

128134
func convertToAPIRegions(regions []*core.RegionInfo) *RegionsInfo {
129-
regionInfos := make([]*RegionInfo, len(regions))
135+
regionInfos := make([]RegionInfo, len(regions))
136+
regionInfosRefs := make([]*RegionInfo, len(regions))
137+
138+
for i := 0; i < len(regions); i++ {
139+
regionInfosRefs[i] = &regionInfos[i]
140+
}
130141
for i, r := range regions {
131-
regionInfos[i] = NewRegionInfo(r)
142+
regionInfosRefs[i] = InitRegion(r, regionInfosRefs[i])
132143
}
133144
return &RegionsInfo{
134145
Count: len(regions),
135-
Regions: regionInfos,
146+
Regions: regionInfosRefs,
136147
}
137148
}
138149

server/api/region_test.go

+69
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@
1414
package api
1515

1616
import (
17+
"bytes"
1718
"fmt"
1819
"math/rand"
1920
"net/url"
2021
"sort"
22+
"testing"
2123

2224
. "github.com/pingcap/check"
2325
"github.com/pingcap/kvproto/pkg/metapb"
@@ -320,3 +322,70 @@ func (s *testGetRegionSuite) TestScanRegionByKey(c *C) {
320322
c.Assert(v, Equals, regions.Regions[i].ID)
321323
}
322324
}
325+
326+
// Create n regions (0..n) of n stores (0..n).
327+
// Each region contains np peers, the first peer is the leader.
328+
// (copied from server/cluster_test.go)
329+
func newTestRegions() []*core.RegionInfo {
330+
n := uint64(10000)
331+
np := uint64(3)
332+
333+
regions := make([]*core.RegionInfo, 0, n)
334+
for i := uint64(0); i < n; i++ {
335+
peers := make([]*metapb.Peer, 0, np)
336+
for j := uint64(0); j < np; j++ {
337+
peer := &metapb.Peer{
338+
Id: i*np + j,
339+
}
340+
peer.StoreId = (i + j) % n
341+
peers = append(peers, peer)
342+
}
343+
region := &metapb.Region{
344+
Id: i,
345+
Peers: peers,
346+
StartKey: []byte(fmt.Sprintf("%d", i)),
347+
EndKey: []byte(fmt.Sprintf("%d", i+1)),
348+
RegionEpoch: &metapb.RegionEpoch{ConfVer: 2, Version: 2},
349+
}
350+
regions = append(regions, core.NewRegionInfo(region, peers[0]))
351+
}
352+
return regions
353+
}
354+
355+
func BenchmarkRenderJSON(b *testing.B) {
356+
regionInfos := newTestRegions()
357+
rd := createStreamingRender()
358+
regions := convertToAPIRegions(regionInfos)
359+
360+
b.ResetTimer()
361+
for i := 0; i < b.N; i++ {
362+
var buffer bytes.Buffer
363+
rd.JSON(&buffer, 200, regions)
364+
}
365+
}
366+
367+
func BenchmarkConvertToAPIRegions(b *testing.B) {
368+
regionInfos := newTestRegions()
369+
370+
b.ResetTimer()
371+
for i := 0; i < b.N; i++ {
372+
regions := convertToAPIRegions(regionInfos)
373+
_ = regions.Count
374+
}
375+
}
376+
377+
func BenchmarkHexRegionKey(b *testing.B) {
378+
key := []byte("region_number_infinity")
379+
b.ResetTimer()
380+
for i := 0; i < b.N; i++ {
381+
_ = core.HexRegionKey(key)
382+
}
383+
}
384+
385+
func BenchmarkHexRegionKeyStr(b *testing.B) {
386+
key := []byte("region_number_infinity")
387+
b.ResetTimer()
388+
for i := 0; i < b.N; i++ {
389+
_ = core.HexRegionKeyStr(key)
390+
}
391+
}

server/api/router.go

+16-5
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,20 @@ import (
2222
"github.com/unrolled/render"
2323
)
2424

25-
const pingAPI = "/ping"
25+
func createStreamingRender() *render.Render {
26+
return render.New(render.Options{
27+
StreamingJSON: true,
28+
})
29+
}
2630

27-
func createRouter(prefix string, svr *server.Server) *mux.Router {
28-
rd := render.New(render.Options{
31+
func createIndentRender() *render.Render {
32+
return render.New(render.Options{
2933
IndentJSON: true,
3034
})
35+
}
36+
37+
func createRouter(prefix string, svr *server.Server) *mux.Router {
38+
rd := createIndentRender()
3139

3240
router := mux.NewRouter().PathPrefix(prefix).Subrouter()
3341
handler := svr.GetHandler()
@@ -88,8 +96,11 @@ func createRouter(prefix string, svr *server.Server) *mux.Router {
8896
router.HandleFunc("/api/v1/region/id/{id}", regionHandler.GetRegionByID).Methods("GET")
8997
router.HandleFunc("/api/v1/region/key/{key}", regionHandler.GetRegionByKey).Methods("GET")
9098

99+
srd := createStreamingRender()
100+
regionsAllHandler := newRegionsHandler(svr, srd)
101+
router.HandleFunc("/api/v1/regions", regionsAllHandler.GetAll).Methods("GET")
102+
91103
regionsHandler := newRegionsHandler(svr, rd)
92-
router.HandleFunc("/api/v1/regions", regionsHandler.GetAll).Methods("GET")
93104
router.HandleFunc("/api/v1/regions/key", regionsHandler.ScanRegionsByKey).Methods("GET")
94105
router.HandleFunc("/api/v1/regions/store/{id}", regionsHandler.GetStoreRegions).Methods("GET")
95106
router.HandleFunc("/api/v1/regions/writeflow", regionsHandler.GetTopWriteFlow).Methods("GET")
@@ -136,7 +147,7 @@ func createRouter(prefix string, svr *server.Server) *mux.Router {
136147
logHanler := newlogHandler(svr, rd)
137148
router.HandleFunc("/api/v1/admin/log", logHanler.Handle).Methods("POST")
138149

139-
router.HandleFunc(pingAPI, func(w http.ResponseWriter, r *http.Request) {}).Methods("GET")
150+
router.HandleFunc("/ping", func(w http.ResponseWriter, r *http.Request) {}).Methods("GET")
140151
router.Handle("/health", newHealthHandler(svr, rd)).Methods("GET")
141152
router.Handle("/diagnose", newDiagnoseHandler(svr, rd)).Methods("GET")
142153
return router

server/core/region.go

+49-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"math/rand"
2121
"reflect"
2222
"strings"
23+
"unsafe"
2324

2425
"github.com/gogo/protobuf/proto"
2526
"github.com/pingcap/kvproto/pkg/metapb"
@@ -816,10 +817,57 @@ func DiffRegionKeyInfo(origin *RegionInfo, other *RegionInfo) string {
816817
return strings.Join(ret, ", ")
817818
}
818819

820+
// String converts slice of bytes to string without copy.
821+
func String(b []byte) (s string) {
822+
if len(b) == 0 {
823+
return ""
824+
}
825+
pbytes := (*reflect.SliceHeader)(unsafe.Pointer(&b))
826+
pstring := (*reflect.StringHeader)(unsafe.Pointer(&s))
827+
pstring.Data = pbytes.Data
828+
pstring.Len = pbytes.Len
829+
return
830+
}
831+
832+
// ToUpperASCIIInplace bytes.ToUpper but zero-cost
833+
func ToUpperASCIIInplace(s []byte) []byte {
834+
hasLower := false
835+
for i := 0; i < len(s); i++ {
836+
c := s[i]
837+
hasLower = hasLower || ('a' <= c && c <= 'z')
838+
}
839+
840+
if !hasLower {
841+
return s
842+
}
843+
var c byte
844+
for i := 0; i < len(s); i++ {
845+
c = s[i]
846+
if 'a' <= c && c <= 'z' {
847+
c -= 'a' - 'A'
848+
}
849+
s[i] = c
850+
}
851+
return s
852+
}
853+
854+
// EncodeToString overrides hex.EncodeToString implementation. Difference: returns []byte, not string
855+
func EncodeToString(src []byte) []byte {
856+
dst := make([]byte, hex.EncodedLen(len(src)))
857+
hex.Encode(dst, src)
858+
return dst
859+
}
860+
819861
// HexRegionKey converts region key to hex format. Used for formating region in
820862
// logs.
821863
func HexRegionKey(key []byte) []byte {
822-
return []byte(strings.ToUpper(hex.EncodeToString(key)))
864+
return ToUpperASCIIInplace(EncodeToString(key))
865+
}
866+
867+
// HexRegionKeyStr converts region key to hex format. Used for formating region in
868+
// logs.
869+
func HexRegionKeyStr(key []byte) string {
870+
return String(HexRegionKey(key))
823871
}
824872

825873
// RegionToHexMeta converts a region meta's keys to hex format. Used for formating

0 commit comments

Comments
 (0)