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