HBASE-26481 Consider rolling upgrading from old region replication framework (#3880)
[hbase.git] / hbase-client / src / main / java / org / apache / hadoop / hbase / RegionLocations.java
blob0d3a464e0f86644a75df224522d29f0c601de0f4
1 /**
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;
30 /**
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
44 // in the cache.
45 private final HRegionLocation[] locations; // replicaId -> HRegionLocation.
47 /**
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;
58 int index = 0;
59 for (HRegionLocation loc : locations) {
60 if (loc != null) {
61 if (loc.getRegion().getReplicaId() >= maxReplicaId) {
62 maxReplicaId = loc.getRegion().getReplicaId();
63 maxReplicaIdIndex = index;
66 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;
73 } else {
74 this.locations = new HRegionLocation[maxReplicaId + 1];
75 for (HRegionLocation loc : locations) {
76 if (loc != null) {
77 this.locations[loc.getRegion().getReplicaId()] = loc;
81 for (HRegionLocation loc : this.locations) {
82 if (loc != null && loc.getServerName() != null){
83 numNonNullElements++;
86 this.numNonNullElements = numNonNullElements;
89 public RegionLocations(Collection<HRegionLocation> locations) {
90 this(locations.toArray(new HRegionLocation[locations.size()]));
93 /**
94 * Returns the size of the list even if some of the elements
95 * might be null.
96 * @return the size of the list (corresponding to the max replicaId)
98 public int size() {
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])) {
159 return this;
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) {
177 return this;
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) {
270 return location;
273 if (force
274 || isGreaterThan(location.getSeqNum(), oldLocation.getSeqNum(), checkForEquals)) {
275 return location;
277 return oldLocation;
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) {
301 return this;
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) {
324 return null;
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) {
337 if (loc != null) {
338 if (Bytes.equals(loc.getRegion().getRegionName(), regionName)
339 || Bytes.equals(loc.getRegion().getEncodedNameAsBytes(), regionName)) {
340 return loc;
344 return null;
347 public HRegionLocation[] getRegionLocations() {
348 return locations;
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) {
360 if (loc != null) {
361 return loc;
364 return null;
367 @Override
368 public Iterator<HRegionLocation> iterator() {
369 return Arrays.asList(locations).iterator();
372 @Override
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);
381 builder.append("]");
382 return builder.toString();