001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.hadoop.hbase; 020 021import java.util.Arrays; 022import java.util.Collection; 023import java.util.Iterator; 024 025import org.apache.hadoop.hbase.client.RegionInfo; 026import org.apache.hadoop.hbase.client.RegionReplicaUtil; 027import org.apache.hadoop.hbase.util.Bytes; 028import org.apache.yetus.audience.InterfaceAudience; 029 030/** 031 * Container for holding a list of {@link HRegionLocation}'s that correspond to the 032 * same range. The list is indexed by the replicaId. This is an immutable list, 033 * however mutation operations are provided which returns a new List via copy-on-write 034 * (assuming small number of locations) 035 */ 036@InterfaceAudience.Private 037public class RegionLocations implements Iterable<HRegionLocation> { 038 039 private final int numNonNullElements; 040 041 // locations array contains the HRL objects for known region replicas indexes by the replicaId. 042 // elements can be null if the region replica is not known at all. A null value indicates 043 // that there is a region replica with the index as replicaId, but the location is not known 044 // in the cache. 045 private final HRegionLocation[] locations; // replicaId -> HRegionLocation. 046 047 /** 048 * Constructs the region location list. The locations array should 049 * contain all the locations for known replicas for the region, and should be 050 * sorted in replicaId ascending order, although it can contain nulls indicating replicaIds 051 * that the locations of which are not known. 052 * @param locations an array of HRegionLocations for the same region range 053 */ 054 public RegionLocations(HRegionLocation... locations) { 055 int numNonNullElements = 0; 056 int maxReplicaId = -1; 057 int maxReplicaIdIndex = -1; 058 int index = 0; 059 for (HRegionLocation loc : locations) { 060 if (loc != null) { 061 if (loc.getRegion().getReplicaId() >= maxReplicaId) { 062 maxReplicaId = loc.getRegion().getReplicaId(); 063 maxReplicaIdIndex = index; 064 } 065 } 066 index++; 067 } 068 // account for the null elements in the array after maxReplicaIdIndex 069 maxReplicaId = maxReplicaId + (locations.length - (maxReplicaIdIndex + 1) ); 070 071 if (maxReplicaId + 1 == locations.length) { 072 this.locations = locations; 073 } else { 074 this.locations = new HRegionLocation[maxReplicaId + 1]; 075 for (HRegionLocation loc : locations) { 076 if (loc != null) { 077 this.locations[loc.getRegion().getReplicaId()] = loc; 078 } 079 } 080 } 081 for (HRegionLocation loc : this.locations) { 082 if (loc != null && loc.getServerName() != null){ 083 numNonNullElements++; 084 } 085 } 086 this.numNonNullElements = numNonNullElements; 087 } 088 089 public RegionLocations(Collection<HRegionLocation> locations) { 090 this(locations.toArray(new HRegionLocation[locations.size()])); 091 } 092 093 /** 094 * Returns the size of the list even if some of the elements 095 * might be null. 096 * @return the size of the list (corresponding to the max replicaId) 097 */ 098 public int size() { 099 return locations.length; 100 } 101 102 /** 103 * Returns the size of not-null locations 104 * @return the size of not-null locations 105 */ 106 public int numNonNullElements() { 107 return numNonNullElements; 108 } 109 110 /** 111 * Returns whether there are non-null elements in the list 112 * @return whether there are non-null elements in the list 113 */ 114 public boolean isEmpty() { 115 return numNonNullElements == 0; 116 } 117 118 /** 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 124 */ 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); 133 } 134 newLocations[i] = null; 135 } else if (newLocations != null) { 136 newLocations[i] = locations[i]; 137 } 138 } 139 return newLocations == null ? this : new RegionLocations(newLocations); 140 } 141 142 /** 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 147 */ 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; 153 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; 160 } 161 162 HRegionLocation[] newLocations = new HRegionLocation[locations.length]; 163 System.arraycopy(locations, 0, newLocations, 0, locations.length); 164 newLocations[replicaId] = null; 165 166 return new RegionLocations(newLocations); 167 } 168 169 /** 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 174 */ 175 public RegionLocations remove(int replicaId) { 176 if (getRegionLocation(replicaId) == null) { 177 return this; 178 } 179 180 HRegionLocation[] newLocations = new HRegionLocation[locations.length]; 181 182 System.arraycopy(locations, 0, newLocations, 0, locations.length); 183 if (replicaId < newLocations.length) { 184 newLocations[replicaId] = null; 185 } 186 187 return new RegionLocations(newLocations); 188 } 189 190 /** 191 * Set the element to null if its getServerName method returns null. Returns null if all the 192 * elements are removed. 193 */ 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]; 201 } 202 } 203 return hasNonNullElement ? new RegionLocations(newLocations) : null; 204 } 205 206 /** 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 214 */ 215 public RegionLocations mergeLocations(RegionLocations other) { 216 assert other != null; 217 218 HRegionLocation[] newLocations = null; 219 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; 223 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(); 232 } 233 234 HRegionLocation selectedLoc = selectRegionLocation(thisLoc, 235 otherLoc, true, false); 236 237 if (selectedLoc != thisLoc) { 238 if (newLocations == null) { 239 newLocations = new HRegionLocation[max]; 240 System.arraycopy(locations, 0, newLocations, 0, i); 241 } 242 } 243 if (newLocations != null) { 244 newLocations[i] = selectedLoc; 245 } 246 } 247 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; 255 } 256 } 257 } 258 } 259 260 return newLocations == null ? this : new RegionLocations(newLocations); 261 } 262 263 private HRegionLocation selectRegionLocation(HRegionLocation oldLocation, 264 HRegionLocation location, boolean checkForEquals, boolean force) { 265 if (location == null) { 266 return oldLocation == null ? null : oldLocation; 267 } 268 269 if (oldLocation == null) { 270 return location; 271 } 272 273 if (force 274 || isGreaterThan(location.getSeqNum(), oldLocation.getSeqNum(), checkForEquals)) { 275 return location; 276 } 277 return oldLocation; 278 } 279 280 /** 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 289 */ 290 public RegionLocations updateLocation(HRegionLocation location, 291 boolean checkForEquals, boolean force) { 292 assert location != null; 293 294 int replicaId = location.getRegion().getReplicaId(); 295 296 HRegionLocation oldLoc = getRegionLocation(location.getRegion().getReplicaId()); 297 HRegionLocation selectedLoc = selectRegionLocation(oldLoc, location, 298 checkForEquals, force); 299 300 if (selectedLoc == oldLoc) { 301 return this; 302 } 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; 312 } 313 } 314 } 315 return new RegionLocations(newLocations); 316 } 317 318 private boolean isGreaterThan(long a, long b, boolean checkForEquals) { 319 return a > b || (checkForEquals && (a == b)); 320 } 321 322 public HRegionLocation getRegionLocation(int replicaId) { 323 if (replicaId >= locations.length) { 324 return null; 325 } 326 return locations[replicaId]; 327 } 328 329 /** 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 334 */ 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; 341 } 342 } 343 } 344 return null; 345 } 346 347 public HRegionLocation[] getRegionLocations() { 348 return locations; 349 } 350 351 public HRegionLocation getDefaultRegionLocation() { 352 return locations[RegionInfo.DEFAULT_REPLICA_ID]; 353 } 354 355 /** 356 * Returns the first not-null region location in the list 357 */ 358 public HRegionLocation getRegionLocation() { 359 for (HRegionLocation loc : locations) { 360 if (loc != null) { 361 return loc; 362 } 363 } 364 return null; 365 } 366 367 @Override 368 public Iterator<HRegionLocation> iterator() { 369 return Arrays.asList(locations).iterator(); 370 } 371 372 @Override 373 public String toString() { 374 StringBuilder builder = new StringBuilder("["); 375 for (HRegionLocation loc : locations) { 376 if (builder.length() > 1) { 377 builder.append(", "); 378 } 379 builder.append(loc == null ? "null" : loc); 380 } 381 builder.append("]"); 382 return builder.toString(); 383 } 384 385}