View Javadoc

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
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
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.
17   */
18  
19  package org.apache.hadoop.hbase;
20  
21  import java.util.Collection;
22  import java.util.Objects;
23  
24  import org.apache.hadoop.hbase.classification.InterfaceAudience;
25  import org.apache.hadoop.hbase.client.RegionReplicaUtil;
26  import org.apache.hadoop.hbase.util.Bytes;
27  
28  /**
29   * Container for holding a list of {@link HRegionLocation}'s that correspond to the
30   * same range. The list is indexed by the replicaId. This is an immutable list,
31   * however mutation operations are provided which returns a new List via copy-on-write
32   * (assuming small number of locations)
33   */
34  @InterfaceAudience.Private
35  public class RegionLocations {
36  
37    private final int numNonNullElements;
38  
39    // locations array contains the HRL objects for known region replicas indexes by the replicaId.
40    // elements can be null if the region replica is not known at all. A null value indicates
41    // that there is a region replica with the index as replicaId, but the location is not known
42    // in the cache.
43    private final HRegionLocation[] locations; // replicaId -> HRegionLocation.
44  
45    /**
46     * Constructs the region location list. The locations array should
47     * contain all the locations for known replicas for the region, and should be
48     * sorted in replicaId ascending order, although it can contain nulls indicating replicaIds
49     * that the locations of which are not known.
50     * @param locations an array of HRegionLocations for the same region range
51     */
52    public RegionLocations(HRegionLocation... locations) {
53      int numNonNullElements = 0;
54      int maxReplicaId = -1;
55      int maxReplicaIdIndex = -1;
56      int index = 0;
57      for (HRegionLocation loc : locations) {
58        if (loc != null) {
59          if (loc.getServerName() != null) {
60            numNonNullElements++;
61          }
62          if (loc.getRegionInfo().getReplicaId() >= maxReplicaId) {
63            maxReplicaId = loc.getRegionInfo().getReplicaId();
64            maxReplicaIdIndex = index;
65          }
66        }
67        index++;
68      }
69      this.numNonNullElements = numNonNullElements;
70  
71      // account for the null elements in the array after maxReplicaIdIndex
72      maxReplicaId = maxReplicaId + (locations.length - (maxReplicaIdIndex + 1) );
73  
74      if (maxReplicaId + 1 == locations.length) {
75        this.locations = locations;
76      } else {
77        this.locations = new HRegionLocation[maxReplicaId + 1];
78        for (HRegionLocation loc : locations) {
79          if (loc != null) {
80            this.locations[loc.getRegionInfo().getReplicaId()] = loc;
81          }
82        }
83      }
84    }
85  
86    public RegionLocations(Collection<HRegionLocation> locations) {
87      this(locations.toArray(new HRegionLocation[locations.size()]));
88    }
89  
90    /**
91     * Returns the size of the list even if some of the elements
92     * might be null.
93     * @return the size of the list (corresponding to the max replicaId)
94     */
95    public int size() {
96      return locations.length;
97    }
98  
99    /**
100    * Returns the size of not-null locations
101    * @return the size of not-null locations
102    */
103   public int numNonNullElements() {
104     return numNonNullElements;
105   }
106 
107   /**
108    * Returns whether there are non-null elements in the list
109    * @return whether there are non-null elements in the list
110    */
111   public boolean isEmpty() {
112     return numNonNullElements == 0;
113   }
114 
115   /**
116    * Returns a new RegionLocations with the locations removed (set to null)
117    * which have the destination server as given.
118    * @param serverName the serverName to remove locations of
119    * @return an RegionLocations object with removed locations or the same object
120    * if nothing is removed
121    */
122   public RegionLocations removeByServer(ServerName serverName) {
123     HRegionLocation[] newLocations = null;
124     for (int i = 0; i < locations.length; i++) {
125       // check whether something to remove
126       if (locations[i] != null && serverName.equals(locations[i].getServerName())) {
127         if (newLocations == null) { //first time
128           newLocations = new HRegionLocation[locations.length];
129           System.arraycopy(locations, 0, newLocations, 0, i);
130         }
131         newLocations[i] = null;
132       } else if (newLocations != null) {
133         newLocations[i] = locations[i];
134       }
135     }
136     return newLocations == null ? this : new RegionLocations(newLocations);
137   }
138 
139   /**
140    * Removes the given location from the list
141    * @param location the location to remove
142    * @return an RegionLocations object with removed locations or the same object
143    * if nothing is removed
144    */
145   public RegionLocations remove(HRegionLocation location) {
146     if (location == null) return this;
147     if (location.getRegionInfo() == null) return this;
148     int replicaId = location.getRegionInfo().getReplicaId();
149     if (replicaId >= locations.length) return this;
150 
151     // check whether something to remove. HRL.compareTo() compares ONLY the
152     // serverName. We want to compare the HRI's as well.
153     if (locations[replicaId] == null
154         || !location.getRegionInfo().equals(locations[replicaId].getRegionInfo())
155         || !location.equals(locations[replicaId])) {
156       return this;
157     }
158 
159     HRegionLocation[] newLocations = new HRegionLocation[locations.length];
160     System.arraycopy(locations, 0, newLocations, 0, locations.length);
161     newLocations[replicaId] = null;
162 
163     return new RegionLocations(newLocations);
164   }
165 
166   /**
167    * Removes location of the given replicaId from the list
168    * @param replicaId the replicaId of the location to remove
169    * @return an RegionLocations object with removed locations or the same object
170    * if nothing is removed
171    */
172   public RegionLocations remove(int replicaId) {
173     if (getRegionLocation(replicaId) == null) {
174       return this;
175     }
176 
177     HRegionLocation[] newLocations = new HRegionLocation[locations.length];
178 
179     System.arraycopy(locations, 0, newLocations, 0, locations.length);
180     if (replicaId < newLocations.length) {
181       newLocations[replicaId] = null;
182     }
183 
184     return new RegionLocations(newLocations);
185   }
186 
187   /**
188    * Merges this RegionLocations list with the given list assuming
189    * same range, and keeping the most up to date version of the
190    * HRegionLocation entries from either list according to seqNum. If seqNums
191    * are equal, the location from the argument (other) is taken.
192    * @param other the locations to merge with
193    * @return an RegionLocations object with merged locations or the same object
194    * if nothing is merged
195    */
196   public RegionLocations mergeLocations(RegionLocations other) {
197     assert other != null;
198 
199     HRegionLocation[] newLocations = null;
200 
201     // Use the length from other, since it is coming from meta. Otherwise,
202     // in case of region replication going down, we might have a leak here.
203     int max = other.locations.length;
204 
205     HRegionInfo regionInfo = null;
206     for (int i = 0; i < max; i++) {
207       HRegionLocation thisLoc = this.getRegionLocation(i);
208       HRegionLocation otherLoc = other.getRegionLocation(i);
209       if (regionInfo == null && otherLoc != null && otherLoc.getRegionInfo() != null) {
210         // regionInfo is the first non-null HRI from other RegionLocations. We use it to ensure that
211         // all replica region infos belong to the same region with same region id.
212         regionInfo = otherLoc.getRegionInfo();
213       }
214 
215       HRegionLocation selectedLoc = selectRegionLocation(thisLoc,
216         otherLoc, true, false);
217 
218       if (!Objects.equals(selectedLoc, thisLoc)) {
219         if (newLocations == null) {
220           newLocations = new HRegionLocation[max];
221           System.arraycopy(locations, 0, newLocations, 0, i);
222         }
223       }
224       if (newLocations != null) {
225         newLocations[i] = selectedLoc;
226       }
227     }
228 
229     // ensure that all replicas share the same start code. Otherwise delete them
230     if (newLocations != null && regionInfo != null) {
231       for (int i=0; i < newLocations.length; i++) {
232         if (newLocations[i] != null) {
233           if (!RegionReplicaUtil.isReplicasForSameRegion(regionInfo,
234             newLocations[i].getRegionInfo())) {
235             newLocations[i] = null;
236           }
237         }
238       }
239     }
240 
241     return newLocations == null ? this : new RegionLocations(newLocations);
242   }
243 
244   private HRegionLocation selectRegionLocation(HRegionLocation oldLocation,
245       HRegionLocation location, boolean checkForEquals, boolean force) {
246     if (location == null) {
247       return oldLocation == null ? null : oldLocation;
248     }
249 
250     if (oldLocation == null) {
251       return location;
252     }
253 
254     if (force
255         || isGreaterThan(location.getSeqNum(), oldLocation.getSeqNum(), checkForEquals)) {
256       return location;
257     }
258     return oldLocation;
259   }
260 
261   /**
262    * Updates the location with new only if the new location has a higher
263    * seqNum than the old one or force is true.
264    * @param location the location to add or update
265    * @param checkForEquals whether to update the location if seqNums for the
266    * HRegionLocations for the old and new location are the same
267    * @param force whether to force update
268    * @return an RegionLocations object with updated locations or the same object
269    * if nothing is updated
270    */
271   public RegionLocations updateLocation(HRegionLocation location,
272       boolean checkForEquals, boolean force) {
273     assert location != null;
274 
275     int replicaId = location.getRegionInfo().getReplicaId();
276 
277     HRegionLocation oldLoc = getRegionLocation(location.getRegionInfo().getReplicaId());
278     HRegionLocation selectedLoc = selectRegionLocation(oldLoc, location,
279       checkForEquals, force);
280 
281     if (Objects.equals(selectedLoc, oldLoc)) {
282       return this;
283     }
284     HRegionLocation[] newLocations = new HRegionLocation[Math.max(locations.length, replicaId +1)];
285     System.arraycopy(locations, 0, newLocations, 0, locations.length);
286     newLocations[replicaId] = location;
287     // ensure that all replicas share the same start code. Otherwise delete them
288     for (int i=0; i < newLocations.length; i++) {
289       if (newLocations[i] != null) {
290         if (!RegionReplicaUtil.isReplicasForSameRegion(location.getRegionInfo(),
291           newLocations[i].getRegionInfo())) {
292           newLocations[i] = null;
293         }
294       }
295     }
296     return new RegionLocations(newLocations);
297   }
298 
299   private boolean isGreaterThan(long a, long b, boolean checkForEquals) {
300     return a > b || (checkForEquals && (a == b));
301   }
302 
303   public HRegionLocation getRegionLocation(int replicaId) {
304     if (replicaId >= locations.length) {
305       return null;
306     }
307     return locations[replicaId];
308   }
309 
310   /**
311    * Returns the region location from the list for matching regionName, which can
312    * be regionName or encodedRegionName
313    * @param regionName regionName or encodedRegionName
314    * @return HRegionLocation found or null
315    */
316   public HRegionLocation getRegionLocationByRegionName(byte[] regionName) {
317     for (HRegionLocation loc : locations) {
318       if (loc != null) {
319         if (Bytes.equals(loc.getRegionInfo().getRegionName(), regionName)
320             || Bytes.equals(loc.getRegionInfo().getEncodedNameAsBytes(), regionName)) {
321           return loc;
322         }
323       }
324     }
325     return null;
326   }
327 
328   public HRegionLocation[] getRegionLocations() {
329     return locations;
330   }
331 
332   public HRegionLocation getDefaultRegionLocation() {
333     return locations[HRegionInfo.DEFAULT_REPLICA_ID];
334   }
335 
336   /**
337    * Returns the first not-null region location in the list
338    */
339   public HRegionLocation getRegionLocation() {
340     for (HRegionLocation loc : locations) {
341       if (loc != null) {
342         return loc;
343       }
344     }
345     return null;
346   }
347 
348   @Override
349   public String toString() {
350     StringBuilder builder = new StringBuilder("[");
351     for (HRegionLocation loc : locations) {
352       if (builder.length() > 1) {
353         builder.append(", ");
354       }
355       builder.append(loc == null ? "null" : loc);
356     }
357     builder.append("]");
358     return builder.toString();
359   }
360 
361 }