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