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