2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
19 package org
.apache
.hadoop
.hbase
;
21 import java
.util
.Arrays
;
22 import java
.util
.Collection
;
23 import java
.util
.Iterator
;
25 import org
.apache
.hadoop
.hbase
.client
.RegionInfo
;
26 import org
.apache
.hadoop
.hbase
.client
.RegionReplicaUtil
;
27 import org
.apache
.hadoop
.hbase
.util
.Bytes
;
28 import org
.apache
.yetus
.audience
.InterfaceAudience
;
31 * Container for holding a list of {@link HRegionLocation}'s that correspond to the
32 * same range. The list is indexed by the replicaId. This is an immutable list,
33 * however mutation operations are provided which returns a new List via copy-on-write
34 * (assuming small number of locations)
36 @InterfaceAudience.Private
37 public class RegionLocations
implements Iterable
<HRegionLocation
> {
39 private final int numNonNullElements
;
41 // locations array contains the HRL objects for known region replicas indexes by the replicaId.
42 // elements can be null if the region replica is not known at all. A null value indicates
43 // that there is a region replica with the index as replicaId, but the location is not known
45 private final HRegionLocation
[] locations
; // replicaId -> HRegionLocation.
48 * Constructs the region location list. The locations array should
49 * contain all the locations for known replicas for the region, and should be
50 * sorted in replicaId ascending order, although it can contain nulls indicating replicaIds
51 * that the locations of which are not known.
52 * @param locations an array of HRegionLocations for the same region range
54 public RegionLocations(HRegionLocation
... locations
) {
55 int numNonNullElements
= 0;
56 int maxReplicaId
= -1;
57 int maxReplicaIdIndex
= -1;
59 for (HRegionLocation loc
: locations
) {
61 if (loc
.getRegion().getReplicaId() >= maxReplicaId
) {
62 maxReplicaId
= loc
.getRegion().getReplicaId();
63 maxReplicaIdIndex
= index
;
68 // account for the null elements in the array after maxReplicaIdIndex
69 maxReplicaId
= maxReplicaId
+ (locations
.length
- (maxReplicaIdIndex
+ 1) );
71 if (maxReplicaId
+ 1 == locations
.length
) {
72 this.locations
= locations
;
74 this.locations
= new HRegionLocation
[maxReplicaId
+ 1];
75 for (HRegionLocation loc
: locations
) {
77 this.locations
[loc
.getRegion().getReplicaId()] = loc
;
81 for (HRegionLocation loc
: this.locations
) {
82 if (loc
!= null && loc
.getServerName() != null){
86 this.numNonNullElements
= numNonNullElements
;
89 public RegionLocations(Collection
<HRegionLocation
> locations
) {
90 this(locations
.toArray(new HRegionLocation
[locations
.size()]));
94 * Returns the size of the list even if some of the elements
96 * @return the size of the list (corresponding to the max replicaId)
99 return locations
.length
;
103 * Returns the size of not-null locations
104 * @return the size of not-null locations
106 public int numNonNullElements() {
107 return numNonNullElements
;
111 * Returns whether there are non-null elements in the list
112 * @return whether there are non-null elements in the list
114 public boolean isEmpty() {
115 return numNonNullElements
== 0;
119 * Returns a new RegionLocations with the locations removed (set to null)
120 * which have the destination server as given.
121 * @param serverName the serverName to remove locations of
122 * @return an RegionLocations object with removed locations or the same object
123 * if nothing is removed
125 public RegionLocations
removeByServer(ServerName serverName
) {
126 HRegionLocation
[] newLocations
= null;
127 for (int i
= 0; i
< locations
.length
; i
++) {
128 // check whether something to remove
129 if (locations
[i
] != null && serverName
.equals(locations
[i
].getServerName())) {
130 if (newLocations
== null) { //first time
131 newLocations
= new HRegionLocation
[locations
.length
];
132 System
.arraycopy(locations
, 0, newLocations
, 0, i
);
134 newLocations
[i
] = null;
135 } else if (newLocations
!= null) {
136 newLocations
[i
] = locations
[i
];
139 return newLocations
== null ?
this : new RegionLocations(newLocations
);
143 * Removes the given location from the list
144 * @param location the location to remove
145 * @return an RegionLocations object with removed locations or the same object
146 * if nothing is removed
148 public RegionLocations
remove(HRegionLocation location
) {
149 if (location
== null) return this;
150 if (location
.getRegion() == null) return this;
151 int replicaId
= location
.getRegion().getReplicaId();
152 if (replicaId
>= locations
.length
) return this;
154 // check whether something to remove. HRL.compareTo() compares ONLY the
155 // serverName. We want to compare the HRI's as well.
156 if (locations
[replicaId
] == null
157 || RegionInfo
.COMPARATOR
.compare(location
.getRegion(), locations
[replicaId
].getRegion()) != 0
158 || !location
.equals(locations
[replicaId
])) {
162 HRegionLocation
[] newLocations
= new HRegionLocation
[locations
.length
];
163 System
.arraycopy(locations
, 0, newLocations
, 0, locations
.length
);
164 newLocations
[replicaId
] = null;
166 return new RegionLocations(newLocations
);
170 * Removes location of the given replicaId from the list
171 * @param replicaId the replicaId of the location to remove
172 * @return an RegionLocations object with removed locations or the same object
173 * if nothing is removed
175 public RegionLocations
remove(int replicaId
) {
176 if (getRegionLocation(replicaId
) == null) {
180 HRegionLocation
[] newLocations
= new HRegionLocation
[locations
.length
];
182 System
.arraycopy(locations
, 0, newLocations
, 0, locations
.length
);
183 if (replicaId
< newLocations
.length
) {
184 newLocations
[replicaId
] = null;
187 return new RegionLocations(newLocations
);
191 * Set the element to null if its getServerName method returns null. Returns null if all the
192 * elements are removed.
194 public RegionLocations
removeElementsWithNullLocation() {
195 HRegionLocation
[] newLocations
= new HRegionLocation
[locations
.length
];
196 boolean hasNonNullElement
= false;
197 for (int i
= 0; i
< locations
.length
; i
++) {
198 if (locations
[i
] != null && locations
[i
].getServerName() != null) {
199 hasNonNullElement
= true;
200 newLocations
[i
] = locations
[i
];
203 return hasNonNullElement ?
new RegionLocations(newLocations
) : null;
207 * Merges this RegionLocations list with the given list assuming
208 * same range, and keeping the most up to date version of the
209 * HRegionLocation entries from either list according to seqNum. If seqNums
210 * are equal, the location from the argument (other) is taken.
211 * @param other the locations to merge with
212 * @return an RegionLocations object with merged locations or the same object
213 * if nothing is merged
215 public RegionLocations
mergeLocations(RegionLocations other
) {
216 assert other
!= null;
218 HRegionLocation
[] newLocations
= null;
220 // Use the length from other, since it is coming from meta. Otherwise,
221 // in case of region replication going down, we might have a leak here.
222 int max
= other
.locations
.length
;
224 RegionInfo regionInfo
= null;
225 for (int i
= 0; i
< max
; i
++) {
226 HRegionLocation thisLoc
= this.getRegionLocation(i
);
227 HRegionLocation otherLoc
= other
.getRegionLocation(i
);
228 if (regionInfo
== null && otherLoc
!= null && otherLoc
.getRegion() != null) {
229 // regionInfo is the first non-null HRI from other RegionLocations. We use it to ensure that
230 // all replica region infos belong to the same region with same region id.
231 regionInfo
= otherLoc
.getRegion();
234 HRegionLocation selectedLoc
= selectRegionLocation(thisLoc
,
235 otherLoc
, true, false);
237 if (selectedLoc
!= thisLoc
) {
238 if (newLocations
== null) {
239 newLocations
= new HRegionLocation
[max
];
240 System
.arraycopy(locations
, 0, newLocations
, 0, i
);
243 if (newLocations
!= null) {
244 newLocations
[i
] = selectedLoc
;
248 // ensure that all replicas share the same start code. Otherwise delete them
249 if (newLocations
!= null && regionInfo
!= null) {
250 for (int i
=0; i
< newLocations
.length
; i
++) {
251 if (newLocations
[i
] != null) {
252 if (!RegionReplicaUtil
.isReplicasForSameRegion(regionInfo
,
253 newLocations
[i
].getRegion())) {
254 newLocations
[i
] = null;
260 return newLocations
== null ?
this : new RegionLocations(newLocations
);
263 private HRegionLocation
selectRegionLocation(HRegionLocation oldLocation
,
264 HRegionLocation location
, boolean checkForEquals
, boolean force
) {
265 if (location
== null) {
266 return oldLocation
== null ?
null : oldLocation
;
269 if (oldLocation
== null) {
274 || isGreaterThan(location
.getSeqNum(), oldLocation
.getSeqNum(), checkForEquals
)) {
281 * Updates the location with new only if the new location has a higher
282 * seqNum than the old one or force is true.
283 * @param location the location to add or update
284 * @param checkForEquals whether to update the location if seqNums for the
285 * HRegionLocations for the old and new location are the same
286 * @param force whether to force update
287 * @return an RegionLocations object with updated locations or the same object
288 * if nothing is updated
290 public RegionLocations
updateLocation(HRegionLocation location
,
291 boolean checkForEquals
, boolean force
) {
292 assert location
!= null;
294 int replicaId
= location
.getRegion().getReplicaId();
296 HRegionLocation oldLoc
= getRegionLocation(location
.getRegion().getReplicaId());
297 HRegionLocation selectedLoc
= selectRegionLocation(oldLoc
, location
,
298 checkForEquals
, force
);
300 if (selectedLoc
== oldLoc
) {
303 HRegionLocation
[] newLocations
= new HRegionLocation
[Math
.max(locations
.length
, replicaId
+1)];
304 System
.arraycopy(locations
, 0, newLocations
, 0, locations
.length
);
305 newLocations
[replicaId
] = location
;
306 // ensure that all replicas share the same start code. Otherwise delete them
307 for (int i
=0; i
< newLocations
.length
; i
++) {
308 if (newLocations
[i
] != null) {
309 if (!RegionReplicaUtil
.isReplicasForSameRegion(location
.getRegion(),
310 newLocations
[i
].getRegion())) {
311 newLocations
[i
] = null;
315 return new RegionLocations(newLocations
);
318 private boolean isGreaterThan(long a
, long b
, boolean checkForEquals
) {
319 return a
> b
|| (checkForEquals
&& (a
== b
));
322 public HRegionLocation
getRegionLocation(int replicaId
) {
323 if (replicaId
>= locations
.length
) {
326 return locations
[replicaId
];
330 * Returns the region location from the list for matching regionName, which can
331 * be regionName or encodedRegionName
332 * @param regionName regionName or encodedRegionName
333 * @return HRegionLocation found or null
335 public HRegionLocation
getRegionLocationByRegionName(byte[] regionName
) {
336 for (HRegionLocation loc
: locations
) {
338 if (Bytes
.equals(loc
.getRegion().getRegionName(), regionName
)
339 || Bytes
.equals(loc
.getRegion().getEncodedNameAsBytes(), regionName
)) {
347 public HRegionLocation
[] getRegionLocations() {
351 public HRegionLocation
getDefaultRegionLocation() {
352 return locations
[RegionInfo
.DEFAULT_REPLICA_ID
];
356 * Returns the first not-null region location in the list
358 public HRegionLocation
getRegionLocation() {
359 for (HRegionLocation loc
: locations
) {
368 public Iterator
<HRegionLocation
> iterator() {
369 return Arrays
.asList(locations
).iterator();
373 public String
toString() {
374 StringBuilder builder
= new StringBuilder("[");
375 for (HRegionLocation loc
: locations
) {
376 if (builder
.length() > 1) {
377 builder
.append(", ");
379 builder
.append(loc
== null ?
"null" : loc
);
382 return builder
.toString();