1717 */ 
1818package  org .apache .hadoop .hbase .master .balancer ;
1919
20+ import  org .apache .hadoop .conf .Configuration ;
21+ import  org .apache .hadoop .hbase .client .RegionInfo ;
22+ import  org .apache .hadoop .hbase .client .RegionReplicaUtil ;
2023import  org .apache .hadoop .hbase .master .RegionPlan ;
2124import  org .slf4j .Logger ;
2225import  org .slf4j .LoggerFactory ;
2831 */ 
2932public  class  DistributeReplicasConditional  extends  RegionPlanConditional  {
3033
34+   /** 
35+    * Local mini cluster tests can only one on one server/rack by design. If enabled, this will 
36+    * pretend that localhost RegionServer threads are actually running on separate hosts/racks. This 
37+    * should only be used in unit tests. 
38+    */ 
39+   public  static  boolean  TEST_MODE_ENABLED  = false ;
40+ 
3141  private  static  final  Logger  LOG  = LoggerFactory .getLogger (DistributeReplicasConditional .class );
3242
3343  private  final  BalancerClusterState  cluster ;
3444
35-   public  DistributeReplicasConditional (BalancerClusterState  cluster ) {
36-     super (cluster );
45+   public  DistributeReplicasConditional (Configuration   conf ,  BalancerClusterState  cluster ) {
46+     super (conf ,  cluster );
3747    this .cluster  = cluster ;
3848  }
3949
@@ -63,15 +73,17 @@ boolean isViolating(RegionPlan regionPlan) {
6373    }
6474
6575    if  (
66-       checkViolation (destinationServerIndex , cluster .serversPerHost , cluster .serverIndexToHostIndex ,
67-         cluster .regionsPerServer , primaryRegionIndex , cluster .regionIndexToPrimaryIndex , "host" )
76+       checkViolation (cluster .regions , regionPlan .getRegionInfo (), destinationServerIndex ,
77+         cluster .serversPerHost , cluster .serverIndexToHostIndex , cluster .regionsPerServer ,
78+         primaryRegionIndex , "host" )
6879    ) {
6980      return  true ;
7081    }
7182
7283    if  (
73-       checkViolation (destinationServerIndex , cluster .serversPerRack , cluster .serverIndexToRackIndex ,
74-         cluster .regionsPerServer , primaryRegionIndex , cluster .regionIndexToPrimaryIndex , "rack" )
84+       checkViolation (cluster .regions , regionPlan .getRegionInfo (), destinationServerIndex ,
85+         cluster .serversPerRack , cluster .serverIndexToRackIndex , cluster .regionsPerServer ,
86+         primaryRegionIndex , "rack" )
7587    ) {
7688      return  true ;
7789    }
@@ -89,23 +101,61 @@ boolean isViolating(RegionPlan regionPlan) {
89101   * @param locationType           Type of location being checked ("Host" or "Rack"). 
90102   * @return True if a violation is found, false otherwise. 
91103   */ 
92-   static  boolean  checkViolation (int  destinationServerIndex , int [][] serversPerLocation ,
93-     int [] serverToLocationIndex , int [][] regionsPerServer , int  primaryRegionIndex ,
94-     int [] regionIndexToPrimaryIndex , String  locationType ) {
95-     if  (serversPerLocation  == null  || serversPerLocation .length  <= 1 ) {
96-       LOG .debug ("{} violation check skipped: serversPerLocation is null or has <= 1 location" ,
97-         locationType );
104+   static  boolean  checkViolation (RegionInfo [] regions , RegionInfo  regionToBeMoved ,
105+     int  destinationServerIndex , int [][] serversPerLocation , int [] serverToLocationIndex ,
106+     int [][] regionsPerServer , int  primaryRegionIndex , String  locationType ) {
107+ 
108+     if  (TEST_MODE_ENABLED ) {
109+       // Take the flat serversPerLocation, like {0: [0, 1, 2, 3, 4]} 
110+       // and pretend it is multi-location, like {0: [1], 1: [2] ...} 
111+       int  numServers  = serversPerLocation [0 ].length ;
112+       // Create a new serversPerLocation array where each server gets its own "location" 
113+       int [][] simulatedServersPerLocation  = new  int [numServers ][];
114+       for  (int  i  = 0 ; i  < numServers ; i ++) {
115+         simulatedServersPerLocation [i ] = new  int [] { serversPerLocation [0 ][i ] };
116+       }
117+       // Adjust serverToLocationIndex to map each server to its simulated location 
118+       int [] simulatedServerToLocationIndex  = new  int [numServers ];
119+       for  (int  i  = 0 ; i  < numServers ; i ++) {
120+         simulatedServerToLocationIndex [serversPerLocation [0 ][i ]] = i ;
121+       }
122+       LOG .trace ("Test mode enabled: Simulated {} locations for servers." , numServers );
123+       // Use the simulated arrays for test mode 
124+       serversPerLocation  = simulatedServersPerLocation ;
125+       serverToLocationIndex  = simulatedServerToLocationIndex ;
126+     }
127+ 
128+     if  (serversPerLocation  == null ) {
129+       LOG .trace ("{} violation check skipped: serversPerLocation is null" , locationType );
98130      return  false ;
99131    }
100132
133+     if  (serversPerLocation .length  == 1 ) {
134+       LOG .warn (
135+         "{} violation inevitable: serversPerLocation has only 1 entry. You probably should not be using read replicas." ,
136+         locationType );
137+       return  true ;
138+     }
139+ 
101140    int  destinationLocationIndex  = serverToLocationIndex [destinationServerIndex ];
102-     LOG .debug ("Checking {} violations for destination server index {} at location index {}" ,
141+     LOG .trace ("Checking {} violations for destination server index {} at location index {}" ,
103142      locationType , destinationServerIndex , destinationLocationIndex );
104143
144+     // For every RegionServer on host/rack 
105145    for  (int  serverIndex  : serversPerLocation [destinationLocationIndex ]) {
146+       // For every Region on RegionServer 
106147      for  (int  hostedRegion  : regionsPerServer [serverIndex ]) {
107-         if  (regionIndexToPrimaryIndex [hostedRegion ] == primaryRegionIndex ) {
108-           LOG .debug ("{} violation detected: region {} on {} {}" , locationType , primaryRegionIndex ,
148+         RegionInfo  targetRegion  = regions [hostedRegion ];
149+         if  (targetRegion .getEncodedName ().equals (regionToBeMoved .getEncodedName ())) {
150+           // The balancer state will already show this region as having moved. 
151+           // A region's replicas will also have unique encoded names. 
152+           // So we should skip this check if the encoded name is the same. 
153+           continue ;
154+         }
155+         boolean  isReplicaForSameRegion  =
156+           RegionReplicaUtil .isReplicasForSameRegion (targetRegion , regionToBeMoved );
157+         if  (isReplicaForSameRegion ) {
158+           LOG .trace ("{} violation detected: region {} on {} {}" , locationType , primaryRegionIndex ,
109159            locationType , destinationLocationIndex );
110160          return  true ;
111161        }
0 commit comments