From 949d15b3a00d84c9a8add94d3772891be8d9b6ae Mon Sep 17 00:00:00 2001 From: nolouch Date: Fri, 19 Apr 2024 16:04:33 +0800 Subject: [PATCH 1/3] server/api: add the api the show the regions in subtree by type Signed-off-by: nolouch --- pkg/core/region.go | 51 ++++++++++++++++++++ pkg/response/store.go | 2 + server/api/region.go | 7 ++- server/cluster/cluster.go | 5 ++ tools/pd-ctl/pdctl/command/region_command.go | 21 ++++++++ tools/pd-ctl/tests/region/region_test.go | 7 ++- 6 files changed, 91 insertions(+), 2 deletions(-) diff --git a/pkg/core/region.go b/pkg/core/region.go index 41bfb4d31ad..0aff9ac5e2f 100644 --- a/pkg/core/region.go +++ b/pkg/core/region.go @@ -1368,6 +1368,57 @@ func (r *RegionsInfo) GetStoreRegions(storeID uint64) []*RegionInfo { return regions } +// SubTreeRegionType is the type of sub tree region. +type SubTreeRegionType int + +var ( + // AllInSubTree is all sub trees. + AllInSubTree SubTreeRegionType = 0 + // LeaderInSubTree is the leader sub tree. + LeaderInSubTree SubTreeRegionType = 1 + // FollowerInSubTree is the follower sub tree. + FollowerInSubTree SubTreeRegionType = 2 + // LearnerInSubTree is the learner sub tree. + LearnerInSubTree SubTreeRegionType = 3 + // WitnessInSubTree is the witness sub tree. + WitnessInSubTree SubTreeRegionType = 4 + // PendingPeerInSubTree is the pending peer sub tree. + PendingPeerInSubTree SubTreeRegionType = 5 +) + +// GetStoreRegions gets all RegionInfo with a given storeID +func (r *RegionsInfo) GetStoreRegionsByTypeInSubTree(storeID uint64, typ SubTreeRegionType) []*RegionInfo { + r.st.RLock() + var regions []*RegionInfo + switch typ { + case LeaderInSubTree: + if leaders, ok := r.leaders[storeID]; ok { + regions = leaders.scanRanges() + } + case FollowerInSubTree: + if followers, ok := r.followers[storeID]; ok { + regions = followers.scanRanges() + } + case LearnerInSubTree: + if learners, ok := r.learners[storeID]; ok { + regions = learners.scanRanges() + } + case WitnessInSubTree: + if witnesses, ok := r.witnesses[storeID]; ok { + regions = witnesses.scanRanges() + } + case PendingPeerInSubTree: + if pendingPeers, ok := r.pendingPeers[storeID]; ok { + regions = pendingPeers.scanRanges() + } + case AllInSubTree: + r.st.RUnlock() + return r.GetStoreRegions(storeID) + } + r.st.RUnlock() + return regions +} + // GetStoreLeaderRegionSize get total size of store's leader regions func (r *RegionsInfo) GetStoreLeaderRegionSize(storeID uint64) int64 { r.st.RLock() diff --git a/pkg/response/store.go b/pkg/response/store.go index 1efe11bfb39..8bff1e75e42 100644 --- a/pkg/response/store.go +++ b/pkg/response/store.go @@ -64,6 +64,7 @@ type StoreStatus struct { RegionSize int64 `json:"region_size"` LearnerCount int `json:"learner_count,omitempty"` WitnessCount int `json:"witness_count,omitempty"` + PendingPeerCount int `json:"pending_peer_count,omitempty"` SlowScore uint64 `json:"slow_score,omitempty"` SlowTrend *SlowTrend `json:"slow_trend,omitempty"` SendingSnapCount uint32 `json:"sending_snap_count,omitempty"` @@ -117,6 +118,7 @@ func BuildStoreInfo(opt *sc.ScheduleConfig, store *core.StoreInfo) *StoreInfo { SlowTrend: slowTrend, SendingSnapCount: store.GetSendingSnapCount(), ReceivingSnapCount: store.GetReceivingSnapCount(), + PendingPeerCount: store.GetPendingPeerCount(), IsBusy: store.IsBusy(), }, } diff --git a/server/api/region.go b/server/api/region.go index dac92f247ca..dc466bd33c7 100644 --- a/server/api/region.go +++ b/server/api/region.go @@ -218,7 +218,12 @@ func (h *regionsHandler) GetStoreRegions(w http.ResponseWriter, r *http.Request) h.rd.JSON(w, http.StatusBadRequest, err.Error()) return } - regions := rc.GetStoreRegions(uint64(id)) + // get type from query + typID, err := strconv.Atoi(r.URL.Query().Get("type")) + if err != nil { + typID = int(core.AllInSubTree) + } + regions := rc.GetStoreRegionsByTypeInSubTree(uint64(id), core.SubTreeRegionType(typID)) b, err := response.MarshalRegionsInfoJSON(r.Context(), regions) if err != nil { h.rd.JSON(w, http.StatusInternalServerError, err.Error()) diff --git a/server/cluster/cluster.go b/server/cluster/cluster.go index dd59b63240f..e5a73b198ba 100644 --- a/server/cluster/cluster.go +++ b/server/cluster/cluster.go @@ -1204,6 +1204,11 @@ func (c *RaftCluster) GetStoreRegions(storeID uint64) []*core.RegionInfo { return c.core.GetStoreRegions(storeID) } +// GetStoreRegions returns all regions' information with a given storeID. +func (c *RaftCluster) GetStoreRegionsByType(storeID uint64) []*core.RegionInfo { + return c.core.GetStoreRegions(storeID) +} + // RandLeaderRegions returns some random regions that has leader on the store. func (c *RaftCluster) RandLeaderRegions(storeID uint64, ranges []core.KeyRange) []*core.RegionInfo { return c.core.RandLeaderRegions(storeID, ranges) diff --git a/tools/pd-ctl/pdctl/command/region_command.go b/tools/pd-ctl/pdctl/command/region_command.go index d7e19967c7a..3a09601eb1d 100644 --- a/tools/pd-ctl/pdctl/command/region_command.go +++ b/tools/pd-ctl/pdctl/command/region_command.go @@ -486,6 +486,7 @@ func NewRegionWithStoreCommand() *cobra.Command { Short: "show the regions of a specific store", Run: showRegionWithStoreCommandFunc, } + r.Flags().String("type", "all", "the type of the regions, could be 'all', 'leader', 'learner' or 'pending'") return r } @@ -496,6 +497,26 @@ func showRegionWithStoreCommandFunc(cmd *cobra.Command, args []string) { } storeID := args[0] prefix := regionsStorePrefix + "/" + storeID + flag := cmd.Flag("type") + typ := 0 + switch flag.Value.String() { + case "all": + typ = 0 + case "leader": + typ = 1 + case "follower": + typ = 2 + case "learner": + typ = 3 + case "witness": + typ = 4 + case "pending": + typ = 5 + default: + cmd.Println("unknown type") + return + } + prefix += "?type=" + strconv.Itoa(typ) r, err := doRequest(cmd, prefix, http.MethodGet, http.Header{}) if err != nil { cmd.Printf("Failed to get regions with the given storeID: %s\n", err) diff --git a/tools/pd-ctl/tests/region/region_test.go b/tools/pd-ctl/tests/region/region_test.go index b328fd88286..109432ca9fa 100644 --- a/tools/pd-ctl/tests/region/region_test.go +++ b/tools/pd-ctl/tests/region/region_test.go @@ -118,7 +118,12 @@ func TestRegion(t *testing.T) { {[]string{"region", "sibling", "2"}, leaderServer.GetAdjacentRegions(leaderServer.GetRegionInfoByID(2))}, // region store command {[]string{"region", "store", "1"}, leaderServer.GetStoreRegions(1)}, - {[]string{"region", "store", "1"}, []*core.RegionInfo{r1, r2, r3, r4}}, + {[]string{"region", "store", "1", "--type=leader"}, leaderServer.GetRaftCluster().GetStoreRegionsByTypeInSubTree(1, core.LeaderInSubTree)}, + {[]string{"region", "store", "1", "--type=follower"}, leaderServer.GetRaftCluster().GetStoreRegionsByTypeInSubTree(1, core.FollowerInSubTree)}, + {[]string{"region", "store", "1", "--type=learner"}, leaderServer.GetRaftCluster().GetStoreRegionsByTypeInSubTree(1, core.LearnerInSubTree)}, + {[]string{"region", "store", "1", "--type=witness"}, leaderServer.GetRaftCluster().GetStoreRegionsByTypeInSubTree(1, core.WitnessInSubTree)}, + {[]string{"region", "store", "1", "--type=pending"}, leaderServer.GetRaftCluster().GetStoreRegionsByTypeInSubTree(1, core.PendingPeerInSubTree)}, + {[]string{"region", "store", "1", "--type=all"}, []*core.RegionInfo{r1, r2, r3, r4}}, // region check extra-peer command {[]string{"region", "check", "extra-peer"}, []*core.RegionInfo{r1}}, // region check miss-peer command From 95099af0188a27469621f5f75d0f63eae1655b17 Mon Sep 17 00:00:00 2001 From: nolouch Date: Mon, 29 Apr 2024 16:12:26 +0800 Subject: [PATCH 2/3] address Signed-off-by: nolouch --- pkg/core/region.go | 23 +++++++++++++---------- server/api/region.go | 6 +++++- tools/pd-ctl/tests/region/region_test.go | 15 ++++++++++----- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/pkg/core/region.go b/pkg/core/region.go index 0aff9ac5e2f..d6aa9d8df8a 100644 --- a/pkg/core/region.go +++ b/pkg/core/region.go @@ -1371,23 +1371,23 @@ func (r *RegionsInfo) GetStoreRegions(storeID uint64) []*RegionInfo { // SubTreeRegionType is the type of sub tree region. type SubTreeRegionType int -var ( +const ( // AllInSubTree is all sub trees. - AllInSubTree SubTreeRegionType = 0 + AllInSubTree SubTreeRegionType = iota // LeaderInSubTree is the leader sub tree. - LeaderInSubTree SubTreeRegionType = 1 + LeaderInSubTree // FollowerInSubTree is the follower sub tree. - FollowerInSubTree SubTreeRegionType = 2 + FollowerInSubTree // LearnerInSubTree is the learner sub tree. - LearnerInSubTree SubTreeRegionType = 3 + LearnerInSubTree // WitnessInSubTree is the witness sub tree. - WitnessInSubTree SubTreeRegionType = 4 + WitnessInSubTree // PendingPeerInSubTree is the pending peer sub tree. - PendingPeerInSubTree SubTreeRegionType = 5 + PendingPeerInSubTree ) // GetStoreRegions gets all RegionInfo with a given storeID -func (r *RegionsInfo) GetStoreRegionsByTypeInSubTree(storeID uint64, typ SubTreeRegionType) []*RegionInfo { +func (r *RegionsInfo) GetStoreRegionsByTypeInSubTree(storeID uint64, typ SubTreeRegionType) ([]*RegionInfo, error) { r.st.RLock() var regions []*RegionInfo switch typ { @@ -1413,10 +1413,13 @@ func (r *RegionsInfo) GetStoreRegionsByTypeInSubTree(storeID uint64, typ SubTree } case AllInSubTree: r.st.RUnlock() - return r.GetStoreRegions(storeID) + return r.GetStoreRegions(storeID), nil + default: + return nil, errors.Errorf("unknown sub tree region type %v", typ) } + r.st.RUnlock() - return regions + return regions, nil } // GetStoreLeaderRegionSize get total size of store's leader regions diff --git a/server/api/region.go b/server/api/region.go index dc466bd33c7..2f1d07988c4 100644 --- a/server/api/region.go +++ b/server/api/region.go @@ -223,7 +223,11 @@ func (h *regionsHandler) GetStoreRegions(w http.ResponseWriter, r *http.Request) if err != nil { typID = int(core.AllInSubTree) } - regions := rc.GetStoreRegionsByTypeInSubTree(uint64(id), core.SubTreeRegionType(typID)) + regions, err := rc.GetStoreRegionsByTypeInSubTree(uint64(id), core.SubTreeRegionType(typID)) + if err != nil { + h.rd.JSON(w, http.StatusBadRequest, err.Error()) + return + } b, err := response.MarshalRegionsInfoJSON(r.Context(), regions) if err != nil { h.rd.JSON(w, http.StatusInternalServerError, err.Error()) diff --git a/tools/pd-ctl/tests/region/region_test.go b/tools/pd-ctl/tests/region/region_test.go index 109432ca9fa..2952e137f3b 100644 --- a/tools/pd-ctl/tests/region/region_test.go +++ b/tools/pd-ctl/tests/region/region_test.go @@ -108,6 +108,11 @@ func TestRegion(t *testing.T) { ) defer cluster.Destroy() + getRegionsByType := func(storeID uint64, regionType core.SubTreeRegionType) []*core.RegionInfo { + regions, _ := leaderServer.GetRaftCluster().GetStoreRegionsByTypeInSubTree(storeID, regionType) + return regions + } + var testRegionsCases = []struct { args []string expect []*core.RegionInfo @@ -118,11 +123,11 @@ func TestRegion(t *testing.T) { {[]string{"region", "sibling", "2"}, leaderServer.GetAdjacentRegions(leaderServer.GetRegionInfoByID(2))}, // region store command {[]string{"region", "store", "1"}, leaderServer.GetStoreRegions(1)}, - {[]string{"region", "store", "1", "--type=leader"}, leaderServer.GetRaftCluster().GetStoreRegionsByTypeInSubTree(1, core.LeaderInSubTree)}, - {[]string{"region", "store", "1", "--type=follower"}, leaderServer.GetRaftCluster().GetStoreRegionsByTypeInSubTree(1, core.FollowerInSubTree)}, - {[]string{"region", "store", "1", "--type=learner"}, leaderServer.GetRaftCluster().GetStoreRegionsByTypeInSubTree(1, core.LearnerInSubTree)}, - {[]string{"region", "store", "1", "--type=witness"}, leaderServer.GetRaftCluster().GetStoreRegionsByTypeInSubTree(1, core.WitnessInSubTree)}, - {[]string{"region", "store", "1", "--type=pending"}, leaderServer.GetRaftCluster().GetStoreRegionsByTypeInSubTree(1, core.PendingPeerInSubTree)}, + {[]string{"region", "store", "1", "--type=leader"}, getRegionsByType(1, core.LeaderInSubTree)}, + {[]string{"region", "store", "1", "--type=follower"}, getRegionsByType(1, core.FollowerInSubTree)}, + {[]string{"region", "store", "1", "--type=learner"}, getRegionsByType(1, core.LearnerInSubTree)}, + {[]string{"region", "store", "1", "--type=witness"}, getRegionsByType(1, core.WitnessInSubTree)}, + {[]string{"region", "store", "1", "--type=pending"}, getRegionsByType(1, core.PendingPeerInSubTree)}, {[]string{"region", "store", "1", "--type=all"}, []*core.RegionInfo{r1, r2, r3, r4}}, // region check extra-peer command {[]string{"region", "check", "extra-peer"}, []*core.RegionInfo{r1}}, From 74c04245f962ed5631d3294fadce7776ec7c500e Mon Sep 17 00:00:00 2001 From: nolouch Date: Mon, 29 Apr 2024 18:17:35 +0800 Subject: [PATCH 3/3] address Signed-off-by: nolouch --- pkg/core/region.go | 14 ++++++------- server/api/region.go | 9 ++++---- tools/pd-ctl/pdctl/command/region_command.go | 22 ++------------------ 3 files changed, 14 insertions(+), 31 deletions(-) diff --git a/pkg/core/region.go b/pkg/core/region.go index d6aa9d8df8a..72b0e2c3a24 100644 --- a/pkg/core/region.go +++ b/pkg/core/region.go @@ -1369,21 +1369,21 @@ func (r *RegionsInfo) GetStoreRegions(storeID uint64) []*RegionInfo { } // SubTreeRegionType is the type of sub tree region. -type SubTreeRegionType int +type SubTreeRegionType string const ( // AllInSubTree is all sub trees. - AllInSubTree SubTreeRegionType = iota + AllInSubTree SubTreeRegionType = "all" // LeaderInSubTree is the leader sub tree. - LeaderInSubTree + LeaderInSubTree SubTreeRegionType = "leader" // FollowerInSubTree is the follower sub tree. - FollowerInSubTree + FollowerInSubTree SubTreeRegionType = "follower" // LearnerInSubTree is the learner sub tree. - LearnerInSubTree + LearnerInSubTree SubTreeRegionType = "learner" // WitnessInSubTree is the witness sub tree. - WitnessInSubTree + WitnessInSubTree SubTreeRegionType = "witness" // PendingPeerInSubTree is the pending peer sub tree. - PendingPeerInSubTree + PendingPeerInSubTree SubTreeRegionType = "pending" ) // GetStoreRegions gets all RegionInfo with a given storeID diff --git a/server/api/region.go b/server/api/region.go index 2f1d07988c4..974b5e4fa12 100644 --- a/server/api/region.go +++ b/server/api/region.go @@ -219,11 +219,12 @@ func (h *regionsHandler) GetStoreRegions(w http.ResponseWriter, r *http.Request) return } // get type from query - typID, err := strconv.Atoi(r.URL.Query().Get("type")) - if err != nil { - typID = int(core.AllInSubTree) + typ := r.URL.Query().Get("type") + if len(typ) == 0 { + typ = string(core.AllInSubTree) } - regions, err := rc.GetStoreRegionsByTypeInSubTree(uint64(id), core.SubTreeRegionType(typID)) + + regions, err := rc.GetStoreRegionsByTypeInSubTree(uint64(id), core.SubTreeRegionType(typ)) if err != nil { h.rd.JSON(w, http.StatusBadRequest, err.Error()) return diff --git a/tools/pd-ctl/pdctl/command/region_command.go b/tools/pd-ctl/pdctl/command/region_command.go index 3a09601eb1d..3536b01a606 100644 --- a/tools/pd-ctl/pdctl/command/region_command.go +++ b/tools/pd-ctl/pdctl/command/region_command.go @@ -497,26 +497,8 @@ func showRegionWithStoreCommandFunc(cmd *cobra.Command, args []string) { } storeID := args[0] prefix := regionsStorePrefix + "/" + storeID - flag := cmd.Flag("type") - typ := 0 - switch flag.Value.String() { - case "all": - typ = 0 - case "leader": - typ = 1 - case "follower": - typ = 2 - case "learner": - typ = 3 - case "witness": - typ = 4 - case "pending": - typ = 5 - default: - cmd.Println("unknown type") - return - } - prefix += "?type=" + strconv.Itoa(typ) + flagType := cmd.Flag("type") + prefix += "?type=" + flagType.Value.String() r, err := doRequest(cmd, prefix, http.MethodGet, http.Header{}) if err != nil { cmd.Printf("Failed to get regions with the given storeID: %s\n", err)