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