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 */
018package org.apache.hadoop.hbase.util;
019
020import java.io.IOException;
021import java.util.Arrays;
022import java.util.Comparator;
023import java.util.List;
024
025import org.apache.hadoop.conf.Configuration;
026import org.apache.hadoop.fs.FileSystem;
027import org.apache.hadoop.fs.Path;
028import org.apache.hadoop.hbase.HRegionInfo;
029import org.apache.hadoop.hbase.ServerName;
030import org.apache.hadoop.hbase.TableName;
031import org.apache.hadoop.hbase.client.RegionInfo;
032import org.apache.hadoop.hbase.client.RegionReplicaUtil;
033import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
034import org.apache.yetus.audience.InterfaceAudience;
035import org.apache.yetus.audience.InterfaceStability;
036import org.slf4j.Logger;
037import org.slf4j.LoggerFactory;
038
039import org.apache.hbase.thirdparty.com.google.common.base.Joiner;
040import org.apache.hbase.thirdparty.com.google.common.collect.Lists;
041
042/**
043 * Maintain information about a particular region.  It gathers information
044 * from three places -- HDFS, META, and region servers.
045 */
046@InterfaceAudience.Private
047@InterfaceStability.Evolving
048public class HbckRegionInfo implements KeyRange {
049  private static final Logger LOG = LoggerFactory.getLogger(HbckRegionInfo.class.getName());
050
051  private MetaEntry metaEntry = null; // info in META
052  private HdfsEntry hdfsEntry = null; // info in HDFS
053  private List<OnlineEntry> deployedEntries = Lists.newArrayList(); // on Region Server
054  private List<ServerName> deployedOn = Lists.newArrayList(); // info on RS's
055  private boolean skipChecks = false; // whether to skip further checks to this region info.
056  private boolean isMerged = false;// whether this region has already been merged into another one
057  private int deployedReplicaId = RegionInfo.DEFAULT_REPLICA_ID;
058  private RegionInfo primaryHRIForDeployedReplica = null;
059
060  public HbckRegionInfo(MetaEntry metaEntry) {
061    this.metaEntry = metaEntry;
062  }
063
064  public synchronized int getReplicaId() {
065    return metaEntry != null? metaEntry.getReplicaId(): deployedReplicaId;
066  }
067
068  public synchronized void addServer(RegionInfo regionInfo, ServerName serverName) {
069    OnlineEntry rse = new OnlineEntry(regionInfo, serverName) ;
070    this.deployedEntries.add(rse);
071    this.deployedOn.add(serverName);
072    // save the replicaId that we see deployed in the cluster
073    this.deployedReplicaId = regionInfo.getReplicaId();
074    this.primaryHRIForDeployedReplica =
075        RegionReplicaUtil.getRegionInfoForDefaultReplica(regionInfo);
076  }
077
078  @Override
079  public synchronized String toString() {
080    StringBuilder sb = new StringBuilder();
081    sb.append("{ meta => ");
082    sb.append((metaEntry != null)? metaEntry.getRegionNameAsString() : "null");
083    sb.append(", hdfs => " + getHdfsRegionDir());
084    sb.append(", deployed => " + Joiner.on(", ").join(deployedEntries));
085    sb.append(", replicaId => " + getReplicaId());
086    sb.append(" }");
087    return sb.toString();
088  }
089
090  @Override
091  public byte[] getStartKey() {
092    if (this.metaEntry != null) {
093      return this.metaEntry.getStartKey();
094    } else if (this.hdfsEntry != null) {
095      return this.hdfsEntry.hri.getStartKey();
096    } else {
097      LOG.error("Entry " + this + " has no meta or hdfs region start key.");
098      return null;
099    }
100  }
101
102  @Override
103  public byte[] getEndKey() {
104    if (this.metaEntry != null) {
105      return this.metaEntry.getEndKey();
106    } else if (this.hdfsEntry != null) {
107      return this.hdfsEntry.hri.getEndKey();
108    } else {
109      LOG.error("Entry " + this + " has no meta or hdfs region start key.");
110      return null;
111    }
112  }
113
114  public MetaEntry getMetaEntry() {
115    return this.metaEntry;
116  }
117
118  public void setMetaEntry(MetaEntry metaEntry) {
119    this.metaEntry = metaEntry;
120  }
121
122  public HdfsEntry getHdfsEntry() {
123    return this.hdfsEntry;
124  }
125
126  public void setHdfsEntry(HdfsEntry hdfsEntry) {
127    this.hdfsEntry = hdfsEntry;
128  }
129
130  public List<OnlineEntry> getOnlineEntries() {
131    return this.deployedEntries;
132  }
133
134  public List<ServerName> getDeployedOn() {
135    return this.deployedOn;
136  }
137
138  /**
139   * Read the .regioninfo file from the file system.  If there is no
140   * .regioninfo, add it to the orphan hdfs region list.
141   */
142  public void loadHdfsRegioninfo(Configuration conf) throws IOException {
143    Path regionDir = getHdfsRegionDir();
144    if (regionDir == null) {
145      if (getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) {
146        // Log warning only for default/ primary replica with no region dir
147        LOG.warn("No HDFS region dir found: " + this + " meta=" + metaEntry);
148      }
149      return;
150    }
151
152    if (hdfsEntry.hri != null) {
153      // already loaded data
154      return;
155    }
156
157    FileSystem fs = FileSystem.get(conf);
158    RegionInfo hri = HRegionFileSystem.loadRegionInfoFileContent(fs, regionDir);
159    LOG.debug("RegionInfo read: " + hri.toString());
160    hdfsEntry.hri = hri;
161  }
162
163  public TableName getTableName() {
164    if (this.metaEntry != null) {
165      return this.metaEntry.getTable();
166    } else if (this.hdfsEntry != null) {
167      // we are only guaranteed to have a path and not an HRI for hdfsEntry,
168      // so we get the name from the Path
169      Path tableDir = this.hdfsEntry.regionDir.getParent();
170      return FSUtils.getTableName(tableDir);
171    } else {
172      // return the info from the first online/deployed hri
173      for (OnlineEntry e : deployedEntries) {
174        return e.getRegionInfo().getTable();
175      }
176      return null;
177    }
178  }
179
180  public String getRegionNameAsString() {
181    if (metaEntry != null) {
182      return metaEntry.getRegionNameAsString();
183    } else if (hdfsEntry != null) {
184      if (hdfsEntry.hri != null) {
185        return hdfsEntry.hri.getRegionNameAsString();
186      }
187    } else {
188      // return the info from the first online/deployed hri
189      for (OnlineEntry e : deployedEntries) {
190        return e.getRegionInfo().getRegionNameAsString();
191      }
192    }
193    return null;
194  }
195
196  public byte[] getRegionName() {
197    if (metaEntry != null) {
198      return metaEntry.getRegionName();
199    } else if (hdfsEntry != null) {
200      return hdfsEntry.hri.getRegionName();
201    } else {
202      // return the info from the first online/deployed hri
203      for (OnlineEntry e : deployedEntries) {
204        return e.getRegionInfo().getRegionName();
205      }
206      return null;
207    }
208  }
209
210  public RegionInfo getPrimaryHRIForDeployedReplica() {
211    return primaryHRIForDeployedReplica;
212  }
213
214  public Path getHdfsRegionDir() {
215    if (hdfsEntry == null) {
216      return null;
217    }
218    return hdfsEntry.regionDir;
219  }
220
221  public boolean containsOnlyHdfsEdits() {
222    if (hdfsEntry == null) {
223      return false;
224    }
225    return hdfsEntry.hdfsOnlyEdits;
226  }
227
228  public boolean isHdfsRegioninfoPresent() {
229    if (hdfsEntry == null) {
230      return false;
231    }
232    return hdfsEntry.hdfsRegioninfoFilePresent;
233  }
234
235  public long getModTime() {
236    if (hdfsEntry == null) {
237      return 0;
238    }
239    return hdfsEntry.regionDirModTime;
240  }
241
242  public RegionInfo getHdfsHRI() {
243    if (hdfsEntry == null) {
244      return null;
245    }
246    return hdfsEntry.hri;
247  }
248
249  public void setSkipChecks(boolean skipChecks) {
250    this.skipChecks = skipChecks;
251  }
252
253  public boolean isSkipChecks() {
254    return skipChecks;
255  }
256
257  public void setMerged(boolean isMerged) {
258    this.isMerged = isMerged;
259  }
260
261  public boolean isMerged() {
262    return this.isMerged;
263  }
264
265  /**
266   * Stores the regioninfo entries scanned from META
267   */
268  public static class MetaEntry extends HRegionInfo {
269    ServerName regionServer;   // server hosting this region
270    long modTime;          // timestamp of most recent modification metadata
271    RegionInfo splitA, splitB; //split daughters
272
273    public MetaEntry(RegionInfo rinfo, ServerName regionServer, long modTime) {
274      this(rinfo, regionServer, modTime, null, null);
275    }
276
277    public MetaEntry(RegionInfo rinfo, ServerName regionServer, long modTime,
278        RegionInfo splitA, RegionInfo splitB) {
279      super(rinfo);
280      this.regionServer = regionServer;
281      this.modTime = modTime;
282      this.splitA = splitA;
283      this.splitB = splitB;
284    }
285
286    public ServerName getRegionServer() {
287      return this.regionServer;
288    }
289
290    @Override
291    public boolean equals(Object o) {
292      boolean superEq = super.equals(o);
293      if (!superEq) {
294        return superEq;
295      }
296
297      MetaEntry me = (MetaEntry) o;
298      if (!regionServer.equals(me.regionServer)) {
299        return false;
300      }
301      return (modTime == me.modTime);
302    }
303
304    @Override
305    public int hashCode() {
306      int hash = Arrays.hashCode(getRegionName());
307      hash = (int) (hash ^ getRegionId());
308      hash ^= Arrays.hashCode(getStartKey());
309      hash ^= Arrays.hashCode(getEndKey());
310      hash ^= Boolean.valueOf(isOffline()).hashCode();
311      hash ^= getTable().hashCode();
312      if (regionServer != null) {
313        hash ^= regionServer.hashCode();
314      }
315      hash = (int) (hash ^ modTime);
316      return hash;
317    }
318  }
319
320  /**
321   * Stores the regioninfo entries from HDFS
322   */
323  public static class HdfsEntry {
324    RegionInfo hri;
325    Path regionDir = null;
326    long regionDirModTime = 0;
327    boolean hdfsRegioninfoFilePresent = false;
328    boolean hdfsOnlyEdits = false;
329
330    HdfsEntry() {
331    }
332
333    public HdfsEntry(Path regionDir) {
334      this.regionDir = regionDir;
335    }
336  }
337
338  /**
339   * Stores the regioninfo retrieved from Online region servers.
340   */
341  static class OnlineEntry {
342    private RegionInfo regionInfo;
343    private ServerName serverName;
344
345    OnlineEntry(RegionInfo regionInfo, ServerName serverName) {
346      this.regionInfo = regionInfo;
347      this.serverName = serverName;
348    }
349
350    public RegionInfo getRegionInfo() {
351      return regionInfo;
352    }
353
354    public ServerName getServerName() {
355      return serverName;
356    }
357
358    @Override
359    public String toString() {
360      return serverName.toString() + ";" + regionInfo.getRegionNameAsString();
361    }
362  }
363
364  final static Comparator<HbckRegionInfo> COMPARATOR = new Comparator<HbckRegionInfo>() {
365    @Override
366    public int compare(HbckRegionInfo l, HbckRegionInfo r) {
367      if (l == r) {
368        // same instance
369        return 0;
370      }
371
372      int tableCompare = l.getTableName().compareTo(r.getTableName());
373      if (tableCompare != 0) {
374        return tableCompare;
375      }
376
377      int startComparison = RegionSplitCalculator.BYTES_COMPARATOR.compare(
378          l.getStartKey(), r.getStartKey());
379      if (startComparison != 0) {
380        return startComparison;
381      }
382
383      // Special case for absolute endkey
384      byte[] endKey = r.getEndKey();
385      endKey = (endKey.length == 0) ? null : endKey;
386      byte[] endKey2 = l.getEndKey();
387      endKey2 = (endKey2.length == 0) ? null : endKey2;
388      int endComparison = RegionSplitCalculator.BYTES_COMPARATOR.compare(
389          endKey2,  endKey);
390
391      if (endComparison != 0) {
392        return endComparison;
393      }
394
395      // use regionId as tiebreaker.
396      // Null is considered after all possible values so make it bigger.
397      if (l.getHdfsEntry() == null && r.getHdfsEntry() == null) {
398        return 0;
399      }
400      if (l.getHdfsEntry() == null && r.getHdfsEntry() != null) {
401        return 1;
402      }
403      // l.hdfsEntry must not be null
404      if (r.getHdfsEntry() == null) {
405        return -1;
406      }
407      // both l.hdfsEntry and r.hdfsEntry must not be null.
408      return Long.compare(l.getHdfsEntry().hri.getRegionId(), r.getHdfsEntry().hri.getRegionId());
409    }
410  };
411}