@@ -1059,6 +1059,10 @@ type RegionsInfo struct {
10591059 pendingPeers map [uint64 ]* regionTree // storeID -> sub regionTree
10601060 // This tree is used to check the overlaps among all the subtrees.
10611061 overlapTree * regionTree
1062+ // sizeTree is enabled only by the primary PD service. It never holds the
1063+ // root-tree or subtree lock while updating the size index.
1064+ sizeTreeMu syncutil.Mutex
1065+ sizeTree atomic.Pointer [regionSizeTree ]
10621066}
10631067
10641068// NewRegionsInfo creates RegionsInfo with tree, regions, leaders and followers
@@ -1076,6 +1080,76 @@ func NewRegionsInfo() *RegionsInfo {
10761080 }
10771081}
10781082
1083+ // StartRegionSizeTree starts the eventually consistent range-size index.
1084+ func (r * RegionsInfo ) StartRegionSizeTree (ctx context.Context ) {
1085+ if r .sizeTree .Load () != nil {
1086+ return
1087+ }
1088+ r .sizeTreeMu .Lock ()
1089+ defer r .sizeTreeMu .Unlock ()
1090+ if r .sizeTree .Load () != nil {
1091+ return
1092+ }
1093+ sizeTree := newRegionSizeTree (ctx , r )
1094+ r .sizeTree .Store (sizeTree )
1095+ sizeTree .requestRebuild ()
1096+ sizeTree .start ()
1097+ }
1098+
1099+ // StopRegionSizeTree stops the range-size index and waits for its worker.
1100+ func (r * RegionsInfo ) StopRegionSizeTree () {
1101+ r .sizeTreeMu .Lock ()
1102+ defer r .sizeTreeMu .Unlock ()
1103+ if sizeTree := r .sizeTree .Swap (nil ); sizeTree != nil {
1104+ sizeTree .stop ()
1105+ }
1106+ }
1107+
1108+ func (r * RegionsInfo ) notifyRegionSizeTree (regionIDs ... uint64 ) {
1109+ if sizeTree := r .sizeTree .Load (); sizeTree != nil {
1110+ sizeTree .notify (regionIDs ... )
1111+ }
1112+ }
1113+
1114+ func (r * RegionsInfo ) notifyRegionSizeTreeIfChanged (
1115+ origin , region * RegionInfo ,
1116+ overlaps []* RegionInfo ,
1117+ rangeChanged bool ,
1118+ ) {
1119+ sizeTree := r .sizeTree .Load ()
1120+ if sizeTree == nil {
1121+ return
1122+ }
1123+ sizeChanged := origin == nil || rangeChanged ||
1124+ origin .GetApproximateSize () != region .GetApproximateSize ()
1125+ if len (overlaps ) == 0 {
1126+ if sizeChanged {
1127+ sizeTree .notify (region .GetID ())
1128+ }
1129+ return
1130+ }
1131+ capacity := len (overlaps )
1132+ if sizeChanged {
1133+ capacity ++
1134+ }
1135+ regionIDs := make ([]uint64 , 0 , capacity )
1136+ if sizeChanged {
1137+ regionIDs = append (regionIDs , region .GetID ())
1138+ }
1139+ for _ , overlap := range overlaps {
1140+ if overlap .GetID () != region .GetID () {
1141+ regionIDs = append (regionIDs , overlap .GetID ())
1142+ }
1143+ }
1144+ sizeTree .notify (regionIDs ... )
1145+ }
1146+
1147+ func (r * RegionsInfo ) resetRegionSizeTree () {
1148+ if sizeTree := r .sizeTree .Load (); sizeTree != nil {
1149+ sizeTree .requestReset ()
1150+ }
1151+ }
1152+
10791153// GetRegion returns the RegionInfo with regionID
10801154func (r * RegionsInfo ) GetRegion (regionID uint64 ) * RegionInfo {
10811155 r .t .RLock ()
@@ -1108,6 +1182,7 @@ func (r *RegionsInfo) CheckAndPutRegion(region *RegionInfo) []*RegionInfo {
11081182 }
11091183 origin , overlaps , rangeChanged := r .setRegionLocked (region , true , ols ... )
11101184 r .t .Unlock ()
1185+ r .notifyRegionSizeTreeIfChanged (origin , region , overlaps , rangeChanged )
11111186 r .UpdateSubTree (region , origin , overlaps , rangeChanged )
11121187 return overlaps
11131188}
@@ -1146,6 +1221,7 @@ func (r *RegionsInfo) AtomicCheckAndPutRegion(ctx *MetaProcessContext, region *R
11461221 origin , overlaps , rangeChanged := r .setRegionLocked (region , true , ols ... )
11471222 r .t .Unlock ()
11481223 tracer .OnSetRegionFinished ()
1224+ r .notifyRegionSizeTreeIfChanged (origin , region , overlaps , rangeChanged )
11491225 r .UpdateSubTree (region , origin , overlaps , rangeChanged )
11501226 tracer .OnUpdateSubTreeFinished ()
11511227 return overlaps , nil
@@ -1169,9 +1245,10 @@ func (r *RegionsInfo) CheckAndPutRootTree(ctx *MetaProcessContext, region *Regio
11691245 return nil , err
11701246 }
11711247 tracer .OnValidateRegionFinished ()
1172- _ , overlaps , _ := r .setRegionLocked (region , true , ols ... )
1248+ origin , overlaps , rangeChanged := r .setRegionLocked (region , true , ols ... )
11731249 r .t .Unlock ()
11741250 tracer .OnSetRegionFinished ()
1251+ r .notifyRegionSizeTreeIfChanged (origin , region , overlaps , rangeChanged )
11751252 return overlaps , nil
11761253}
11771254
@@ -1342,8 +1419,10 @@ func check(region, origin *RegionInfo, overlaps []*RegionInfo) error {
13421419// SetRegion sets the RegionInfo to regionTree and regionMap and return the update info of subtree.
13431420func (r * RegionsInfo ) SetRegion (region * RegionInfo ) (* RegionInfo , []* RegionInfo , bool ) {
13441421 r .t .Lock ()
1345- defer r .t .Unlock ()
1346- return r .setRegionLocked (region , false )
1422+ origin , overlaps , rangeChanged := r .setRegionLocked (region , false )
1423+ r .t .Unlock ()
1424+ r .notifyRegionSizeTreeIfChanged (origin , region , overlaps , rangeChanged )
1425+ return origin , overlaps , rangeChanged
13471426}
13481427
13491428func (r * RegionsInfo ) setRegionLocked (region * RegionInfo , withOverlaps bool , ol ... * RegionInfo ) (* RegionInfo , []* RegionInfo , bool ) {
@@ -1455,17 +1534,19 @@ func (r *RegionsInfo) GetOverlaps(region *RegionInfo) []*RegionInfo {
14551534// RemoveRegion removes RegionInfo from regionTree and regionMap
14561535func (r * RegionsInfo ) RemoveRegion (region * RegionInfo ) {
14571536 r .t .Lock ()
1458- defer r .t .Unlock ()
14591537 // Remove from tree and regions.
14601538 r .tree .remove (region )
14611539 delete (r .regions , region .GetID ())
1540+ r .t .Unlock ()
1541+ r .notifyRegionSizeTree (region .GetID ())
14621542}
14631543
14641544// ResetRegionCache resets the regions info.
14651545func (r * RegionsInfo ) ResetRegionCache () {
14661546 r .t .Lock ()
14671547 r .tree = newRegionTreeWithCountRef ()
14681548 r .regions = make (map [uint64 ]* regionItem )
1549+ r .resetRegionSizeTree ()
14691550 r .t .Unlock ()
14701551 r .st .Lock ()
14711552 defer r .st .Unlock ()
@@ -2263,6 +2344,16 @@ func (r *RegionsInfo) GetRegionSizeByRange(startKey, endKey []byte) int64 {
22632344 return size
22642345}
22652346
2347+ // GetRegionSizeByRangeFromSizeTree returns the approximate total size of
2348+ // Regions intersecting [startKey, endKey) and whether the eventually
2349+ // consistent index is ready.
2350+ func (r * RegionsInfo ) GetRegionSizeByRangeFromSizeTree (startKey , endKey []byte ) (int64 , bool ) {
2351+ if sizeTree := r .sizeTree .Load (); sizeTree != nil && sizeTree .isReady () {
2352+ return sizeTree .getRegionSizeByRange (startKey , endKey ), true
2353+ }
2354+ return 0 , false
2355+ }
2356+
22662357// metrics default poll interval
22672358const defaultPollInterval = 15 * time .Second
22682359
0 commit comments