diff --git a/server/core/region.go b/server/core/region.go index b00911a3480..fef60c6aead 100644 --- a/server/core/region.go +++ b/server/core/region.go @@ -442,154 +442,36 @@ func (r *RegionInfo) GetReplicationStatus() *replication_modepb.RegionReplicatio } // regionMap wraps a map[uint64]*core.RegionInfo and supports randomly pick a region. -type regionMap struct { - m map[uint64]*RegionInfo - totalSize int64 - totalKeys int64 -} +type regionMap map[uint64]*RegionInfo -func newRegionMap() *regionMap { - return ®ionMap{ - m: make(map[uint64]*RegionInfo), - } +func newRegionMap() regionMap { + return make(map[uint64]*RegionInfo) } -func (rm *regionMap) Len() int { - if rm == nil { - return 0 - } - return len(rm.m) +func (rm regionMap) Len() int { + return len(rm) } -func (rm *regionMap) Get(id uint64) *RegionInfo { - if rm == nil { - return nil - } - if r, ok := rm.m[id]; ok { - return r - } - return nil +func (rm regionMap) Get(id uint64) *RegionInfo { + return rm[id] } -func (rm *regionMap) Put(region *RegionInfo) { - if old, ok := rm.m[region.GetID()]; ok { - rm.totalSize -= old.approximateSize - rm.totalKeys -= old.approximateKeys - } - rm.m[region.GetID()] = region - rm.totalSize += region.approximateSize - rm.totalKeys += region.approximateKeys +func (rm regionMap) Put(region *RegionInfo) { + rm[region.GetID()] = region } -func (rm *regionMap) Delete(id uint64) { - if rm == nil { - return - } - if old, ok := rm.m[id]; ok { - delete(rm.m, id) - rm.totalSize -= old.approximateSize - rm.totalKeys -= old.approximateKeys - } -} - -func (rm *regionMap) TotalSize() int64 { - if rm.Len() == 0 { - return 0 - } - return rm.totalSize -} - -// regionSubTree is used to manager different types of regions. -type regionSubTree struct { - *regionTree - totalSize int64 - totalKeys int64 -} - -func newRegionSubTree() *regionSubTree { - return ®ionSubTree{ - regionTree: newRegionTree(), - totalSize: 0, - } -} - -func (rst *regionSubTree) TotalSize() int64 { - if rst.length() == 0 { - return 0 - } - return rst.totalSize -} - -func (rst *regionSubTree) scanRanges() []*RegionInfo { - if rst.length() == 0 { - return nil - } - var res []*RegionInfo - rst.scanRange([]byte(""), func(region *RegionInfo) bool { - res = append(res, region) - return true - }) - return res -} - -func (rst *regionSubTree) update(region *RegionInfo) { - overlaps := rst.regionTree.update(region) - rst.totalSize += region.approximateSize - rst.totalKeys += region.approximateKeys - for _, r := range overlaps { - rst.totalSize -= r.approximateSize - rst.totalKeys -= r.approximateKeys - } -} - -func (rst *regionSubTree) remove(region *RegionInfo) { - if rst.length() == 0 { - return - } - if rst.regionTree.remove(region) != nil { - rst.totalSize -= region.approximateSize - rst.totalKeys -= region.approximateKeys - } -} - -func (rst *regionSubTree) length() int { - if rst == nil { - return 0 - } - return rst.regionTree.length() -} - -func (rst *regionSubTree) RandomRegion(ranges []KeyRange) *RegionInfo { - if rst.length() == 0 { - return nil - } - - return rst.regionTree.RandomRegion(ranges) -} - -func (rst *regionSubTree) RandomRegions(n int, ranges []KeyRange) []*RegionInfo { - if rst.length() == 0 { - return nil - } - - regions := make([]*RegionInfo, 0, n) - - for i := 0; i < n; i++ { - if region := rst.regionTree.RandomRegion(ranges); region != nil { - regions = append(regions, region) - } - } - return regions +func (rm regionMap) Delete(id uint64) { + delete(rm, id) } // RegionsInfo for export type RegionsInfo struct { tree *regionTree - regions *regionMap // regionID -> regionInfo - leaders map[uint64]*regionSubTree // storeID -> regionSubTree - followers map[uint64]*regionSubTree // storeID -> regionSubTree - learners map[uint64]*regionSubTree // storeID -> regionSubTree - pendingPeers map[uint64]*regionSubTree // storeID -> regionSubTree + regions regionMap // regionID -> regionInfo + leaders map[uint64]*regionTree // storeID -> sub regionTree + followers map[uint64]*regionTree // storeID -> sub regionTree + learners map[uint64]*regionTree // storeID -> sub regionTree + pendingPeers map[uint64]*regionTree // storeID -> sub regionTree } // NewRegionsInfo creates RegionsInfo with tree, regions, leaders and followers @@ -597,10 +479,10 @@ func NewRegionsInfo() *RegionsInfo { return &RegionsInfo{ tree: newRegionTree(), regions: newRegionMap(), - leaders: make(map[uint64]*regionSubTree), - followers: make(map[uint64]*regionSubTree), - learners: make(map[uint64]*regionSubTree), - pendingPeers: make(map[uint64]*regionSubTree), + leaders: make(map[uint64]*regionTree), + followers: make(map[uint64]*regionTree), + learners: make(map[uint64]*regionTree), + pendingPeers: make(map[uint64]*regionTree), } } @@ -626,13 +508,13 @@ func (r *RegionsInfo) SetRegion(region *RegionInfo) []*RegionInfo { return r.AddRegion(region) } -// Length returns the RegionsInfo length -func (r *RegionsInfo) Length() int { +// Len returns the RegionsInfo length +func (r *RegionsInfo) Len() int { return r.regions.Len() } -// TreeLength returns the RegionsInfo tree length(now only used in test) -func (r *RegionsInfo) TreeLength() int { +// TreeLen returns the RegionsInfo tree length(now only used in test) +func (r *RegionsInfo) TreeLen() int { return r.tree.length() } @@ -653,6 +535,7 @@ func (r *RegionsInfo) AddRegion(region *RegionInfo) []*RegionInfo { if bytes.Equal(regionOld.region.GetStartKey(), region.GetStartKey()) && bytes.Equal(regionOld.region.GetEndKey(), region.GetEndKey()) && regionOld.region.GetID() == region.GetID() { + r.tree.updateStat(regionOld.region, region) regionOld.region = region treeNeedAdd = false } @@ -675,7 +558,7 @@ func (r *RegionsInfo) AddRegion(region *RegionInfo) []*RegionInfo { // Add leader peer to leaders. store, ok := r.leaders[storeID] if !ok { - store = newRegionSubTree() + store = newRegionTree() r.leaders[storeID] = store } store.update(region) @@ -683,7 +566,7 @@ func (r *RegionsInfo) AddRegion(region *RegionInfo) []*RegionInfo { // Add follower peer to followers. store, ok := r.followers[storeID] if !ok { - store = newRegionSubTree() + store = newRegionTree() r.followers[storeID] = store } store.update(region) @@ -695,7 +578,7 @@ func (r *RegionsInfo) AddRegion(region *RegionInfo) []*RegionInfo { storeID := peer.GetStoreId() store, ok := r.learners[storeID] if !ok { - store = newRegionSubTree() + store = newRegionTree() r.learners[storeID] = store } store.update(region) @@ -705,7 +588,7 @@ func (r *RegionsInfo) AddRegion(region *RegionInfo) []*RegionInfo { storeID := peer.GetStoreId() store, ok := r.pendingPeers[storeID] if !ok { - store = newRegionSubTree() + store = newRegionTree() r.pendingPeers[storeID] = store } store.update(region) @@ -824,7 +707,7 @@ func (r *RegionsInfo) SearchPrevRegion(regionKey []byte) *RegionInfo { // GetRegions gets all RegionInfo from regionMap func (r *RegionsInfo) GetRegions() []*RegionInfo { regions := make([]*RegionInfo, 0, r.regions.Len()) - for _, region := range r.regions.m { + for _, region := range r.regions { regions = append(regions, region) } return regions @@ -868,7 +751,7 @@ func (r *RegionsInfo) GetStoreRegionSize(storeID uint64) int64 { // GetMetaRegions gets a set of metapb.Region from regionMap func (r *RegionsInfo) GetMetaRegions() []*metapb.Region { regions := make([]*metapb.Region, 0, r.regions.Len()) - for _, region := range r.regions.m { + for _, region := range r.regions { regions = append(regions, proto.Clone(region.meta).(*metapb.Region)) } return regions @@ -1019,10 +902,10 @@ func (r *RegionsInfo) GetAdjacentRegions(region *RegionInfo) (*RegionInfo, *Regi // GetAverageRegionSize returns the average region approximate size. func (r *RegionsInfo) GetAverageRegionSize() int64 { - if r.regions.Len() == 0 { + if r.tree.length() == 0 { return 0 } - return r.regions.TotalSize() / int64(r.regions.Len()) + return r.tree.TotalSize() / int64(r.tree.length()) } // DiffRegionPeersInfo return the difference of peers info between two RegionInfo diff --git a/server/core/region_test.go b/server/core/region_test.go index f8832712ab6..b20ee6b77ce 100644 --- a/server/core/region_test.go +++ b/server/core/region_test.go @@ -160,10 +160,6 @@ var _ = Suite(&testRegionMapSuite{}) type testRegionMapSuite struct{} func (s *testRegionMapSuite) TestRegionMap(c *C) { - var empty *regionMap - c.Assert(empty.Len(), Equals, 0) - c.Assert(empty.Get(1), IsNil) - rm := newRegionMap() s.check(c, rm) rm.Put(s.regionInfo(1)) @@ -195,7 +191,7 @@ func (s *testRegionMapSuite) regionInfo(id uint64) *RegionInfo { } } -func (s *testRegionMapSuite) check(c *C, rm *regionMap, ids ...uint64) { +func (s *testRegionMapSuite) check(c *C, rm regionMap, ids ...uint64) { // Check Get. for _, id := range ids { c.Assert(rm.Get(id).GetID(), Equals, id) @@ -208,16 +204,10 @@ func (s *testRegionMapSuite) check(c *C, rm *regionMap, ids ...uint64) { expect[id] = struct{}{} } set1 := make(map[uint64]struct{}) - for _, r := range rm.m { + for _, r := range rm { set1[r.GetID()] = struct{}{} } c.Assert(set1, DeepEquals, expect) - // Check region size. - var total int64 - for _, id := range ids { - total += int64(id) - } - c.Assert(rm.TotalSize(), Equals, total) } var _ = Suite(&testRegionKey{}) @@ -325,8 +315,7 @@ func (*testRegionKey) TestSetRegion(c *C) { c.Assert(regions.tree.length(), Equals, 96) c.Assert(len(regions.GetRegions()), Equals, 96) c.Assert(regions.GetRegion(201), NotNil) - c.Assert(regions.regions.totalKeys, Equals, int64(20)) - c.Assert(regions.regions.totalSize, Equals, int64(30)) + c.Assert(regions.tree.TotalSize(), Equals, int64(30)) } func (*testRegionKey) TestShouldRemoveFromSubTree(c *C) { diff --git a/server/core/region_tree.go b/server/core/region_tree.go index f3abef95c14..2e582bf034d 100644 --- a/server/core/region_tree.go +++ b/server/core/region_tree.go @@ -49,15 +49,21 @@ const ( type regionTree struct { tree *btree.BTree + // Statistics + totalSize int64 } func newRegionTree() *regionTree { return ®ionTree{ - tree: btree.New(defaultBTreeDegree), + tree: btree.New(defaultBTreeDegree), + totalSize: 0, } } func (t *regionTree) length() int { + if t == nil { + return 0 + } return t.tree.Len() } @@ -92,13 +98,16 @@ func (t *regionTree) getOverlaps(region *RegionInfo) []*RegionInfo { // It finds and deletes all the overlapped regions first, and then // insert the region. func (t *regionTree) update(region *RegionInfo) []*RegionInfo { + t.totalSize += region.approximateSize + overlaps := t.getOverlaps(region) - for _, item := range overlaps { + for _, old := range overlaps { log.Debug("overlapping region", - zap.Uint64("region-id", item.GetID()), - logutil.ZapRedactStringer("delete-region", RegionToHexMeta(item.GetMeta())), + zap.Uint64("region-id", old.GetID()), + logutil.ZapRedactStringer("delete-region", RegionToHexMeta(old.GetMeta())), logutil.ZapRedactStringer("update-region", RegionToHexMeta(region.GetMeta()))) - t.tree.Delete(®ionItem{item}) + t.tree.Delete(®ionItem{old}) + t.totalSize -= old.approximateSize } t.tree.ReplaceOrInsert(®ionItem{region: region}) @@ -106,6 +115,12 @@ func (t *regionTree) update(region *RegionInfo) []*RegionInfo { return overlaps } +// updateStat is used to update statistics when regionItem.region is directly replaced. +func (t *regionTree) updateStat(origin *RegionInfo, region *RegionInfo) { + t.totalSize += region.approximateSize + t.totalSize -= origin.approximateSize +} + // remove removes a region if the region is in the tree. // It will do nothing if it cannot find the region or the found region // is not the same with the region. @@ -118,6 +133,8 @@ func (t *regionTree) remove(region *RegionInfo) btree.Item { return nil } + t.totalSize -= region.approximateSize + return t.tree.Delete(result) } @@ -180,6 +197,18 @@ func (t *regionTree) scanRange(startKey []byte, f func(*RegionInfo) bool) { }) } +func (t *regionTree) scanRanges() []*RegionInfo { + if t.length() == 0 { + return nil + } + var res []*RegionInfo + t.scanRange([]byte(""), func(region *RegionInfo) bool { + res = append(res, region) + return true + }) + return res +} + func (t *regionTree) getAdjacentRegions(region *RegionInfo) (*regionItem, *regionItem) { item := ®ionItem{region: &RegionInfo{meta: &metapb.Region{StartKey: region.GetStartKey()}}} var prev, next *regionItem @@ -246,6 +275,28 @@ func (t *regionTree) RandomRegion(ranges []KeyRange) *RegionInfo { return nil } +func (t *regionTree) RandomRegions(n int, ranges []KeyRange) []*RegionInfo { + if t.length() == 0 { + return nil + } + + regions := make([]*RegionInfo, 0, n) + + for i := 0; i < n; i++ { + if region := t.RandomRegion(ranges); region != nil { + regions = append(regions, region) + } + } + return regions +} + +func (t *regionTree) TotalSize() int64 { + if t.length() == 0 { + return 0 + } + return t.totalSize +} + func init() { rand.Seed(time.Now().UnixNano()) } diff --git a/server/core/region_tree_test.go b/server/core/region_tree_test.go index 2169e852564..2a3a8ed20ae 100644 --- a/server/core/region_tree_test.go +++ b/server/core/region_tree_test.go @@ -120,36 +120,28 @@ func (s *testRegionSuite) newRegionWithStat(start, end string, size, keys int64) return region } -func (s *testRegionSuite) TestRegionSubTree(c *C) { - tree := newRegionSubTree() +func (s *testRegionSuite) TestRegionTreeStat(c *C) { + tree := newRegionTree() c.Assert(tree.totalSize, Equals, int64(0)) - c.Assert(tree.totalKeys, Equals, int64(0)) tree.update(s.newRegionWithStat("a", "b", 1, 2)) c.Assert(tree.totalSize, Equals, int64(1)) - c.Assert(tree.totalKeys, Equals, int64(2)) tree.update(s.newRegionWithStat("b", "c", 3, 4)) c.Assert(tree.totalSize, Equals, int64(4)) - c.Assert(tree.totalKeys, Equals, int64(6)) tree.update(s.newRegionWithStat("b", "e", 5, 6)) c.Assert(tree.totalSize, Equals, int64(6)) - c.Assert(tree.totalKeys, Equals, int64(8)) tree.remove(s.newRegionWithStat("a", "b", 1, 2)) c.Assert(tree.totalSize, Equals, int64(5)) - c.Assert(tree.totalKeys, Equals, int64(6)) tree.remove(s.newRegionWithStat("f", "g", 1, 2)) c.Assert(tree.totalSize, Equals, int64(5)) - c.Assert(tree.totalKeys, Equals, int64(6)) } -func (s *testRegionSuite) TestRegionSubTreeMerge(c *C) { - tree := newRegionSubTree() +func (s *testRegionSuite) TestRegionTreeMerge(c *C) { + tree := newRegionTree() tree.update(s.newRegionWithStat("a", "b", 1, 2)) tree.update(s.newRegionWithStat("b", "c", 3, 4)) c.Assert(tree.totalSize, Equals, int64(4)) - c.Assert(tree.totalKeys, Equals, int64(6)) tree.update(s.newRegionWithStat("a", "c", 5, 5)) c.Assert(tree.totalSize, Equals, int64(5)) - c.Assert(tree.totalKeys, Equals, int64(5)) } func (s *testRegionSuite) TestRegionTree(c *C) {