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.Closeable; 021import java.io.FileNotFoundException; 022import java.io.IOException; 023import java.io.InterruptedIOException; 024import java.io.PrintWriter; 025import java.io.StringWriter; 026import java.net.InetAddress; 027import java.net.URI; 028import java.util.ArrayList; 029import java.util.Arrays; 030import java.util.Collection; 031import java.util.Collections; 032import java.util.Comparator; 033import java.util.EnumSet; 034import java.util.HashMap; 035import java.util.HashSet; 036import java.util.Iterator; 037import java.util.List; 038import java.util.Locale; 039import java.util.Map; 040import java.util.Map.Entry; 041import java.util.Objects; 042import java.util.Optional; 043import java.util.Set; 044import java.util.SortedMap; 045import java.util.SortedSet; 046import java.util.TreeMap; 047import java.util.TreeSet; 048import java.util.Vector; 049import java.util.concurrent.Callable; 050import java.util.concurrent.ConcurrentSkipListMap; 051import java.util.concurrent.ExecutionException; 052import java.util.concurrent.ExecutorService; 053import java.util.concurrent.Executors; 054import java.util.concurrent.Future; 055import java.util.concurrent.FutureTask; 056import java.util.concurrent.ScheduledThreadPoolExecutor; 057import java.util.concurrent.TimeUnit; 058import java.util.concurrent.TimeoutException; 059import java.util.concurrent.atomic.AtomicBoolean; 060import java.util.concurrent.atomic.AtomicInteger; 061import org.apache.commons.io.IOUtils; 062import org.apache.commons.lang3.RandomStringUtils; 063import org.apache.commons.lang3.StringUtils; 064import org.apache.hadoop.conf.Configuration; 065import org.apache.hadoop.conf.Configured; 066import org.apache.hadoop.fs.FSDataOutputStream; 067import org.apache.hadoop.fs.FileStatus; 068import org.apache.hadoop.fs.FileSystem; 069import org.apache.hadoop.fs.Path; 070import org.apache.hadoop.fs.permission.FsAction; 071import org.apache.hadoop.fs.permission.FsPermission; 072import org.apache.hadoop.hbase.Abortable; 073import org.apache.hadoop.hbase.Cell; 074import org.apache.hadoop.hbase.CellUtil; 075import org.apache.hadoop.hbase.ClusterMetrics; 076import org.apache.hadoop.hbase.ClusterMetrics.Option; 077import org.apache.hadoop.hbase.HBaseConfiguration; 078import org.apache.hadoop.hbase.HBaseInterfaceAudience; 079import org.apache.hadoop.hbase.HConstants; 080import org.apache.hadoop.hbase.HRegionInfo; 081import org.apache.hadoop.hbase.HRegionLocation; 082import org.apache.hadoop.hbase.KeyValue; 083import org.apache.hadoop.hbase.MasterNotRunningException; 084import org.apache.hadoop.hbase.MetaTableAccessor; 085import org.apache.hadoop.hbase.RegionLocations; 086import org.apache.hadoop.hbase.ServerName; 087import org.apache.hadoop.hbase.TableName; 088import org.apache.hadoop.hbase.ZooKeeperConnectionException; 089import org.apache.hadoop.hbase.client.Admin; 090import org.apache.hadoop.hbase.client.ClusterConnection; 091import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; 092import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 093import org.apache.hadoop.hbase.client.Connection; 094import org.apache.hadoop.hbase.client.ConnectionFactory; 095import org.apache.hadoop.hbase.client.Delete; 096import org.apache.hadoop.hbase.client.Get; 097import org.apache.hadoop.hbase.client.Put; 098import org.apache.hadoop.hbase.client.RegionInfo; 099import org.apache.hadoop.hbase.client.RegionInfoBuilder; 100import org.apache.hadoop.hbase.client.RegionReplicaUtil; 101import org.apache.hadoop.hbase.client.Result; 102import org.apache.hadoop.hbase.client.RowMutations; 103import org.apache.hadoop.hbase.client.Table; 104import org.apache.hadoop.hbase.client.TableDescriptor; 105import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 106import org.apache.hadoop.hbase.client.TableState; 107import org.apache.hadoop.hbase.io.FileLink; 108import org.apache.hadoop.hbase.io.HFileLink; 109import org.apache.hadoop.hbase.io.hfile.CacheConfig; 110import org.apache.hadoop.hbase.io.hfile.HFile; 111import org.apache.hadoop.hbase.log.HBaseMarkers; 112import org.apache.hadoop.hbase.master.MasterFileSystem; 113import org.apache.hadoop.hbase.master.RegionState; 114import org.apache.hadoop.hbase.regionserver.HRegion; 115import org.apache.hadoop.hbase.regionserver.HRegionFileSystem; 116import org.apache.hadoop.hbase.regionserver.StoreFileInfo; 117import org.apache.hadoop.hbase.security.AccessDeniedException; 118import org.apache.hadoop.hbase.security.UserProvider; 119import org.apache.hadoop.hbase.util.Bytes.ByteArrayComparator; 120import org.apache.hadoop.hbase.util.HBaseFsck.ErrorReporter.ERROR_CODE; 121import org.apache.hadoop.hbase.util.hbck.HFileCorruptionChecker; 122import org.apache.hadoop.hbase.util.hbck.ReplicationChecker; 123import org.apache.hadoop.hbase.util.hbck.TableIntegrityErrorHandler; 124import org.apache.hadoop.hbase.util.hbck.TableIntegrityErrorHandlerImpl; 125import org.apache.hadoop.hbase.wal.WAL; 126import org.apache.hadoop.hbase.wal.WALFactory; 127import org.apache.hadoop.hbase.wal.WALSplitter; 128import org.apache.hadoop.hbase.zookeeper.MetaTableLocator; 129import org.apache.hadoop.hbase.zookeeper.ZKUtil; 130import org.apache.hadoop.hbase.zookeeper.ZKWatcher; 131import org.apache.hadoop.hbase.zookeeper.ZNodePaths; 132import org.apache.hadoop.hdfs.protocol.AlreadyBeingCreatedException; 133import org.apache.hadoop.ipc.RemoteException; 134import org.apache.hadoop.security.UserGroupInformation; 135import org.apache.hadoop.util.ReflectionUtils; 136import org.apache.hadoop.util.Tool; 137import org.apache.hadoop.util.ToolRunner; 138import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting; 139import org.apache.hbase.thirdparty.com.google.common.collect.Sets; 140import org.apache.yetus.audience.InterfaceAudience; 141import org.apache.yetus.audience.InterfaceStability; 142import org.apache.zookeeper.KeeperException; 143import org.slf4j.Logger; 144import org.slf4j.LoggerFactory; 145 146import org.apache.hbase.thirdparty.com.google.common.base.Joiner; 147import org.apache.hbase.thirdparty.com.google.common.base.Preconditions; 148import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableList; 149import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 150import org.apache.hbase.thirdparty.com.google.common.collect.Multimap; 151import org.apache.hbase.thirdparty.com.google.common.collect.Ordering; 152import org.apache.hbase.thirdparty.com.google.common.collect.TreeMultimap; 153 154import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 155import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.AdminService.BlockingInterface; 156 157/** 158 * HBaseFsck (hbck) is a tool for checking and repairing region consistency and 159 * table integrity problems in a corrupted HBase. This tool was written for hbase-1.x. It does not 160 * work with hbase-2.x; it can read state but is not allowed to change state; i.e. effect 'repair'. 161 * See hbck2 (HBASE-19121) for a hbck tool for hbase2. 162 * 163 * <p> 164 * Region consistency checks verify that hbase:meta, region deployment on region 165 * servers and the state of data in HDFS (.regioninfo files) all are in 166 * accordance. 167 * <p> 168 * Table integrity checks verify that all possible row keys resolve to exactly 169 * one region of a table. This means there are no individual degenerate 170 * or backwards regions; no holes between regions; and that there are no 171 * overlapping regions. 172 * <p> 173 * The general repair strategy works in two phases: 174 * <ol> 175 * <li> Repair Table Integrity on HDFS. (merge or fabricate regions) 176 * <li> Repair Region Consistency with hbase:meta and assignments 177 * </ol> 178 * <p> 179 * For table integrity repairs, the tables' region directories are scanned 180 * for .regioninfo files. Each table's integrity is then verified. If there 181 * are any orphan regions (regions with no .regioninfo files) or holes, new 182 * regions are fabricated. Backwards regions are sidelined as well as empty 183 * degenerate (endkey==startkey) regions. If there are any overlapping regions, 184 * a new region is created and all data is merged into the new region. 185 * <p> 186 * Table integrity repairs deal solely with HDFS and could potentially be done 187 * offline -- the hbase region servers or master do not need to be running. 188 * This phase can eventually be used to completely reconstruct the hbase:meta table in 189 * an offline fashion. 190 * <p> 191 * Region consistency requires three conditions -- 1) valid .regioninfo file 192 * present in an HDFS region dir, 2) valid row with .regioninfo data in META, 193 * and 3) a region is deployed only at the regionserver that was assigned to 194 * with proper state in the master. 195 * <p> 196 * Region consistency repairs require hbase to be online so that hbck can 197 * contact the HBase master and region servers. The hbck#connect() method must 198 * first be called successfully. Much of the region consistency information 199 * is transient and less risky to repair. 200 * <p> 201 * If hbck is run from the command line, there are a handful of arguments that 202 * can be used to limit the kinds of repairs hbck will do. See the code in 203 * {@link #printUsageAndExit()} for more details. 204 */ 205@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.TOOLS) 206@InterfaceStability.Evolving 207public class HBaseFsck extends Configured implements Closeable { 208 public static final long DEFAULT_TIME_LAG = 60000; // default value of 1 minute 209 public static final long DEFAULT_SLEEP_BEFORE_RERUN = 10000; 210 private static final int MAX_NUM_THREADS = 50; // #threads to contact regions 211 private static boolean rsSupportsOffline = true; 212 private static final int DEFAULT_OVERLAPS_TO_SIDELINE = 2; 213 private static final int DEFAULT_MAX_MERGE = 5; 214 private static final String TO_BE_LOADED = "to_be_loaded"; 215 /** 216 * Here is where hbase-1.x used to default the lock for hbck1. 217 * It puts in place a lock when it goes to write/make changes. 218 */ 219 @VisibleForTesting 220 public static final String HBCK_LOCK_FILE = "hbase-hbck.lock"; 221 private static final int DEFAULT_MAX_LOCK_FILE_ATTEMPTS = 5; 222 private static final int DEFAULT_LOCK_FILE_ATTEMPT_SLEEP_INTERVAL = 200; // milliseconds 223 private static final int DEFAULT_LOCK_FILE_ATTEMPT_MAX_SLEEP_TIME = 5000; // milliseconds 224 // We have to set the timeout value > HdfsConstants.LEASE_SOFTLIMIT_PERIOD. 225 // In HADOOP-2.6 and later, the Namenode proxy now created with custom RetryPolicy for 226 // AlreadyBeingCreatedException which is implies timeout on this operations up to 227 // HdfsConstants.LEASE_SOFTLIMIT_PERIOD (60 seconds). 228 private static final int DEFAULT_WAIT_FOR_LOCK_TIMEOUT = 80; // seconds 229 private static final int DEFAULT_MAX_CREATE_ZNODE_ATTEMPTS = 5; 230 private static final int DEFAULT_CREATE_ZNODE_ATTEMPT_SLEEP_INTERVAL = 200; // milliseconds 231 private static final int DEFAULT_CREATE_ZNODE_ATTEMPT_MAX_SLEEP_TIME = 5000; // milliseconds 232 233 /********************** 234 * Internal resources 235 **********************/ 236 private static final Logger LOG = LoggerFactory.getLogger(HBaseFsck.class.getName()); 237 private ClusterMetrics status; 238 private ClusterConnection connection; 239 private Admin admin; 240 private Table meta; 241 // threads to do ||izable tasks: retrieve data from regionservers, handle overlapping regions 242 protected ExecutorService executor; 243 private long startMillis = EnvironmentEdgeManager.currentTime(); 244 private HFileCorruptionChecker hfcc; 245 private int retcode = 0; 246 private Path HBCK_LOCK_PATH; 247 private FSDataOutputStream hbckOutFd; 248 // This lock is to prevent cleanup of balancer resources twice between 249 // ShutdownHook and the main code. We cleanup only if the connect() is 250 // successful 251 private final AtomicBoolean hbckLockCleanup = new AtomicBoolean(false); 252 253 // Unsupported options in HBase 2.0+ 254 private static final Set<String> unsupportedOptionsInV2 = Sets.newHashSet("-fix", 255 "-fixAssignments", "-fixMeta", "-fixHdfsHoles", "-fixHdfsOrphans", "-fixTableOrphans", 256 "-fixHdfsOverlaps", "-sidelineBigOverlaps", "-fixSplitParents", "-removeParents", 257 "-fixEmptyMetaCells", "-repair", "-repairHoles", "-maxOverlapsToSideline", "-maxMerge"); 258 259 /*********** 260 * Options 261 ***********/ 262 private static boolean details = false; // do we display the full report 263 private long timelag = DEFAULT_TIME_LAG; // tables whose modtime is older 264 private static boolean forceExclusive = false; // only this hbck can modify HBase 265 private boolean fixAssignments = false; // fix assignment errors? 266 private boolean fixMeta = false; // fix meta errors? 267 private boolean checkHdfs = true; // load and check fs consistency? 268 private boolean fixHdfsHoles = false; // fix fs holes? 269 private boolean fixHdfsOverlaps = false; // fix fs overlaps (risky) 270 private boolean fixHdfsOrphans = false; // fix fs holes (missing .regioninfo) 271 private boolean fixTableOrphans = false; // fix fs holes (missing .tableinfo) 272 private boolean fixVersionFile = false; // fix missing hbase.version file in hdfs 273 private boolean fixSplitParents = false; // fix lingering split parents 274 private boolean removeParents = false; // remove split parents 275 private boolean fixReferenceFiles = false; // fix lingering reference store file 276 private boolean fixHFileLinks = false; // fix lingering HFileLinks 277 private boolean fixEmptyMetaCells = false; // fix (remove) empty REGIONINFO_QUALIFIER rows 278 private boolean fixReplication = false; // fix undeleted replication queues for removed peer 279 private boolean fixAny = false; // Set to true if any of the fix is required. 280 281 // limit checking/fixes to listed tables, if empty attempt to check/fix all 282 // hbase:meta are always checked 283 private Set<TableName> tablesIncluded = new HashSet<>(); 284 private int maxMerge = DEFAULT_MAX_MERGE; // maximum number of overlapping regions to merge 285 // maximum number of overlapping regions to sideline 286 private int maxOverlapsToSideline = DEFAULT_OVERLAPS_TO_SIDELINE; 287 private boolean sidelineBigOverlaps = false; // sideline overlaps with >maxMerge regions 288 private Path sidelineDir = null; 289 290 private boolean rerun = false; // if we tried to fix something, rerun hbck 291 private static boolean summary = false; // if we want to print less output 292 private boolean checkMetaOnly = false; 293 private boolean checkRegionBoundaries = false; 294 private boolean ignorePreCheckPermission = false; // if pre-check permission 295 296 /********* 297 * State 298 *********/ 299 final private ErrorReporter errors; 300 int fixes = 0; 301 302 /** 303 * This map contains the state of all hbck items. It maps from encoded region 304 * name to HbckInfo structure. The information contained in HbckInfo is used 305 * to detect and correct consistency (hdfs/meta/deployment) problems. 306 */ 307 private TreeMap<String, HbckInfo> regionInfoMap = new TreeMap<>(); 308 // Empty regioninfo qualifiers in hbase:meta 309 private Set<Result> emptyRegionInfoQualifiers = new HashSet<>(); 310 311 /** 312 * This map from Tablename -> TableInfo contains the structures necessary to 313 * detect table consistency problems (holes, dupes, overlaps). It is sorted 314 * to prevent dupes. 315 * 316 * If tablesIncluded is empty, this map contains all tables. 317 * Otherwise, it contains only meta tables and tables in tablesIncluded, 318 * unless checkMetaOnly is specified, in which case, it contains only 319 * the meta table 320 */ 321 private SortedMap<TableName, TableInfo> tablesInfo = new ConcurrentSkipListMap<>(); 322 323 /** 324 * When initially looking at HDFS, we attempt to find any orphaned data. 325 */ 326 private List<HbckInfo> orphanHdfsDirs = Collections.synchronizedList(new ArrayList<HbckInfo>()); 327 328 private Map<TableName, Set<String>> orphanTableDirs = new HashMap<>(); 329 private Map<TableName, TableState> tableStates = new HashMap<>(); 330 private final RetryCounterFactory lockFileRetryCounterFactory; 331 private final RetryCounterFactory createZNodeRetryCounterFactory; 332 333 private Map<TableName, Set<String>> skippedRegions = new HashMap<>(); 334 335 private ZKWatcher zkw = null; 336 private String hbckEphemeralNodePath = null; 337 private boolean hbckZodeCreated = false; 338 339 /** 340 * Constructor 341 * 342 * @param conf Configuration object 343 * @throws MasterNotRunningException if the master is not running 344 * @throws ZooKeeperConnectionException if unable to connect to ZooKeeper 345 */ 346 public HBaseFsck(Configuration conf) throws MasterNotRunningException, 347 ZooKeeperConnectionException, IOException, ClassNotFoundException { 348 this(conf, createThreadPool(conf)); 349 } 350 351 private static ExecutorService createThreadPool(Configuration conf) { 352 int numThreads = conf.getInt("hbasefsck.numthreads", MAX_NUM_THREADS); 353 return new ScheduledThreadPoolExecutor(numThreads, Threads.newDaemonThreadFactory("hbasefsck")); 354 } 355 356 /** 357 * Constructor 358 * 359 * @param conf 360 * Configuration object 361 * @throws MasterNotRunningException 362 * if the master is not running 363 * @throws ZooKeeperConnectionException 364 * if unable to connect to ZooKeeper 365 */ 366 public HBaseFsck(Configuration conf, ExecutorService exec) throws MasterNotRunningException, 367 ZooKeeperConnectionException, IOException, ClassNotFoundException { 368 super(conf); 369 errors = getErrorReporter(getConf()); 370 this.executor = exec; 371 lockFileRetryCounterFactory = createLockRetryCounterFactory(getConf()); 372 createZNodeRetryCounterFactory = createZnodeRetryCounterFactory(getConf()); 373 zkw = createZooKeeperWatcher(); 374 } 375 376 /** 377 * @return A retry counter factory configured for retrying lock file creation. 378 */ 379 public static RetryCounterFactory createLockRetryCounterFactory(Configuration conf) { 380 return new RetryCounterFactory( 381 conf.getInt("hbase.hbck.lockfile.attempts", DEFAULT_MAX_LOCK_FILE_ATTEMPTS), 382 conf.getInt("hbase.hbck.lockfile.attempt.sleep.interval", 383 DEFAULT_LOCK_FILE_ATTEMPT_SLEEP_INTERVAL), 384 conf.getInt("hbase.hbck.lockfile.attempt.maxsleeptime", 385 DEFAULT_LOCK_FILE_ATTEMPT_MAX_SLEEP_TIME)); 386 } 387 388 /** 389 * @return A retry counter factory configured for retrying znode creation. 390 */ 391 private static RetryCounterFactory createZnodeRetryCounterFactory(Configuration conf) { 392 return new RetryCounterFactory( 393 conf.getInt("hbase.hbck.createznode.attempts", DEFAULT_MAX_CREATE_ZNODE_ATTEMPTS), 394 conf.getInt("hbase.hbck.createznode.attempt.sleep.interval", 395 DEFAULT_CREATE_ZNODE_ATTEMPT_SLEEP_INTERVAL), 396 conf.getInt("hbase.hbck.createznode.attempt.maxsleeptime", 397 DEFAULT_CREATE_ZNODE_ATTEMPT_MAX_SLEEP_TIME)); 398 } 399 400 /** 401 * @return Return the tmp dir this tool writes too. 402 */ 403 @VisibleForTesting 404 public static Path getTmpDir(Configuration conf) throws IOException { 405 return new Path(FSUtils.getRootDir(conf), HConstants.HBASE_TEMP_DIRECTORY); 406 } 407 408 private static class FileLockCallable implements Callable<FSDataOutputStream> { 409 RetryCounter retryCounter; 410 private final Configuration conf; 411 private Path hbckLockPath = null; 412 413 public FileLockCallable(Configuration conf, RetryCounter retryCounter) { 414 this.retryCounter = retryCounter; 415 this.conf = conf; 416 } 417 418 /** 419 * @return Will be <code>null</code> unless you call {@link #call()} 420 */ 421 Path getHbckLockPath() { 422 return this.hbckLockPath; 423 } 424 425 @Override 426 public FSDataOutputStream call() throws IOException { 427 try { 428 FileSystem fs = FSUtils.getCurrentFileSystem(this.conf); 429 FsPermission defaultPerms = FSUtils.getFilePermissions(fs, this.conf, 430 HConstants.DATA_FILE_UMASK_KEY); 431 Path tmpDir = getTmpDir(conf); 432 this.hbckLockPath = new Path(tmpDir, HBCK_LOCK_FILE); 433 fs.mkdirs(tmpDir); 434 final FSDataOutputStream out = createFileWithRetries(fs, this.hbckLockPath, defaultPerms); 435 out.writeBytes(InetAddress.getLocalHost().toString()); 436 // Add a note into the file we write on why hbase2 is writing out an hbck1 lock file. 437 out.writeBytes(" Written by an hbase-2.x Master to block an " + 438 "attempt by an hbase-1.x HBCK tool making modification to state. " + 439 "See 'HBCK must match HBase server version' in the hbase refguide."); 440 out.flush(); 441 return out; 442 } catch(RemoteException e) { 443 if(AlreadyBeingCreatedException.class.getName().equals(e.getClassName())){ 444 return null; 445 } else { 446 throw e; 447 } 448 } 449 } 450 451 private FSDataOutputStream createFileWithRetries(final FileSystem fs, 452 final Path hbckLockFilePath, final FsPermission defaultPerms) 453 throws IOException { 454 IOException exception = null; 455 do { 456 try { 457 return FSUtils.create(fs, hbckLockFilePath, defaultPerms, false); 458 } catch (IOException ioe) { 459 LOG.info("Failed to create lock file " + hbckLockFilePath.getName() 460 + ", try=" + (retryCounter.getAttemptTimes() + 1) + " of " 461 + retryCounter.getMaxAttempts()); 462 LOG.debug("Failed to create lock file " + hbckLockFilePath.getName(), 463 ioe); 464 try { 465 exception = ioe; 466 retryCounter.sleepUntilNextRetry(); 467 } catch (InterruptedException ie) { 468 throw (InterruptedIOException) new InterruptedIOException( 469 "Can't create lock file " + hbckLockFilePath.getName()) 470 .initCause(ie); 471 } 472 } 473 } while (retryCounter.shouldRetry()); 474 475 throw exception; 476 } 477 } 478 479 /** 480 * This method maintains a lock using a file. If the creation fails we return null 481 * 482 * @return FSDataOutputStream object corresponding to the newly opened lock file 483 * @throws IOException if IO failure occurs 484 */ 485 public static Pair<Path, FSDataOutputStream> checkAndMarkRunningHbck(Configuration conf, 486 RetryCounter retryCounter) throws IOException { 487 FileLockCallable callable = new FileLockCallable(conf, retryCounter); 488 ExecutorService executor = Executors.newFixedThreadPool(1); 489 FutureTask<FSDataOutputStream> futureTask = new FutureTask<>(callable); 490 executor.execute(futureTask); 491 final int timeoutInSeconds = conf.getInt( 492 "hbase.hbck.lockfile.maxwaittime", DEFAULT_WAIT_FOR_LOCK_TIMEOUT); 493 FSDataOutputStream stream = null; 494 try { 495 stream = futureTask.get(timeoutInSeconds, TimeUnit.SECONDS); 496 } catch (ExecutionException ee) { 497 LOG.warn("Encountered exception when opening lock file", ee); 498 } catch (InterruptedException ie) { 499 LOG.warn("Interrupted when opening lock file", ie); 500 Thread.currentThread().interrupt(); 501 } catch (TimeoutException exception) { 502 // took too long to obtain lock 503 LOG.warn("Took more than " + timeoutInSeconds + " seconds in obtaining lock"); 504 futureTask.cancel(true); 505 } finally { 506 executor.shutdownNow(); 507 } 508 return new Pair<Path, FSDataOutputStream>(callable.getHbckLockPath(), stream); 509 } 510 511 private void unlockHbck() { 512 if (isExclusive() && hbckLockCleanup.compareAndSet(true, false)) { 513 RetryCounter retryCounter = lockFileRetryCounterFactory.create(); 514 do { 515 try { 516 IOUtils.closeQuietly(hbckOutFd); 517 FSUtils.delete(FSUtils.getCurrentFileSystem(getConf()), HBCK_LOCK_PATH, true); 518 LOG.info("Finishing hbck"); 519 return; 520 } catch (IOException ioe) { 521 LOG.info("Failed to delete " + HBCK_LOCK_PATH + ", try=" 522 + (retryCounter.getAttemptTimes() + 1) + " of " 523 + retryCounter.getMaxAttempts()); 524 LOG.debug("Failed to delete " + HBCK_LOCK_PATH, ioe); 525 try { 526 retryCounter.sleepUntilNextRetry(); 527 } catch (InterruptedException ie) { 528 Thread.currentThread().interrupt(); 529 LOG.warn("Interrupted while deleting lock file" + 530 HBCK_LOCK_PATH); 531 return; 532 } 533 } 534 } while (retryCounter.shouldRetry()); 535 } 536 } 537 538 /** 539 * To repair region consistency, one must call connect() in order to repair 540 * online state. 541 */ 542 public void connect() throws IOException { 543 544 if (isExclusive()) { 545 // Grab the lock 546 Pair<Path, FSDataOutputStream> pair = 547 checkAndMarkRunningHbck(getConf(), this.lockFileRetryCounterFactory.create()); 548 HBCK_LOCK_PATH = pair.getFirst(); 549 this.hbckOutFd = pair.getSecond(); 550 if (hbckOutFd == null) { 551 setRetCode(-1); 552 LOG.error("Another instance of hbck is fixing HBase, exiting this instance. " + 553 "[If you are sure no other instance is running, delete the lock file " + 554 HBCK_LOCK_PATH + " and rerun the tool]"); 555 throw new IOException("Duplicate hbck - Abort"); 556 } 557 558 // Make sure to cleanup the lock 559 hbckLockCleanup.set(true); 560 } 561 562 563 // Add a shutdown hook to this thread, in case user tries to 564 // kill the hbck with a ctrl-c, we want to cleanup the lock so that 565 // it is available for further calls 566 Runtime.getRuntime().addShutdownHook(new Thread() { 567 @Override 568 public void run() { 569 IOUtils.closeQuietly(HBaseFsck.this); 570 cleanupHbckZnode(); 571 unlockHbck(); 572 } 573 }); 574 575 LOG.info("Launching hbck"); 576 577 connection = (ClusterConnection)ConnectionFactory.createConnection(getConf()); 578 admin = connection.getAdmin(); 579 meta = connection.getTable(TableName.META_TABLE_NAME); 580 status = admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS, 581 Option.DEAD_SERVERS, Option.MASTER, Option.BACKUP_MASTERS, 582 Option.REGIONS_IN_TRANSITION, Option.HBASE_VERSION)); 583 } 584 585 /** 586 * Get deployed regions according to the region servers. 587 */ 588 private void loadDeployedRegions() throws IOException, InterruptedException { 589 // From the master, get a list of all known live region servers 590 Collection<ServerName> regionServers = status.getLiveServerMetrics().keySet(); 591 errors.print("Number of live region servers: " + regionServers.size()); 592 if (details) { 593 for (ServerName rsinfo: regionServers) { 594 errors.print(" " + rsinfo.getServerName()); 595 } 596 } 597 598 // From the master, get a list of all dead region servers 599 Collection<ServerName> deadRegionServers = status.getDeadServerNames(); 600 errors.print("Number of dead region servers: " + deadRegionServers.size()); 601 if (details) { 602 for (ServerName name: deadRegionServers) { 603 errors.print(" " + name); 604 } 605 } 606 607 // Print the current master name and state 608 errors.print("Master: " + status.getMasterName()); 609 610 // Print the list of all backup masters 611 Collection<ServerName> backupMasters = status.getBackupMasterNames(); 612 errors.print("Number of backup masters: " + backupMasters.size()); 613 if (details) { 614 for (ServerName name: backupMasters) { 615 errors.print(" " + name); 616 } 617 } 618 619 errors.print("Average load: " + status.getAverageLoad()); 620 errors.print("Number of requests: " + status.getRequestCount()); 621 errors.print("Number of regions: " + status.getRegionCount()); 622 623 List<RegionState> rits = status.getRegionStatesInTransition(); 624 errors.print("Number of regions in transition: " + rits.size()); 625 if (details) { 626 for (RegionState state: rits) { 627 errors.print(" " + state.toDescriptiveString()); 628 } 629 } 630 631 // Determine what's deployed 632 processRegionServers(regionServers); 633 } 634 635 /** 636 * Clear the current state of hbck. 637 */ 638 private void clearState() { 639 // Make sure regionInfo is empty before starting 640 fixes = 0; 641 regionInfoMap.clear(); 642 emptyRegionInfoQualifiers.clear(); 643 tableStates.clear(); 644 errors.clear(); 645 tablesInfo.clear(); 646 orphanHdfsDirs.clear(); 647 skippedRegions.clear(); 648 } 649 650 /** 651 * This repair method analyzes hbase data in hdfs and repairs it to satisfy 652 * the table integrity rules. HBase doesn't need to be online for this 653 * operation to work. 654 */ 655 public void offlineHdfsIntegrityRepair() throws IOException, InterruptedException { 656 // Initial pass to fix orphans. 657 if (shouldCheckHdfs() && (shouldFixHdfsOrphans() || shouldFixHdfsHoles() 658 || shouldFixHdfsOverlaps() || shouldFixTableOrphans())) { 659 LOG.info("Loading regioninfos HDFS"); 660 // if nothing is happening this should always complete in two iterations. 661 int maxIterations = getConf().getInt("hbase.hbck.integrityrepair.iterations.max", 3); 662 int curIter = 0; 663 do { 664 clearState(); // clears hbck state and reset fixes to 0 and. 665 // repair what's on HDFS 666 restoreHdfsIntegrity(); 667 curIter++;// limit the number of iterations. 668 } while (fixes > 0 && curIter <= maxIterations); 669 670 // Repairs should be done in the first iteration and verification in the second. 671 // If there are more than 2 passes, something funny has happened. 672 if (curIter > 2) { 673 if (curIter == maxIterations) { 674 LOG.warn("Exiting integrity repairs after max " + curIter + " iterations. " 675 + "Tables integrity may not be fully repaired!"); 676 } else { 677 LOG.info("Successfully exiting integrity repairs after " + curIter + " iterations"); 678 } 679 } 680 } 681 } 682 683 /** 684 * This repair method requires the cluster to be online since it contacts 685 * region servers and the masters. It makes each region's state in HDFS, in 686 * hbase:meta, and deployments consistent. 687 * 688 * @return If > 0 , number of errors detected, if < 0 there was an unrecoverable 689 * error. If 0, we have a clean hbase. 690 */ 691 public int onlineConsistencyRepair() throws IOException, KeeperException, 692 InterruptedException { 693 694 // get regions according to what is online on each RegionServer 695 loadDeployedRegions(); 696 // check whether hbase:meta is deployed and online 697 recordMetaRegion(); 698 // Check if hbase:meta is found only once and in the right place 699 if (!checkMetaRegion()) { 700 String errorMsg = "hbase:meta table is not consistent. "; 701 if (shouldFixAssignments()) { 702 errorMsg += "HBCK will try fixing it. Rerun once hbase:meta is back to consistent state."; 703 } else { 704 errorMsg += "Run HBCK with proper fix options to fix hbase:meta inconsistency."; 705 } 706 errors.reportError(errorMsg + " Exiting..."); 707 return -2; 708 } 709 // Not going with further consistency check for tables when hbase:meta itself is not consistent. 710 LOG.info("Loading regionsinfo from the hbase:meta table"); 711 boolean success = loadMetaEntries(); 712 if (!success) return -1; 713 714 // Empty cells in hbase:meta? 715 reportEmptyMetaCells(); 716 717 // Check if we have to cleanup empty REGIONINFO_QUALIFIER rows from hbase:meta 718 if (shouldFixEmptyMetaCells()) { 719 fixEmptyMetaCells(); 720 } 721 722 // get a list of all tables that have not changed recently. 723 if (!checkMetaOnly) { 724 reportTablesInFlux(); 725 } 726 727 // Get disabled tables states 728 loadTableStates(); 729 730 // load regiondirs and regioninfos from HDFS 731 if (shouldCheckHdfs()) { 732 LOG.info("Loading region directories from HDFS"); 733 loadHdfsRegionDirs(); 734 LOG.info("Loading region information from HDFS"); 735 loadHdfsRegionInfos(); 736 } 737 738 // fix the orphan tables 739 fixOrphanTables(); 740 741 LOG.info("Checking and fixing region consistency"); 742 // Check and fix consistency 743 checkAndFixConsistency(); 744 745 // Check integrity (does not fix) 746 checkIntegrity(); 747 return errors.getErrorList().size(); 748 } 749 750 /** 751 * This method maintains an ephemeral znode. If the creation fails we return false or throw 752 * exception 753 * 754 * @return true if creating znode succeeds; false otherwise 755 * @throws IOException if IO failure occurs 756 */ 757 private boolean setMasterInMaintenanceMode() throws IOException { 758 RetryCounter retryCounter = createZNodeRetryCounterFactory.create(); 759 hbckEphemeralNodePath = ZNodePaths.joinZNode( 760 zkw.znodePaths.masterMaintZNode, 761 "hbck-" + Long.toString(EnvironmentEdgeManager.currentTime())); 762 do { 763 try { 764 hbckZodeCreated = ZKUtil.createEphemeralNodeAndWatch(zkw, hbckEphemeralNodePath, null); 765 if (hbckZodeCreated) { 766 break; 767 } 768 } catch (KeeperException e) { 769 if (retryCounter.getAttemptTimes() >= retryCounter.getMaxAttempts()) { 770 throw new IOException("Can't create znode " + hbckEphemeralNodePath, e); 771 } 772 // fall through and retry 773 } 774 775 LOG.warn("Fail to create znode " + hbckEphemeralNodePath + ", try=" + 776 (retryCounter.getAttemptTimes() + 1) + " of " + retryCounter.getMaxAttempts()); 777 778 try { 779 retryCounter.sleepUntilNextRetry(); 780 } catch (InterruptedException ie) { 781 throw (InterruptedIOException) new InterruptedIOException( 782 "Can't create znode " + hbckEphemeralNodePath).initCause(ie); 783 } 784 } while (retryCounter.shouldRetry()); 785 return hbckZodeCreated; 786 } 787 788 private void cleanupHbckZnode() { 789 try { 790 if (zkw != null && hbckZodeCreated) { 791 ZKUtil.deleteNode(zkw, hbckEphemeralNodePath); 792 hbckZodeCreated = false; 793 } 794 } catch (KeeperException e) { 795 // Ignore 796 if (!e.code().equals(KeeperException.Code.NONODE)) { 797 LOG.warn("Delete HBCK znode " + hbckEphemeralNodePath + " failed ", e); 798 } 799 } 800 } 801 802 /** 803 * Contacts the master and prints out cluster-wide information 804 * @return 0 on success, non-zero on failure 805 */ 806 public int onlineHbck() 807 throws IOException, KeeperException, InterruptedException { 808 // print hbase server version 809 errors.print("Version: " + status.getHBaseVersion()); 810 811 // Clean start 812 clearState(); 813 // Do offline check and repair first 814 offlineHdfsIntegrityRepair(); 815 offlineReferenceFileRepair(); 816 offlineHLinkFileRepair(); 817 // If Master runs maintenance tasks (such as balancer, catalog janitor, etc) during online 818 // hbck, it is likely that hbck would be misled and report transient errors. Therefore, it 819 // is better to set Master into maintenance mode during online hbck. 820 // 821 if (!setMasterInMaintenanceMode()) { 822 LOG.warn("HBCK is running while master is not in maintenance mode, you might see transient " 823 + "error. Please run HBCK multiple times to reduce the chance of transient error."); 824 } 825 826 onlineConsistencyRepair(); 827 828 if (checkRegionBoundaries) { 829 checkRegionBoundaries(); 830 } 831 832 checkAndFixReplication(); 833 834 // Remove the hbck znode 835 cleanupHbckZnode(); 836 837 // Remove the hbck lock 838 unlockHbck(); 839 840 // Print table summary 841 printTableSummary(tablesInfo); 842 return errors.summarize(); 843 } 844 845 public static byte[] keyOnly (byte[] b) { 846 if (b == null) 847 return b; 848 int rowlength = Bytes.toShort(b, 0); 849 byte[] result = new byte[rowlength]; 850 System.arraycopy(b, Bytes.SIZEOF_SHORT, result, 0, rowlength); 851 return result; 852 } 853 854 @Override 855 public void close() throws IOException { 856 try { 857 cleanupHbckZnode(); 858 unlockHbck(); 859 } catch (Exception io) { 860 LOG.warn(io.toString(), io); 861 } finally { 862 if (zkw != null) { 863 zkw.close(); 864 zkw = null; 865 } 866 IOUtils.closeQuietly(admin); 867 IOUtils.closeQuietly(meta); 868 IOUtils.closeQuietly(connection); 869 } 870 } 871 872 private static class RegionBoundariesInformation { 873 public byte [] regionName; 874 public byte [] metaFirstKey; 875 public byte [] metaLastKey; 876 public byte [] storesFirstKey; 877 public byte [] storesLastKey; 878 @Override 879 public String toString () { 880 return "regionName=" + Bytes.toStringBinary(regionName) + 881 "\nmetaFirstKey=" + Bytes.toStringBinary(metaFirstKey) + 882 "\nmetaLastKey=" + Bytes.toStringBinary(metaLastKey) + 883 "\nstoresFirstKey=" + Bytes.toStringBinary(storesFirstKey) + 884 "\nstoresLastKey=" + Bytes.toStringBinary(storesLastKey); 885 } 886 } 887 888 public void checkRegionBoundaries() { 889 try { 890 ByteArrayComparator comparator = new ByteArrayComparator(); 891 List<RegionInfo> regions = MetaTableAccessor.getAllRegions(connection, true); 892 final RegionBoundariesInformation currentRegionBoundariesInformation = 893 new RegionBoundariesInformation(); 894 Path hbaseRoot = FSUtils.getRootDir(getConf()); 895 for (RegionInfo regionInfo : regions) { 896 Path tableDir = FSUtils.getTableDir(hbaseRoot, regionInfo.getTable()); 897 currentRegionBoundariesInformation.regionName = regionInfo.getRegionName(); 898 // For each region, get the start and stop key from the META and compare them to the 899 // same information from the Stores. 900 Path path = new Path(tableDir, regionInfo.getEncodedName()); 901 FileSystem fs = path.getFileSystem(getConf()); 902 FileStatus[] files = fs.listStatus(path); 903 // For all the column families in this region... 904 byte[] storeFirstKey = null; 905 byte[] storeLastKey = null; 906 for (FileStatus file : files) { 907 String fileName = file.getPath().toString(); 908 fileName = fileName.substring(fileName.lastIndexOf("/") + 1); 909 if (!fileName.startsWith(".") && !fileName.endsWith("recovered.edits")) { 910 FileStatus[] storeFiles = fs.listStatus(file.getPath()); 911 // For all the stores in this column family. 912 for (FileStatus storeFile : storeFiles) { 913 HFile.Reader reader = HFile.createReader(fs, storeFile.getPath(), 914 new CacheConfig(getConf()), true, getConf()); 915 if ((reader.getFirstKey() != null) 916 && ((storeFirstKey == null) || (comparator.compare(storeFirstKey, 917 ((KeyValue.KeyOnlyKeyValue) reader.getFirstKey().get()).getKey()) > 0))) { 918 storeFirstKey = ((KeyValue.KeyOnlyKeyValue)reader.getFirstKey().get()).getKey(); 919 } 920 if ((reader.getLastKey() != null) 921 && ((storeLastKey == null) || (comparator.compare(storeLastKey, 922 ((KeyValue.KeyOnlyKeyValue)reader.getLastKey().get()).getKey())) < 0)) { 923 storeLastKey = ((KeyValue.KeyOnlyKeyValue)reader.getLastKey().get()).getKey(); 924 } 925 reader.close(); 926 } 927 } 928 } 929 currentRegionBoundariesInformation.metaFirstKey = regionInfo.getStartKey(); 930 currentRegionBoundariesInformation.metaLastKey = regionInfo.getEndKey(); 931 currentRegionBoundariesInformation.storesFirstKey = keyOnly(storeFirstKey); 932 currentRegionBoundariesInformation.storesLastKey = keyOnly(storeLastKey); 933 if (currentRegionBoundariesInformation.metaFirstKey.length == 0) 934 currentRegionBoundariesInformation.metaFirstKey = null; 935 if (currentRegionBoundariesInformation.metaLastKey.length == 0) 936 currentRegionBoundariesInformation.metaLastKey = null; 937 938 // For a region to be correct, we need the META start key to be smaller or equal to the 939 // smallest start key from all the stores, and the start key from the next META entry to 940 // be bigger than the last key from all the current stores. First region start key is null; 941 // Last region end key is null; some regions can be empty and not have any store. 942 943 boolean valid = true; 944 // Checking start key. 945 if ((currentRegionBoundariesInformation.storesFirstKey != null) 946 && (currentRegionBoundariesInformation.metaFirstKey != null)) { 947 valid = valid 948 && comparator.compare(currentRegionBoundariesInformation.storesFirstKey, 949 currentRegionBoundariesInformation.metaFirstKey) >= 0; 950 } 951 // Checking stop key. 952 if ((currentRegionBoundariesInformation.storesLastKey != null) 953 && (currentRegionBoundariesInformation.metaLastKey != null)) { 954 valid = valid 955 && comparator.compare(currentRegionBoundariesInformation.storesLastKey, 956 currentRegionBoundariesInformation.metaLastKey) < 0; 957 } 958 if (!valid) { 959 errors.reportError(ERROR_CODE.BOUNDARIES_ERROR, "Found issues with regions boundaries", 960 tablesInfo.get(regionInfo.getTable())); 961 LOG.warn("Region's boundaries not aligned between stores and META for:"); 962 LOG.warn(Objects.toString(currentRegionBoundariesInformation)); 963 } 964 } 965 } catch (IOException e) { 966 LOG.error(e.toString(), e); 967 } 968 } 969 970 /** 971 * Iterates through the list of all orphan/invalid regiondirs. 972 */ 973 private void adoptHdfsOrphans(Collection<HbckInfo> orphanHdfsDirs) throws IOException { 974 for (HbckInfo hi : orphanHdfsDirs) { 975 LOG.info("Attempting to handle orphan hdfs dir: " + hi.getHdfsRegionDir()); 976 adoptHdfsOrphan(hi); 977 } 978 } 979 980 /** 981 * Orphaned regions are regions without a .regioninfo file in them. We "adopt" 982 * these orphans by creating a new region, and moving the column families, 983 * recovered edits, WALs, into the new region dir. We determine the region 984 * startkey and endkeys by looking at all of the hfiles inside the column 985 * families to identify the min and max keys. The resulting region will 986 * likely violate table integrity but will be dealt with by merging 987 * overlapping regions. 988 */ 989 @SuppressWarnings("deprecation") 990 private void adoptHdfsOrphan(HbckInfo hi) throws IOException { 991 Path p = hi.getHdfsRegionDir(); 992 FileSystem fs = p.getFileSystem(getConf()); 993 FileStatus[] dirs = fs.listStatus(p); 994 if (dirs == null) { 995 LOG.warn("Attempt to adopt orphan hdfs region skipped because no files present in " + 996 p + ". This dir could probably be deleted."); 997 return ; 998 } 999 1000 TableName tableName = hi.getTableName(); 1001 TableInfo tableInfo = tablesInfo.get(tableName); 1002 Preconditions.checkNotNull(tableInfo, "Table '" + tableName + "' not present!"); 1003 TableDescriptor template = tableInfo.getHTD(); 1004 1005 // find min and max key values 1006 Pair<byte[],byte[]> orphanRegionRange = null; 1007 for (FileStatus cf : dirs) { 1008 String cfName= cf.getPath().getName(); 1009 // TODO Figure out what the special dirs are 1010 if (cfName.startsWith(".") || cfName.equals(HConstants.SPLIT_LOGDIR_NAME)) continue; 1011 1012 FileStatus[] hfiles = fs.listStatus(cf.getPath()); 1013 for (FileStatus hfile : hfiles) { 1014 byte[] start, end; 1015 HFile.Reader hf = null; 1016 try { 1017 CacheConfig cacheConf = new CacheConfig(getConf()); 1018 hf = HFile.createReader(fs, hfile.getPath(), cacheConf, true, getConf()); 1019 hf.loadFileInfo(); 1020 Optional<Cell> startKv = hf.getFirstKey(); 1021 start = CellUtil.cloneRow(startKv.get()); 1022 Optional<Cell> endKv = hf.getLastKey(); 1023 end = CellUtil.cloneRow(endKv.get()); 1024 } catch (IOException ioe) { 1025 LOG.warn("Problem reading orphan file " + hfile + ", skipping"); 1026 continue; 1027 } catch (NullPointerException ioe) { 1028 LOG.warn("Orphan file " + hfile + " is possibly corrupted HFile, skipping"); 1029 continue; 1030 } finally { 1031 if (hf != null) { 1032 hf.close(); 1033 } 1034 } 1035 1036 // expand the range to include the range of all hfiles 1037 if (orphanRegionRange == null) { 1038 // first range 1039 orphanRegionRange = new Pair<>(start, end); 1040 } else { 1041 // TODO add test 1042 1043 // expand range only if the hfile is wider. 1044 if (Bytes.compareTo(orphanRegionRange.getFirst(), start) > 0) { 1045 orphanRegionRange.setFirst(start); 1046 } 1047 if (Bytes.compareTo(orphanRegionRange.getSecond(), end) < 0 ) { 1048 orphanRegionRange.setSecond(end); 1049 } 1050 } 1051 } 1052 } 1053 if (orphanRegionRange == null) { 1054 LOG.warn("No data in dir " + p + ", sidelining data"); 1055 fixes++; 1056 sidelineRegionDir(fs, hi); 1057 return; 1058 } 1059 LOG.info("Min max keys are : [" + Bytes.toString(orphanRegionRange.getFirst()) + ", " + 1060 Bytes.toString(orphanRegionRange.getSecond()) + ")"); 1061 1062 // create new region on hdfs. move data into place. 1063 RegionInfo regionInfo = RegionInfoBuilder.newBuilder(template.getTableName()) 1064 .setStartKey(orphanRegionRange.getFirst()) 1065 .setEndKey(Bytes.add(orphanRegionRange.getSecond(), new byte[1])) 1066 .build(); 1067 LOG.info("Creating new region : " + regionInfo); 1068 HRegion region = HBaseFsckRepair.createHDFSRegionDir(getConf(), regionInfo, template); 1069 Path target = region.getRegionFileSystem().getRegionDir(); 1070 1071 // rename all the data to new region 1072 mergeRegionDirs(target, hi); 1073 fixes++; 1074 } 1075 1076 /** 1077 * This method determines if there are table integrity errors in HDFS. If 1078 * there are errors and the appropriate "fix" options are enabled, the method 1079 * will first correct orphan regions making them into legit regiondirs, and 1080 * then reload to merge potentially overlapping regions. 1081 * 1082 * @return number of table integrity errors found 1083 */ 1084 private int restoreHdfsIntegrity() throws IOException, InterruptedException { 1085 // Determine what's on HDFS 1086 LOG.info("Loading HBase regioninfo from HDFS..."); 1087 loadHdfsRegionDirs(); // populating regioninfo table. 1088 1089 int errs = errors.getErrorList().size(); 1090 // First time just get suggestions. 1091 tablesInfo = loadHdfsRegionInfos(); // update tableInfos based on region info in fs. 1092 checkHdfsIntegrity(false, false); 1093 1094 if (errors.getErrorList().size() == errs) { 1095 LOG.info("No integrity errors. We are done with this phase. Glorious."); 1096 return 0; 1097 } 1098 1099 if (shouldFixHdfsOrphans() && orphanHdfsDirs.size() > 0) { 1100 adoptHdfsOrphans(orphanHdfsDirs); 1101 // TODO optimize by incrementally adding instead of reloading. 1102 } 1103 1104 // Make sure there are no holes now. 1105 if (shouldFixHdfsHoles()) { 1106 clearState(); // this also resets # fixes. 1107 loadHdfsRegionDirs(); 1108 tablesInfo = loadHdfsRegionInfos(); // update tableInfos based on region info in fs. 1109 tablesInfo = checkHdfsIntegrity(shouldFixHdfsHoles(), false); 1110 } 1111 1112 // Now we fix overlaps 1113 if (shouldFixHdfsOverlaps()) { 1114 // second pass we fix overlaps. 1115 clearState(); // this also resets # fixes. 1116 loadHdfsRegionDirs(); 1117 tablesInfo = loadHdfsRegionInfos(); // update tableInfos based on region info in fs. 1118 tablesInfo = checkHdfsIntegrity(false, shouldFixHdfsOverlaps()); 1119 } 1120 1121 return errors.getErrorList().size(); 1122 } 1123 1124 /** 1125 * Scan all the store file names to find any lingering reference files, 1126 * which refer to some none-exiting files. If "fix" option is enabled, 1127 * any lingering reference file will be sidelined if found. 1128 * <p> 1129 * Lingering reference file prevents a region from opening. It has to 1130 * be fixed before a cluster can start properly. 1131 */ 1132 private void offlineReferenceFileRepair() throws IOException, InterruptedException { 1133 clearState(); 1134 Configuration conf = getConf(); 1135 Path hbaseRoot = FSUtils.getRootDir(conf); 1136 FileSystem fs = hbaseRoot.getFileSystem(conf); 1137 LOG.info("Computing mapping of all store files"); 1138 Map<String, Path> allFiles = FSUtils.getTableStoreFilePathMap(fs, hbaseRoot, 1139 new FSUtils.ReferenceFileFilter(fs), executor, errors); 1140 errors.print(""); 1141 LOG.info("Validating mapping using HDFS state"); 1142 for (Path path: allFiles.values()) { 1143 Path referredToFile = StoreFileInfo.getReferredToFile(path); 1144 if (fs.exists(referredToFile)) continue; // good, expected 1145 1146 // Found a lingering reference file 1147 errors.reportError(ERROR_CODE.LINGERING_REFERENCE_HFILE, 1148 "Found lingering reference file " + path); 1149 if (!shouldFixReferenceFiles()) continue; 1150 1151 // Now, trying to fix it since requested 1152 boolean success = false; 1153 String pathStr = path.toString(); 1154 1155 // A reference file path should be like 1156 // ${hbase.rootdir}/data/namespace/table_name/region_id/family_name/referred_file.region_name 1157 // Up 5 directories to get the root folder. 1158 // So the file will be sidelined to a similar folder structure. 1159 int index = pathStr.lastIndexOf(Path.SEPARATOR_CHAR); 1160 for (int i = 0; index > 0 && i < 5; i++) { 1161 index = pathStr.lastIndexOf(Path.SEPARATOR_CHAR, index - 1); 1162 } 1163 if (index > 0) { 1164 Path rootDir = getSidelineDir(); 1165 Path dst = new Path(rootDir, pathStr.substring(index + 1)); 1166 fs.mkdirs(dst.getParent()); 1167 LOG.info("Trying to sideline reference file " 1168 + path + " to " + dst); 1169 setShouldRerun(); 1170 1171 success = fs.rename(path, dst); 1172 debugLsr(dst); 1173 1174 } 1175 if (!success) { 1176 LOG.error("Failed to sideline reference file " + path); 1177 } 1178 } 1179 } 1180 1181 /** 1182 * Scan all the store file names to find any lingering HFileLink files, 1183 * which refer to some none-exiting files. If "fix" option is enabled, 1184 * any lingering HFileLink file will be sidelined if found. 1185 */ 1186 private void offlineHLinkFileRepair() throws IOException, InterruptedException { 1187 Configuration conf = getConf(); 1188 Path hbaseRoot = FSUtils.getRootDir(conf); 1189 FileSystem fs = hbaseRoot.getFileSystem(conf); 1190 LOG.info("Computing mapping of all link files"); 1191 Map<String, Path> allFiles = FSUtils 1192 .getTableStoreFilePathMap(fs, hbaseRoot, new FSUtils.HFileLinkFilter(), executor, errors); 1193 errors.print(""); 1194 1195 LOG.info("Validating mapping using HDFS state"); 1196 for (Path path : allFiles.values()) { 1197 // building HFileLink object to gather locations 1198 HFileLink actualLink = HFileLink.buildFromHFileLinkPattern(conf, path); 1199 if (actualLink.exists(fs)) continue; // good, expected 1200 1201 // Found a lingering HFileLink 1202 errors.reportError(ERROR_CODE.LINGERING_HFILELINK, "Found lingering HFileLink " + path); 1203 if (!shouldFixHFileLinks()) continue; 1204 1205 // Now, trying to fix it since requested 1206 setShouldRerun(); 1207 1208 // An HFileLink path should be like 1209 // ${hbase.rootdir}/data/namespace/table_name/region_id/family_name/linkedtable=linkedregionname-linkedhfilename 1210 // sidelineing will happen in the ${hbase.rootdir}/${sidelinedir} directory with the same folder structure. 1211 boolean success = sidelineFile(fs, hbaseRoot, path); 1212 1213 if (!success) { 1214 LOG.error("Failed to sideline HFileLink file " + path); 1215 } 1216 1217 // An HFileLink backreference path should be like 1218 // ${hbase.rootdir}/archive/data/namespace/table_name/region_id/family_name/.links-linkedhfilename 1219 // sidelineing will happen in the ${hbase.rootdir}/${sidelinedir} directory with the same folder structure. 1220 Path backRefPath = FileLink.getBackReferencesDir(HFileArchiveUtil 1221 .getStoreArchivePath(conf, HFileLink.getReferencedTableName(path.getName().toString()), 1222 HFileLink.getReferencedRegionName(path.getName().toString()), 1223 path.getParent().getName()), 1224 HFileLink.getReferencedHFileName(path.getName().toString())); 1225 success = sidelineFile(fs, hbaseRoot, backRefPath); 1226 1227 if (!success) { 1228 LOG.error("Failed to sideline HFileLink backreference file " + path); 1229 } 1230 } 1231 } 1232 1233 private boolean sidelineFile(FileSystem fs, Path hbaseRoot, Path path) throws IOException { 1234 URI uri = hbaseRoot.toUri().relativize(path.toUri()); 1235 if (uri.isAbsolute()) return false; 1236 String relativePath = uri.getPath(); 1237 Path rootDir = getSidelineDir(); 1238 Path dst = new Path(rootDir, relativePath); 1239 boolean pathCreated = fs.mkdirs(dst.getParent()); 1240 if (!pathCreated) { 1241 LOG.error("Failed to create path: " + dst.getParent()); 1242 return false; 1243 } 1244 LOG.info("Trying to sideline file " + path + " to " + dst); 1245 return fs.rename(path, dst); 1246 } 1247 1248 /** 1249 * TODO -- need to add tests for this. 1250 */ 1251 private void reportEmptyMetaCells() { 1252 errors.print("Number of empty REGIONINFO_QUALIFIER rows in hbase:meta: " + 1253 emptyRegionInfoQualifiers.size()); 1254 if (details) { 1255 for (Result r: emptyRegionInfoQualifiers) { 1256 errors.print(" " + r); 1257 } 1258 } 1259 } 1260 1261 /** 1262 * TODO -- need to add tests for this. 1263 */ 1264 private void reportTablesInFlux() { 1265 AtomicInteger numSkipped = new AtomicInteger(0); 1266 TableDescriptor[] allTables = getTables(numSkipped); 1267 errors.print("Number of Tables: " + allTables.length); 1268 if (details) { 1269 if (numSkipped.get() > 0) { 1270 errors.detail("Number of Tables in flux: " + numSkipped.get()); 1271 } 1272 for (TableDescriptor td : allTables) { 1273 errors.detail(" Table: " + td.getTableName() + "\t" + 1274 (td.isReadOnly() ? "ro" : "rw") + "\t" + 1275 (td.isMetaRegion() ? "META" : " ") + "\t" + 1276 " families: " + td.getColumnFamilyCount()); 1277 } 1278 } 1279 } 1280 1281 public ErrorReporter getErrors() { 1282 return errors; 1283 } 1284 1285 /** 1286 * Read the .regioninfo file from the file system. If there is no 1287 * .regioninfo, add it to the orphan hdfs region list. 1288 */ 1289 private void loadHdfsRegioninfo(HbckInfo hbi) throws IOException { 1290 Path regionDir = hbi.getHdfsRegionDir(); 1291 if (regionDir == null) { 1292 if (hbi.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) { 1293 // Log warning only for default/ primary replica with no region dir 1294 LOG.warn("No HDFS region dir found: " + hbi + " meta=" + hbi.metaEntry); 1295 } 1296 return; 1297 } 1298 1299 if (hbi.hdfsEntry.hri != null) { 1300 // already loaded data 1301 return; 1302 } 1303 1304 FileSystem fs = FileSystem.get(getConf()); 1305 RegionInfo hri = HRegionFileSystem.loadRegionInfoFileContent(fs, regionDir); 1306 LOG.debug("RegionInfo read: " + hri.toString()); 1307 hbi.hdfsEntry.hri = hri; 1308 } 1309 1310 /** 1311 * Exception thrown when a integrity repair operation fails in an 1312 * unresolvable way. 1313 */ 1314 public static class RegionRepairException extends IOException { 1315 private static final long serialVersionUID = 1L; 1316 final IOException ioe; 1317 public RegionRepairException(String s, IOException ioe) { 1318 super(s); 1319 this.ioe = ioe; 1320 } 1321 } 1322 1323 /** 1324 * Populate hbi's from regionInfos loaded from file system. 1325 */ 1326 private SortedMap<TableName, TableInfo> loadHdfsRegionInfos() 1327 throws IOException, InterruptedException { 1328 tablesInfo.clear(); // regenerating the data 1329 // generate region split structure 1330 Collection<HbckInfo> hbckInfos = regionInfoMap.values(); 1331 1332 // Parallelized read of .regioninfo files. 1333 List<WorkItemHdfsRegionInfo> hbis = new ArrayList<>(hbckInfos.size()); 1334 List<Future<Void>> hbiFutures; 1335 1336 for (HbckInfo hbi : hbckInfos) { 1337 WorkItemHdfsRegionInfo work = new WorkItemHdfsRegionInfo(hbi, this, errors); 1338 hbis.add(work); 1339 } 1340 1341 // Submit and wait for completion 1342 hbiFutures = executor.invokeAll(hbis); 1343 1344 for(int i=0; i<hbiFutures.size(); i++) { 1345 WorkItemHdfsRegionInfo work = hbis.get(i); 1346 Future<Void> f = hbiFutures.get(i); 1347 try { 1348 f.get(); 1349 } catch(ExecutionException e) { 1350 LOG.warn("Failed to read .regioninfo file for region " + 1351 work.hbi.getRegionNameAsString(), e.getCause()); 1352 } 1353 } 1354 1355 Path hbaseRoot = FSUtils.getRootDir(getConf()); 1356 FileSystem fs = hbaseRoot.getFileSystem(getConf()); 1357 // serialized table info gathering. 1358 for (HbckInfo hbi: hbckInfos) { 1359 1360 if (hbi.getHdfsHRI() == null) { 1361 // was an orphan 1362 continue; 1363 } 1364 1365 1366 // get table name from hdfs, populate various HBaseFsck tables. 1367 TableName tableName = hbi.getTableName(); 1368 if (tableName == null) { 1369 // There was an entry in hbase:meta not in the HDFS? 1370 LOG.warn("tableName was null for: " + hbi); 1371 continue; 1372 } 1373 1374 TableInfo modTInfo = tablesInfo.get(tableName); 1375 if (modTInfo == null) { 1376 // only executed once per table. 1377 modTInfo = new TableInfo(tableName); 1378 tablesInfo.put(tableName, modTInfo); 1379 try { 1380 TableDescriptor htd = 1381 FSTableDescriptors.getTableDescriptorFromFs(fs, hbaseRoot, tableName); 1382 modTInfo.htds.add(htd); 1383 } catch (IOException ioe) { 1384 if (!orphanTableDirs.containsKey(tableName)) { 1385 LOG.warn("Unable to read .tableinfo from " + hbaseRoot, ioe); 1386 //should only report once for each table 1387 errors.reportError(ERROR_CODE.NO_TABLEINFO_FILE, 1388 "Unable to read .tableinfo from " + hbaseRoot + "/" + tableName); 1389 Set<String> columns = new HashSet<>(); 1390 orphanTableDirs.put(tableName, getColumnFamilyList(columns, hbi)); 1391 } 1392 } 1393 } 1394 if (!hbi.isSkipChecks()) { 1395 modTInfo.addRegionInfo(hbi); 1396 } 1397 } 1398 1399 loadTableInfosForTablesWithNoRegion(); 1400 errors.print(""); 1401 1402 return tablesInfo; 1403 } 1404 1405 /** 1406 * To get the column family list according to the column family dirs 1407 * @param columns 1408 * @param hbi 1409 * @return a set of column families 1410 * @throws IOException 1411 */ 1412 private Set<String> getColumnFamilyList(Set<String> columns, HbckInfo hbi) throws IOException { 1413 Path regionDir = hbi.getHdfsRegionDir(); 1414 FileSystem fs = regionDir.getFileSystem(getConf()); 1415 FileStatus[] subDirs = fs.listStatus(regionDir, new FSUtils.FamilyDirFilter(fs)); 1416 for (FileStatus subdir : subDirs) { 1417 String columnfamily = subdir.getPath().getName(); 1418 columns.add(columnfamily); 1419 } 1420 return columns; 1421 } 1422 1423 /** 1424 * To fabricate a .tableinfo file with following contents<br> 1425 * 1. the correct tablename <br> 1426 * 2. the correct colfamily list<br> 1427 * 3. the default properties for both {@link TableDescriptor} and {@link ColumnFamilyDescriptor}<br> 1428 * @throws IOException 1429 */ 1430 private boolean fabricateTableInfo(FSTableDescriptors fstd, TableName tableName, 1431 Set<String> columns) throws IOException { 1432 if (columns ==null || columns.isEmpty()) return false; 1433 TableDescriptorBuilder builder = TableDescriptorBuilder.newBuilder(tableName); 1434 for (String columnfamimly : columns) { 1435 builder.setColumnFamily(ColumnFamilyDescriptorBuilder.of(columnfamimly)); 1436 } 1437 fstd.createTableDescriptor(builder.build(), true); 1438 return true; 1439 } 1440 1441 /** 1442 * To fix the empty REGIONINFO_QUALIFIER rows from hbase:meta <br> 1443 * @throws IOException 1444 */ 1445 public void fixEmptyMetaCells() throws IOException { 1446 if (shouldFixEmptyMetaCells() && !emptyRegionInfoQualifiers.isEmpty()) { 1447 LOG.info("Trying to fix empty REGIONINFO_QUALIFIER hbase:meta rows."); 1448 for (Result region : emptyRegionInfoQualifiers) { 1449 deleteMetaRegion(region.getRow()); 1450 errors.getErrorList().remove(ERROR_CODE.EMPTY_META_CELL); 1451 } 1452 emptyRegionInfoQualifiers.clear(); 1453 } 1454 } 1455 1456 /** 1457 * To fix orphan table by creating a .tableinfo file under tableDir <br> 1458 * 1. if TableInfo is cached, to recover the .tableinfo accordingly <br> 1459 * 2. else create a default .tableinfo file with following items<br> 1460 * 2.1 the correct tablename <br> 1461 * 2.2 the correct colfamily list<br> 1462 * 2.3 the default properties for both {@link TableDescriptor} and {@link ColumnFamilyDescriptor}<br> 1463 * @throws IOException 1464 */ 1465 public void fixOrphanTables() throws IOException { 1466 if (shouldFixTableOrphans() && !orphanTableDirs.isEmpty()) { 1467 1468 List<TableName> tmpList = new ArrayList<>(orphanTableDirs.keySet().size()); 1469 tmpList.addAll(orphanTableDirs.keySet()); 1470 TableDescriptor[] htds = getTableDescriptors(tmpList); 1471 Iterator<Entry<TableName, Set<String>>> iter = 1472 orphanTableDirs.entrySet().iterator(); 1473 int j = 0; 1474 int numFailedCase = 0; 1475 FSTableDescriptors fstd = new FSTableDescriptors(getConf()); 1476 while (iter.hasNext()) { 1477 Entry<TableName, Set<String>> entry = 1478 iter.next(); 1479 TableName tableName = entry.getKey(); 1480 LOG.info("Trying to fix orphan table error: " + tableName); 1481 if (j < htds.length) { 1482 if (tableName.equals(htds[j].getTableName())) { 1483 TableDescriptor htd = htds[j]; 1484 LOG.info("fixing orphan table: " + tableName + " from cache"); 1485 fstd.createTableDescriptor(htd, true); 1486 j++; 1487 iter.remove(); 1488 } 1489 } else { 1490 if (fabricateTableInfo(fstd, tableName, entry.getValue())) { 1491 LOG.warn("fixing orphan table: " + tableName + " with a default .tableinfo file"); 1492 LOG.warn("Strongly recommend to modify the TableDescriptor if necessary for: " + tableName); 1493 iter.remove(); 1494 } else { 1495 LOG.error("Unable to create default .tableinfo for " + tableName + " while missing column family information"); 1496 numFailedCase++; 1497 } 1498 } 1499 fixes++; 1500 } 1501 1502 if (orphanTableDirs.isEmpty()) { 1503 // all orphanTableDirs are luckily recovered 1504 // re-run doFsck after recovering the .tableinfo file 1505 setShouldRerun(); 1506 LOG.warn("Strongly recommend to re-run manually hfsck after all orphanTableDirs being fixed"); 1507 } else if (numFailedCase > 0) { 1508 LOG.error("Failed to fix " + numFailedCase 1509 + " OrphanTables with default .tableinfo files"); 1510 } 1511 1512 } 1513 //cleanup the list 1514 orphanTableDirs.clear(); 1515 1516 } 1517 1518 /** 1519 * This borrows code from MasterFileSystem.bootstrap(). Explicitly creates it's own WAL, so be 1520 * sure to close it as well as the region when you're finished. 1521 * @param walFactoryID A unique identifier for WAL factory. Filesystem implementations will use 1522 * this ID to make a directory inside WAL directory path. 1523 * @return an open hbase:meta HRegion 1524 */ 1525 private HRegion createNewMeta(String walFactoryID) throws IOException { 1526 Path rootdir = FSUtils.getRootDir(getConf()); 1527 Configuration c = getConf(); 1528 RegionInfo metaHRI = RegionInfoBuilder.FIRST_META_REGIONINFO; 1529 TableDescriptor metaDescriptor = new FSTableDescriptors(c).get(TableName.META_TABLE_NAME); 1530 MasterFileSystem.setInfoFamilyCachingForMeta(metaDescriptor, false); 1531 // The WAL subsystem will use the default rootDir rather than the passed in rootDir 1532 // unless I pass along via the conf. 1533 Configuration confForWAL = new Configuration(c); 1534 confForWAL.set(HConstants.HBASE_DIR, rootdir.toString()); 1535 WAL wal = new WALFactory(confForWAL, walFactoryID).getWAL(metaHRI); 1536 HRegion meta = HRegion.createHRegion(metaHRI, rootdir, c, metaDescriptor, wal); 1537 MasterFileSystem.setInfoFamilyCachingForMeta(metaDescriptor, true); 1538 return meta; 1539 } 1540 1541 /** 1542 * Generate set of puts to add to new meta. This expects the tables to be 1543 * clean with no overlaps or holes. If there are any problems it returns null. 1544 * 1545 * @return An array list of puts to do in bulk, null if tables have problems 1546 */ 1547 private ArrayList<Put> generatePuts(SortedMap<TableName, TableInfo> tablesInfo) 1548 throws IOException { 1549 ArrayList<Put> puts = new ArrayList<>(); 1550 boolean hasProblems = false; 1551 for (Entry<TableName, TableInfo> e : tablesInfo.entrySet()) { 1552 TableName name = e.getKey(); 1553 1554 // skip "hbase:meta" 1555 if (name.compareTo(TableName.META_TABLE_NAME) == 0) { 1556 continue; 1557 } 1558 1559 TableInfo ti = e.getValue(); 1560 puts.add(MetaTableAccessor.makePutFromTableState( 1561 new TableState(ti.tableName, TableState.State.ENABLED), 1562 EnvironmentEdgeManager.currentTime())); 1563 for (Entry<byte[], Collection<HbckInfo>> spl : ti.sc.getStarts().asMap() 1564 .entrySet()) { 1565 Collection<HbckInfo> his = spl.getValue(); 1566 int sz = his.size(); 1567 if (sz != 1) { 1568 // problem 1569 LOG.error("Split starting at " + Bytes.toStringBinary(spl.getKey()) 1570 + " had " + sz + " regions instead of exactly 1." ); 1571 hasProblems = true; 1572 continue; 1573 } 1574 1575 // add the row directly to meta. 1576 HbckInfo hi = his.iterator().next(); 1577 RegionInfo hri = hi.getHdfsHRI(); // hi.metaEntry; 1578 Put p = MetaTableAccessor.makePutFromRegionInfo(hri, EnvironmentEdgeManager.currentTime()); 1579 puts.add(p); 1580 } 1581 } 1582 return hasProblems ? null : puts; 1583 } 1584 1585 /** 1586 * Suggest fixes for each table 1587 */ 1588 private void suggestFixes( 1589 SortedMap<TableName, TableInfo> tablesInfo) throws IOException { 1590 logParallelMerge(); 1591 for (TableInfo tInfo : tablesInfo.values()) { 1592 TableIntegrityErrorHandler handler = tInfo.new IntegrityFixSuggester(tInfo, errors); 1593 tInfo.checkRegionChain(handler); 1594 } 1595 } 1596 1597 /** 1598 * Rebuilds meta from information in hdfs/fs. Depends on configuration settings passed into 1599 * hbck constructor to point to a particular fs/dir. Assumes HBase is OFFLINE. 1600 * 1601 * @param fix flag that determines if method should attempt to fix holes 1602 * @return true if successful, false if attempt failed. 1603 */ 1604 public boolean rebuildMeta(boolean fix) throws IOException, 1605 InterruptedException { 1606 1607 // TODO check to make sure hbase is offline. (or at least the table 1608 // currently being worked on is off line) 1609 1610 // Determine what's on HDFS 1611 LOG.info("Loading HBase regioninfo from HDFS..."); 1612 loadHdfsRegionDirs(); // populating regioninfo table. 1613 1614 int errs = errors.getErrorList().size(); 1615 tablesInfo = loadHdfsRegionInfos(); // update tableInfos based on region info in fs. 1616 checkHdfsIntegrity(false, false); 1617 1618 // make sure ok. 1619 if (errors.getErrorList().size() != errs) { 1620 // While in error state, iterate until no more fixes possible 1621 while(true) { 1622 fixes = 0; 1623 suggestFixes(tablesInfo); 1624 errors.clear(); 1625 loadHdfsRegionInfos(); // update tableInfos based on region info in fs. 1626 checkHdfsIntegrity(shouldFixHdfsHoles(), shouldFixHdfsOverlaps()); 1627 1628 int errCount = errors.getErrorList().size(); 1629 1630 if (fixes == 0) { 1631 if (errCount > 0) { 1632 return false; // failed to fix problems. 1633 } else { 1634 break; // no fixes and no problems? drop out and fix stuff! 1635 } 1636 } 1637 } 1638 } 1639 1640 // we can rebuild, move old meta out of the way and start 1641 LOG.info("HDFS regioninfo's seems good. Sidelining old hbase:meta"); 1642 Path backupDir = sidelineOldMeta(); 1643 1644 LOG.info("Creating new hbase:meta"); 1645 String walFactoryId = "hbck-meta-recovery-" + RandomStringUtils.randomNumeric(8); 1646 HRegion meta = createNewMeta(walFactoryId); 1647 1648 // populate meta 1649 List<Put> puts = generatePuts(tablesInfo); 1650 if (puts == null) { 1651 LOG.error(HBaseMarkers.FATAL, "Problem encountered when creating new hbase:meta " 1652 + "entries. You may need to restore the previously sidelined hbase:meta"); 1653 return false; 1654 } 1655 meta.batchMutate(puts.toArray(new Put[puts.size()]), HConstants.NO_NONCE, HConstants.NO_NONCE); 1656 meta.close(); 1657 if (meta.getWAL() != null) { 1658 meta.getWAL().close(); 1659 } 1660 // clean up the temporary hbck meta recovery WAL directory 1661 removeHBCKMetaRecoveryWALDir(walFactoryId); 1662 LOG.info("Success! hbase:meta table rebuilt."); 1663 LOG.info("Old hbase:meta is moved into " + backupDir); 1664 return true; 1665 } 1666 1667 /** 1668 * Removes the empty Meta recovery WAL directory. 1669 * @param walFactoryId A unique identifier for WAL factory which was used by Filesystem to make a 1670 * Meta recovery WAL directory inside WAL directory path. 1671 */ 1672 private void removeHBCKMetaRecoveryWALDir(String walFactoryId) throws IOException { 1673 Path rootdir = FSUtils.getRootDir(getConf()); 1674 Path walLogDir = new Path(new Path(rootdir, HConstants.HREGION_LOGDIR_NAME), walFactoryId); 1675 FileSystem fs = FSUtils.getCurrentFileSystem(getConf()); 1676 FileStatus[] walFiles = FSUtils.listStatus(fs, walLogDir, null); 1677 if (walFiles == null || walFiles.length == 0) { 1678 LOG.info("HBCK meta recovery WAL directory is empty, removing it now."); 1679 if (!FSUtils.deleteDirectory(fs, walLogDir)) { 1680 LOG.warn("Couldn't clear the HBCK Meta recovery WAL directory " + walLogDir); 1681 } 1682 } 1683 } 1684 1685 /** 1686 * Log an appropriate message about whether or not overlapping merges are computed in parallel. 1687 */ 1688 private void logParallelMerge() { 1689 if (getConf().getBoolean("hbasefsck.overlap.merge.parallel", true)) { 1690 LOG.info("Handling overlap merges in parallel. set hbasefsck.overlap.merge.parallel to" + 1691 " false to run serially."); 1692 } else { 1693 LOG.info("Handling overlap merges serially. set hbasefsck.overlap.merge.parallel to" + 1694 " true to run in parallel."); 1695 } 1696 } 1697 1698 private SortedMap<TableName, TableInfo> checkHdfsIntegrity(boolean fixHoles, 1699 boolean fixOverlaps) throws IOException { 1700 LOG.info("Checking HBase region split map from HDFS data..."); 1701 logParallelMerge(); 1702 for (TableInfo tInfo : tablesInfo.values()) { 1703 TableIntegrityErrorHandler handler; 1704 if (fixHoles || fixOverlaps) { 1705 handler = tInfo.new HDFSIntegrityFixer(tInfo, errors, getConf(), 1706 fixHoles, fixOverlaps); 1707 } else { 1708 handler = tInfo.new IntegrityFixSuggester(tInfo, errors); 1709 } 1710 if (!tInfo.checkRegionChain(handler)) { 1711 // should dump info as well. 1712 errors.report("Found inconsistency in table " + tInfo.getName()); 1713 } 1714 } 1715 return tablesInfo; 1716 } 1717 1718 private Path getSidelineDir() throws IOException { 1719 if (sidelineDir == null) { 1720 Path hbaseDir = FSUtils.getRootDir(getConf()); 1721 Path hbckDir = new Path(hbaseDir, HConstants.HBCK_SIDELINEDIR_NAME); 1722 sidelineDir = new Path(hbckDir, hbaseDir.getName() + "-" 1723 + startMillis); 1724 } 1725 return sidelineDir; 1726 } 1727 1728 /** 1729 * Sideline a region dir (instead of deleting it) 1730 */ 1731 Path sidelineRegionDir(FileSystem fs, HbckInfo hi) throws IOException { 1732 return sidelineRegionDir(fs, null, hi); 1733 } 1734 1735 /** 1736 * Sideline a region dir (instead of deleting it) 1737 * 1738 * @param parentDir if specified, the region will be sidelined to folder like 1739 * {@literal .../parentDir/<table name>/<region name>}. The purpose is to group together 1740 * similar regions sidelined, for example, those regions should be bulk loaded back later 1741 * on. If NULL, it is ignored. 1742 */ 1743 Path sidelineRegionDir(FileSystem fs, 1744 String parentDir, HbckInfo hi) throws IOException { 1745 TableName tableName = hi.getTableName(); 1746 Path regionDir = hi.getHdfsRegionDir(); 1747 1748 if (!fs.exists(regionDir)) { 1749 LOG.warn("No previous " + regionDir + " exists. Continuing."); 1750 return null; 1751 } 1752 1753 Path rootDir = getSidelineDir(); 1754 if (parentDir != null) { 1755 rootDir = new Path(rootDir, parentDir); 1756 } 1757 Path sidelineTableDir= FSUtils.getTableDir(rootDir, tableName); 1758 Path sidelineRegionDir = new Path(sidelineTableDir, regionDir.getName()); 1759 fs.mkdirs(sidelineRegionDir); 1760 boolean success = false; 1761 FileStatus[] cfs = fs.listStatus(regionDir); 1762 if (cfs == null) { 1763 LOG.info("Region dir is empty: " + regionDir); 1764 } else { 1765 for (FileStatus cf : cfs) { 1766 Path src = cf.getPath(); 1767 Path dst = new Path(sidelineRegionDir, src.getName()); 1768 if (fs.isFile(src)) { 1769 // simple file 1770 success = fs.rename(src, dst); 1771 if (!success) { 1772 String msg = "Unable to rename file " + src + " to " + dst; 1773 LOG.error(msg); 1774 throw new IOException(msg); 1775 } 1776 continue; 1777 } 1778 1779 // is a directory. 1780 fs.mkdirs(dst); 1781 1782 LOG.info("Sidelining files from " + src + " into containing region " + dst); 1783 // FileSystem.rename is inconsistent with directories -- if the 1784 // dst (foo/a) exists and is a dir, and the src (foo/b) is a dir, 1785 // it moves the src into the dst dir resulting in (foo/a/b). If 1786 // the dst does not exist, and the src a dir, src becomes dst. (foo/b) 1787 FileStatus[] hfiles = fs.listStatus(src); 1788 if (hfiles != null && hfiles.length > 0) { 1789 for (FileStatus hfile : hfiles) { 1790 success = fs.rename(hfile.getPath(), dst); 1791 if (!success) { 1792 String msg = "Unable to rename file " + src + " to " + dst; 1793 LOG.error(msg); 1794 throw new IOException(msg); 1795 } 1796 } 1797 } 1798 LOG.debug("Sideline directory contents:"); 1799 debugLsr(sidelineRegionDir); 1800 } 1801 } 1802 1803 LOG.info("Removing old region dir: " + regionDir); 1804 success = fs.delete(regionDir, true); 1805 if (!success) { 1806 String msg = "Unable to delete dir " + regionDir; 1807 LOG.error(msg); 1808 throw new IOException(msg); 1809 } 1810 return sidelineRegionDir; 1811 } 1812 1813 /** 1814 * Side line an entire table. 1815 */ 1816 void sidelineTable(FileSystem fs, TableName tableName, Path hbaseDir, 1817 Path backupHbaseDir) throws IOException { 1818 Path tableDir = FSUtils.getTableDir(hbaseDir, tableName); 1819 if (fs.exists(tableDir)) { 1820 Path backupTableDir= FSUtils.getTableDir(backupHbaseDir, tableName); 1821 fs.mkdirs(backupTableDir.getParent()); 1822 boolean success = fs.rename(tableDir, backupTableDir); 1823 if (!success) { 1824 throw new IOException("Failed to move " + tableName + " from " 1825 + tableDir + " to " + backupTableDir); 1826 } 1827 } else { 1828 LOG.info("No previous " + tableName + " exists. Continuing."); 1829 } 1830 } 1831 1832 /** 1833 * @return Path to backup of original directory 1834 */ 1835 Path sidelineOldMeta() throws IOException { 1836 // put current hbase:meta aside. 1837 Path hbaseDir = FSUtils.getRootDir(getConf()); 1838 FileSystem fs = hbaseDir.getFileSystem(getConf()); 1839 Path backupDir = getSidelineDir(); 1840 fs.mkdirs(backupDir); 1841 1842 try { 1843 sidelineTable(fs, TableName.META_TABLE_NAME, hbaseDir, backupDir); 1844 } catch (IOException e) { 1845 LOG.error(HBaseMarkers.FATAL, "... failed to sideline meta. Currently in " 1846 + "inconsistent state. To restore try to rename hbase:meta in " + 1847 backupDir.getName() + " to " + hbaseDir.getName() + ".", e); 1848 throw e; // throw original exception 1849 } 1850 return backupDir; 1851 } 1852 1853 /** 1854 * Load the list of disabled tables in ZK into local set. 1855 * @throws ZooKeeperConnectionException 1856 * @throws IOException 1857 */ 1858 private void loadTableStates() 1859 throws IOException { 1860 tableStates = MetaTableAccessor.getTableStates(connection); 1861 // Add hbase:meta so this tool keeps working. In hbase2, meta is always enabled though it 1862 // has no entry in the table states. HBCK doesn't work right w/ hbase2 but just do this in 1863 // meantime. 1864 this.tableStates.put(TableName.META_TABLE_NAME, 1865 new TableState(TableName.META_TABLE_NAME, TableState.State.ENABLED)); 1866 } 1867 1868 /** 1869 * Check if the specified region's table is disabled. 1870 * @param tableName table to check status of 1871 */ 1872 private boolean isTableDisabled(TableName tableName) { 1873 return tableStates.containsKey(tableName) 1874 && tableStates.get(tableName) 1875 .inStates(TableState.State.DISABLED, TableState.State.DISABLING); 1876 } 1877 1878 /** 1879 * Scan HDFS for all regions, recording their information into 1880 * regionInfoMap 1881 */ 1882 public void loadHdfsRegionDirs() throws IOException, InterruptedException { 1883 Path rootDir = FSUtils.getRootDir(getConf()); 1884 FileSystem fs = rootDir.getFileSystem(getConf()); 1885 1886 // list all tables from HDFS 1887 List<FileStatus> tableDirs = Lists.newArrayList(); 1888 1889 boolean foundVersionFile = fs.exists(new Path(rootDir, HConstants.VERSION_FILE_NAME)); 1890 1891 List<Path> paths = FSUtils.getTableDirs(fs, rootDir); 1892 for (Path path : paths) { 1893 TableName tableName = FSUtils.getTableName(path); 1894 if ((!checkMetaOnly && 1895 isTableIncluded(tableName)) || 1896 tableName.equals(TableName.META_TABLE_NAME)) { 1897 tableDirs.add(fs.getFileStatus(path)); 1898 } 1899 } 1900 1901 // verify that version file exists 1902 if (!foundVersionFile) { 1903 errors.reportError(ERROR_CODE.NO_VERSION_FILE, 1904 "Version file does not exist in root dir " + rootDir); 1905 if (shouldFixVersionFile()) { 1906 LOG.info("Trying to create a new " + HConstants.VERSION_FILE_NAME 1907 + " file."); 1908 setShouldRerun(); 1909 FSUtils.setVersion(fs, rootDir, getConf().getInt( 1910 HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000), getConf().getInt( 1911 HConstants.VERSION_FILE_WRITE_ATTEMPTS, 1912 HConstants.DEFAULT_VERSION_FILE_WRITE_ATTEMPTS)); 1913 } 1914 } 1915 1916 // Avoid multithreading at table-level because already multithreaded internally at 1917 // region-level. Additionally multithreading at table-level can lead to deadlock 1918 // if there are many tables in the cluster. Since there are a limited # of threads 1919 // in the executor's thread pool and if we multithread at the table-level by putting 1920 // WorkItemHdfsDir callables into the executor, then we will have some threads in the 1921 // executor tied up solely in waiting for the tables' region-level calls to complete. 1922 // If there are enough tables then there will be no actual threads in the pool left 1923 // for the region-level callables to be serviced. 1924 for (FileStatus tableDir : tableDirs) { 1925 LOG.debug("Loading region dirs from " +tableDir.getPath()); 1926 WorkItemHdfsDir item = new WorkItemHdfsDir(fs, errors, tableDir); 1927 try { 1928 item.call(); 1929 } catch (ExecutionException e) { 1930 LOG.warn("Could not completely load table dir " + 1931 tableDir.getPath(), e.getCause()); 1932 } 1933 } 1934 errors.print(""); 1935 } 1936 1937 /** 1938 * Record the location of the hbase:meta region as found in ZooKeeper. 1939 */ 1940 private boolean recordMetaRegion() throws IOException { 1941 RegionLocations rl = connection.locateRegion(TableName.META_TABLE_NAME, 1942 HConstants.EMPTY_START_ROW, false, false); 1943 if (rl == null) { 1944 errors.reportError(ERROR_CODE.NULL_META_REGION, 1945 "META region was not found in ZooKeeper"); 1946 return false; 1947 } 1948 for (HRegionLocation metaLocation : rl.getRegionLocations()) { 1949 // Check if Meta region is valid and existing 1950 if (metaLocation == null ) { 1951 errors.reportError(ERROR_CODE.NULL_META_REGION, 1952 "META region location is null"); 1953 return false; 1954 } 1955 if (metaLocation.getRegionInfo() == null) { 1956 errors.reportError(ERROR_CODE.NULL_META_REGION, 1957 "META location regionInfo is null"); 1958 return false; 1959 } 1960 if (metaLocation.getHostname() == null) { 1961 errors.reportError(ERROR_CODE.NULL_META_REGION, 1962 "META location hostName is null"); 1963 return false; 1964 } 1965 ServerName sn = metaLocation.getServerName(); 1966 MetaEntry m = new MetaEntry(metaLocation.getRegionInfo(), sn, EnvironmentEdgeManager.currentTime()); 1967 HbckInfo hbckInfo = regionInfoMap.get(metaLocation.getRegionInfo().getEncodedName()); 1968 if (hbckInfo == null) { 1969 regionInfoMap.put(metaLocation.getRegionInfo().getEncodedName(), new HbckInfo(m)); 1970 } else { 1971 hbckInfo.metaEntry = m; 1972 } 1973 } 1974 return true; 1975 } 1976 1977 private ZKWatcher createZooKeeperWatcher() throws IOException { 1978 return new ZKWatcher(getConf(), "hbase Fsck", new Abortable() { 1979 @Override 1980 public void abort(String why, Throwable e) { 1981 LOG.error(why, e); 1982 System.exit(1); 1983 } 1984 1985 @Override 1986 public boolean isAborted() { 1987 return false; 1988 } 1989 1990 }); 1991 } 1992 1993 private ServerName getMetaRegionServerName(int replicaId) 1994 throws IOException, KeeperException { 1995 return new MetaTableLocator().getMetaRegionLocation(zkw, replicaId); 1996 } 1997 1998 /** 1999 * Contacts each regionserver and fetches metadata about regions. 2000 * @param regionServerList - the list of region servers to connect to 2001 * @throws IOException if a remote or network exception occurs 2002 */ 2003 void processRegionServers(Collection<ServerName> regionServerList) 2004 throws IOException, InterruptedException { 2005 2006 List<WorkItemRegion> workItems = new ArrayList<>(regionServerList.size()); 2007 List<Future<Void>> workFutures; 2008 2009 // loop to contact each region server in parallel 2010 for (ServerName rsinfo: regionServerList) { 2011 workItems.add(new WorkItemRegion(this, rsinfo, errors, connection)); 2012 } 2013 2014 workFutures = executor.invokeAll(workItems); 2015 2016 for(int i=0; i<workFutures.size(); i++) { 2017 WorkItemRegion item = workItems.get(i); 2018 Future<Void> f = workFutures.get(i); 2019 try { 2020 f.get(); 2021 } catch(ExecutionException e) { 2022 LOG.warn("Could not process regionserver " + item.rsinfo.getHostAndPort(), 2023 e.getCause()); 2024 } 2025 } 2026 } 2027 2028 /** 2029 * Check consistency of all regions that have been found in previous phases. 2030 */ 2031 private void checkAndFixConsistency() 2032 throws IOException, KeeperException, InterruptedException { 2033 // Divide the checks in two phases. One for default/primary replicas and another 2034 // for the non-primary ones. Keeps code cleaner this way. 2035 2036 List<CheckRegionConsistencyWorkItem> workItems = new ArrayList<>(regionInfoMap.size()); 2037 for (java.util.Map.Entry<String, HbckInfo> e: regionInfoMap.entrySet()) { 2038 if (e.getValue().getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) { 2039 workItems.add(new CheckRegionConsistencyWorkItem(e.getKey(), e.getValue())); 2040 } 2041 } 2042 checkRegionConsistencyConcurrently(workItems); 2043 2044 boolean prevHdfsCheck = shouldCheckHdfs(); 2045 setCheckHdfs(false); //replicas don't have any hdfs data 2046 // Run a pass over the replicas and fix any assignment issues that exist on the currently 2047 // deployed/undeployed replicas. 2048 List<CheckRegionConsistencyWorkItem> replicaWorkItems = new ArrayList<>(regionInfoMap.size()); 2049 for (java.util.Map.Entry<String, HbckInfo> e: regionInfoMap.entrySet()) { 2050 if (e.getValue().getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) { 2051 replicaWorkItems.add(new CheckRegionConsistencyWorkItem(e.getKey(), e.getValue())); 2052 } 2053 } 2054 checkRegionConsistencyConcurrently(replicaWorkItems); 2055 setCheckHdfs(prevHdfsCheck); 2056 2057 // If some regions is skipped during checkRegionConsistencyConcurrently() phase, we might 2058 // not get accurate state of the hbase if continuing. The config here allows users to tune 2059 // the tolerance of number of skipped region. 2060 // TODO: evaluate the consequence to continue the hbck operation without config. 2061 int terminateThreshold = getConf().getInt("hbase.hbck.skipped.regions.limit", 0); 2062 int numOfSkippedRegions = skippedRegions.size(); 2063 if (numOfSkippedRegions > 0 && numOfSkippedRegions > terminateThreshold) { 2064 throw new IOException(numOfSkippedRegions 2065 + " region(s) could not be checked or repaired. See logs for detail."); 2066 } 2067 2068 if (shouldCheckHdfs()) { 2069 checkAndFixTableStates(); 2070 } 2071 } 2072 2073 /** 2074 * Check consistency of all regions using mulitple threads concurrently. 2075 */ 2076 private void checkRegionConsistencyConcurrently( 2077 final List<CheckRegionConsistencyWorkItem> workItems) 2078 throws IOException, KeeperException, InterruptedException { 2079 if (workItems.isEmpty()) { 2080 return; // nothing to check 2081 } 2082 2083 List<Future<Void>> workFutures = executor.invokeAll(workItems); 2084 for(Future<Void> f: workFutures) { 2085 try { 2086 f.get(); 2087 } catch(ExecutionException e1) { 2088 LOG.warn("Could not check region consistency " , e1.getCause()); 2089 if (e1.getCause() instanceof IOException) { 2090 throw (IOException)e1.getCause(); 2091 } else if (e1.getCause() instanceof KeeperException) { 2092 throw (KeeperException)e1.getCause(); 2093 } else if (e1.getCause() instanceof InterruptedException) { 2094 throw (InterruptedException)e1.getCause(); 2095 } else { 2096 throw new IOException(e1.getCause()); 2097 } 2098 } 2099 } 2100 } 2101 2102 class CheckRegionConsistencyWorkItem implements Callable<Void> { 2103 private final String key; 2104 private final HbckInfo hbi; 2105 2106 CheckRegionConsistencyWorkItem(String key, HbckInfo hbi) { 2107 this.key = key; 2108 this.hbi = hbi; 2109 } 2110 2111 @Override 2112 public synchronized Void call() throws Exception { 2113 try { 2114 checkRegionConsistency(key, hbi); 2115 } catch (Exception e) { 2116 // If the region is non-META region, skip this region and send warning/error message; if 2117 // the region is META region, we should not continue. 2118 LOG.warn("Unable to complete check or repair the region '" + hbi.getRegionNameAsString() 2119 + "'.", e); 2120 if (hbi.getHdfsHRI().isMetaRegion()) { 2121 throw e; 2122 } 2123 LOG.warn("Skip region '" + hbi.getRegionNameAsString() + "'"); 2124 addSkippedRegion(hbi); 2125 } 2126 return null; 2127 } 2128 } 2129 2130 private void addSkippedRegion(final HbckInfo hbi) { 2131 Set<String> skippedRegionNames = skippedRegions.get(hbi.getTableName()); 2132 if (skippedRegionNames == null) { 2133 skippedRegionNames = new HashSet<>(); 2134 } 2135 skippedRegionNames.add(hbi.getRegionNameAsString()); 2136 skippedRegions.put(hbi.getTableName(), skippedRegionNames); 2137 } 2138 2139 /** 2140 * Check and fix table states, assumes full info available: 2141 * - tableInfos 2142 * - empty tables loaded 2143 */ 2144 private void checkAndFixTableStates() throws IOException { 2145 // first check dangling states 2146 for (Entry<TableName, TableState> entry : tableStates.entrySet()) { 2147 TableName tableName = entry.getKey(); 2148 TableState tableState = entry.getValue(); 2149 TableInfo tableInfo = tablesInfo.get(tableName); 2150 if (isTableIncluded(tableName) 2151 && !tableName.isSystemTable() 2152 && tableInfo == null) { 2153 if (fixMeta) { 2154 MetaTableAccessor.deleteTableState(connection, tableName); 2155 TableState state = MetaTableAccessor.getTableState(connection, tableName); 2156 if (state != null) { 2157 errors.reportError(ERROR_CODE.ORPHAN_TABLE_STATE, 2158 tableName + " unable to delete dangling table state " + tableState); 2159 } 2160 } else if (!checkMetaOnly) { 2161 // dangling table state in meta if checkMetaOnly is false. If checkMetaOnly is 2162 // true, tableInfo will be null as tablesInfo are not polulated for all tables from hdfs 2163 errors.reportError(ERROR_CODE.ORPHAN_TABLE_STATE, 2164 tableName + " has dangling table state " + tableState); 2165 } 2166 } 2167 } 2168 // check that all tables have states 2169 for (TableName tableName : tablesInfo.keySet()) { 2170 if (isTableIncluded(tableName) && !tableStates.containsKey(tableName)) { 2171 if (fixMeta) { 2172 MetaTableAccessor.updateTableState(connection, tableName, TableState.State.ENABLED); 2173 TableState newState = MetaTableAccessor.getTableState(connection, tableName); 2174 if (newState == null) { 2175 errors.reportError(ERROR_CODE.NO_TABLE_STATE, 2176 "Unable to change state for table " + tableName + " in meta "); 2177 } 2178 } else { 2179 errors.reportError(ERROR_CODE.NO_TABLE_STATE, 2180 tableName + " has no state in meta "); 2181 } 2182 } 2183 } 2184 } 2185 2186 private void preCheckPermission() throws IOException, AccessDeniedException { 2187 if (shouldIgnorePreCheckPermission()) { 2188 return; 2189 } 2190 2191 Path hbaseDir = FSUtils.getRootDir(getConf()); 2192 FileSystem fs = hbaseDir.getFileSystem(getConf()); 2193 UserProvider userProvider = UserProvider.instantiate(getConf()); 2194 UserGroupInformation ugi = userProvider.getCurrent().getUGI(); 2195 FileStatus[] files = fs.listStatus(hbaseDir); 2196 for (FileStatus file : files) { 2197 try { 2198 FSUtils.checkAccess(ugi, file, FsAction.WRITE); 2199 } catch (AccessDeniedException ace) { 2200 LOG.warn("Got AccessDeniedException when preCheckPermission ", ace); 2201 errors.reportError(ERROR_CODE.WRONG_USAGE, "Current user " + ugi.getUserName() 2202 + " does not have write perms to " + file.getPath() 2203 + ". Please rerun hbck as hdfs user " + file.getOwner()); 2204 throw ace; 2205 } 2206 } 2207 } 2208 2209 /** 2210 * Deletes region from meta table 2211 */ 2212 private void deleteMetaRegion(HbckInfo hi) throws IOException { 2213 deleteMetaRegion(hi.metaEntry.getRegionName()); 2214 } 2215 2216 /** 2217 * Deletes region from meta table 2218 */ 2219 private void deleteMetaRegion(byte[] metaKey) throws IOException { 2220 Delete d = new Delete(metaKey); 2221 meta.delete(d); 2222 LOG.info("Deleted " + Bytes.toString(metaKey) + " from META" ); 2223 } 2224 2225 /** 2226 * Reset the split parent region info in meta table 2227 */ 2228 private void resetSplitParent(HbckInfo hi) throws IOException { 2229 RowMutations mutations = new RowMutations(hi.metaEntry.getRegionName()); 2230 Delete d = new Delete(hi.metaEntry.getRegionName()); 2231 d.addColumn(HConstants.CATALOG_FAMILY, HConstants.SPLITA_QUALIFIER); 2232 d.addColumn(HConstants.CATALOG_FAMILY, HConstants.SPLITB_QUALIFIER); 2233 mutations.add(d); 2234 2235 RegionInfo hri = RegionInfoBuilder.newBuilder(hi.metaEntry) 2236 .setOffline(false) 2237 .setSplit(false) 2238 .build(); 2239 Put p = MetaTableAccessor.makePutFromRegionInfo(hri, EnvironmentEdgeManager.currentTime()); 2240 mutations.add(p); 2241 2242 meta.mutateRow(mutations); 2243 LOG.info("Reset split parent " + hi.metaEntry.getRegionNameAsString() + " in META" ); 2244 } 2245 2246 /** 2247 * This backwards-compatibility wrapper for permanently offlining a region 2248 * that should not be alive. If the region server does not support the 2249 * "offline" method, it will use the closest unassign method instead. This 2250 * will basically work until one attempts to disable or delete the affected 2251 * table. The problem has to do with in-memory only master state, so 2252 * restarting the HMaster or failing over to another should fix this. 2253 */ 2254 private void offline(byte[] regionName) throws IOException { 2255 String regionString = Bytes.toStringBinary(regionName); 2256 if (!rsSupportsOffline) { 2257 LOG.warn("Using unassign region " + regionString 2258 + " instead of using offline method, you should" 2259 + " restart HMaster after these repairs"); 2260 admin.unassign(regionName, true); 2261 return; 2262 } 2263 2264 // first time we assume the rs's supports #offline. 2265 try { 2266 LOG.info("Offlining region " + regionString); 2267 admin.offline(regionName); 2268 } catch (IOException ioe) { 2269 String notFoundMsg = "java.lang.NoSuchMethodException: " + 2270 "org.apache.hadoop.hbase.master.HMaster.offline([B)"; 2271 if (ioe.getMessage().contains(notFoundMsg)) { 2272 LOG.warn("Using unassign region " + regionString 2273 + " instead of using offline method, you should" 2274 + " restart HMaster after these repairs"); 2275 rsSupportsOffline = false; // in the future just use unassign 2276 admin.unassign(regionName, true); 2277 return; 2278 } 2279 throw ioe; 2280 } 2281 } 2282 2283 private void undeployRegions(HbckInfo hi) throws IOException, InterruptedException { 2284 undeployRegionsForHbi(hi); 2285 // undeploy replicas of the region (but only if the method is invoked for the primary) 2286 if (hi.getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) { 2287 return; 2288 } 2289 int numReplicas = admin.getTableDescriptor(hi.getTableName()).getRegionReplication(); 2290 for (int i = 1; i < numReplicas; i++) { 2291 if (hi.getPrimaryHRIForDeployedReplica() == null) continue; 2292 RegionInfo hri = RegionReplicaUtil.getRegionInfoForReplica( 2293 hi.getPrimaryHRIForDeployedReplica(), i); 2294 HbckInfo h = regionInfoMap.get(hri.getEncodedName()); 2295 if (h != null) { 2296 undeployRegionsForHbi(h); 2297 //set skip checks; we undeployed it, and we don't want to evaluate this anymore 2298 //in consistency checks 2299 h.setSkipChecks(true); 2300 } 2301 } 2302 } 2303 2304 private void undeployRegionsForHbi(HbckInfo hi) throws IOException, InterruptedException { 2305 for (OnlineEntry rse : hi.deployedEntries) { 2306 LOG.debug("Undeploy region " + rse.hri + " from " + rse.hsa); 2307 try { 2308 HBaseFsckRepair.closeRegionSilentlyAndWait(connection, rse.hsa, rse.hri); 2309 offline(rse.hri.getRegionName()); 2310 } catch (IOException ioe) { 2311 LOG.warn("Got exception when attempting to offline region " 2312 + Bytes.toString(rse.hri.getRegionName()), ioe); 2313 } 2314 } 2315 } 2316 2317 /** 2318 * Attempts to undeploy a region from a region server based in information in 2319 * META. Any operations that modify the file system should make sure that 2320 * its corresponding region is not deployed to prevent data races. 2321 * 2322 * A separate call is required to update the master in-memory region state 2323 * kept in the AssignementManager. Because disable uses this state instead of 2324 * that found in META, we can't seem to cleanly disable/delete tables that 2325 * have been hbck fixed. When used on a version of HBase that does not have 2326 * the offline ipc call exposed on the master (<0.90.5, <0.92.0) a master 2327 * restart or failover may be required. 2328 */ 2329 private void closeRegion(HbckInfo hi) throws IOException, InterruptedException { 2330 if (hi.metaEntry == null && hi.hdfsEntry == null) { 2331 undeployRegions(hi); 2332 return; 2333 } 2334 2335 // get assignment info and hregioninfo from meta. 2336 Get get = new Get(hi.getRegionName()); 2337 get.addColumn(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER); 2338 get.addColumn(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER); 2339 get.addColumn(HConstants.CATALOG_FAMILY, HConstants.STARTCODE_QUALIFIER); 2340 // also get the locations of the replicas to close if the primary region is being closed 2341 if (hi.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) { 2342 int numReplicas = admin.getTableDescriptor(hi.getTableName()).getRegionReplication(); 2343 for (int i = 0; i < numReplicas; i++) { 2344 get.addColumn(HConstants.CATALOG_FAMILY, MetaTableAccessor.getServerColumn(i)); 2345 get.addColumn(HConstants.CATALOG_FAMILY, MetaTableAccessor.getStartCodeColumn(i)); 2346 } 2347 } 2348 Result r = meta.get(get); 2349 RegionLocations rl = MetaTableAccessor.getRegionLocations(r); 2350 if (rl == null) { 2351 LOG.warn("Unable to close region " + hi.getRegionNameAsString() + 2352 " since meta does not have handle to reach it"); 2353 return; 2354 } 2355 for (HRegionLocation h : rl.getRegionLocations()) { 2356 ServerName serverName = h.getServerName(); 2357 if (serverName == null) { 2358 errors.reportError("Unable to close region " 2359 + hi.getRegionNameAsString() + " because meta does not " 2360 + "have handle to reach it."); 2361 continue; 2362 } 2363 RegionInfo hri = h.getRegionInfo(); 2364 if (hri == null) { 2365 LOG.warn("Unable to close region " + hi.getRegionNameAsString() 2366 + " because hbase:meta had invalid or missing " 2367 + HConstants.CATALOG_FAMILY_STR + ":" 2368 + Bytes.toString(HConstants.REGIONINFO_QUALIFIER) 2369 + " qualifier value."); 2370 continue; 2371 } 2372 // close the region -- close files and remove assignment 2373 HBaseFsckRepair.closeRegionSilentlyAndWait(connection, serverName, hri); 2374 } 2375 } 2376 2377 private void tryAssignmentRepair(HbckInfo hbi, String msg) throws IOException, 2378 KeeperException, InterruptedException { 2379 // If we are trying to fix the errors 2380 if (shouldFixAssignments()) { 2381 errors.print(msg); 2382 undeployRegions(hbi); 2383 setShouldRerun(); 2384 RegionInfo hri = hbi.getHdfsHRI(); 2385 if (hri == null) { 2386 hri = hbi.metaEntry; 2387 } 2388 HBaseFsckRepair.fixUnassigned(admin, hri); 2389 HBaseFsckRepair.waitUntilAssigned(admin, hri); 2390 2391 // also assign replicas if needed (do it only when this call operates on a primary replica) 2392 if (hbi.getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) return; 2393 int replicationCount = admin.getTableDescriptor(hri.getTable()).getRegionReplication(); 2394 for (int i = 1; i < replicationCount; i++) { 2395 hri = RegionReplicaUtil.getRegionInfoForReplica(hri, i); 2396 HbckInfo h = regionInfoMap.get(hri.getEncodedName()); 2397 if (h != null) { 2398 undeployRegions(h); 2399 //set skip checks; we undeploy & deploy it; we don't want to evaluate this hbi anymore 2400 //in consistency checks 2401 h.setSkipChecks(true); 2402 } 2403 HBaseFsckRepair.fixUnassigned(admin, hri); 2404 HBaseFsckRepair.waitUntilAssigned(admin, hri); 2405 } 2406 2407 } 2408 } 2409 2410 /** 2411 * Check a single region for consistency and correct deployment. 2412 */ 2413 private void checkRegionConsistency(final String key, final HbckInfo hbi) 2414 throws IOException, KeeperException, InterruptedException { 2415 2416 if (hbi.isSkipChecks()) return; 2417 String descriptiveName = hbi.toString(); 2418 boolean inMeta = hbi.metaEntry != null; 2419 // In case not checking HDFS, assume the region is on HDFS 2420 boolean inHdfs = !shouldCheckHdfs() || hbi.getHdfsRegionDir() != null; 2421 boolean hasMetaAssignment = inMeta && hbi.metaEntry.regionServer != null; 2422 boolean isDeployed = !hbi.deployedOn.isEmpty(); 2423 boolean isMultiplyDeployed = hbi.deployedOn.size() > 1; 2424 boolean deploymentMatchesMeta = 2425 hasMetaAssignment && isDeployed && !isMultiplyDeployed && 2426 hbi.metaEntry.regionServer.equals(hbi.deployedOn.get(0)); 2427 boolean splitParent = 2428 inMeta && hbi.metaEntry.isSplit() && hbi.metaEntry.isOffline(); 2429 boolean shouldBeDeployed = inMeta && !isTableDisabled(hbi.metaEntry.getTable()); 2430 boolean recentlyModified = inHdfs && 2431 hbi.getModTime() + timelag > EnvironmentEdgeManager.currentTime(); 2432 2433 // ========== First the healthy cases ============= 2434 if (hbi.containsOnlyHdfsEdits()) { 2435 return; 2436 } 2437 if (inMeta && inHdfs && isDeployed && deploymentMatchesMeta && shouldBeDeployed) { 2438 return; 2439 } else if (inMeta && inHdfs && !shouldBeDeployed && !isDeployed) { 2440 LOG.info("Region " + descriptiveName + " is in META, and in a disabled " + 2441 "tabled that is not deployed"); 2442 return; 2443 } else if (recentlyModified) { 2444 LOG.warn("Region " + descriptiveName + " was recently modified -- skipping"); 2445 return; 2446 } 2447 // ========== Cases where the region is not in hbase:meta ============= 2448 else if (!inMeta && !inHdfs && !isDeployed) { 2449 // We shouldn't have record of this region at all then! 2450 assert false : "Entry for region with no data"; 2451 } else if (!inMeta && !inHdfs && isDeployed) { 2452 errors.reportError(ERROR_CODE.NOT_IN_META_HDFS, "Region " 2453 + descriptiveName + ", key=" + key + ", not on HDFS or in hbase:meta but " + 2454 "deployed on " + Joiner.on(", ").join(hbi.deployedOn)); 2455 if (shouldFixAssignments()) { 2456 undeployRegions(hbi); 2457 } 2458 2459 } else if (!inMeta && inHdfs && !isDeployed) { 2460 if (hbi.isMerged()) { 2461 // This region has already been merged, the remaining hdfs file will be 2462 // cleaned by CatalogJanitor later 2463 hbi.setSkipChecks(true); 2464 LOG.info("Region " + descriptiveName 2465 + " got merge recently, its file(s) will be cleaned by CatalogJanitor later"); 2466 return; 2467 } 2468 errors.reportError(ERROR_CODE.NOT_IN_META_OR_DEPLOYED, "Region " 2469 + descriptiveName + " on HDFS, but not listed in hbase:meta " + 2470 "or deployed on any region server"); 2471 // restore region consistency of an adopted orphan 2472 if (shouldFixMeta()) { 2473 if (!hbi.isHdfsRegioninfoPresent()) { 2474 LOG.error("Region " + hbi.getHdfsHRI() + " could have been repaired" 2475 + " in table integrity repair phase if -fixHdfsOrphans was" + 2476 " used."); 2477 return; 2478 } 2479 2480 RegionInfo hri = hbi.getHdfsHRI(); 2481 TableInfo tableInfo = tablesInfo.get(hri.getTable()); 2482 2483 for (RegionInfo region : tableInfo.getRegionsFromMeta()) { 2484 if (Bytes.compareTo(region.getStartKey(), hri.getStartKey()) <= 0 2485 && (region.getEndKey().length == 0 || Bytes.compareTo(region.getEndKey(), 2486 hri.getEndKey()) >= 0) 2487 && Bytes.compareTo(region.getStartKey(), hri.getEndKey()) <= 0) { 2488 if(region.isSplit() || region.isOffline()) continue; 2489 Path regionDir = hbi.getHdfsRegionDir(); 2490 FileSystem fs = regionDir.getFileSystem(getConf()); 2491 List<Path> familyDirs = FSUtils.getFamilyDirs(fs, regionDir); 2492 for (Path familyDir : familyDirs) { 2493 List<Path> referenceFilePaths = FSUtils.getReferenceFilePaths(fs, familyDir); 2494 for (Path referenceFilePath : referenceFilePaths) { 2495 Path parentRegionDir = 2496 StoreFileInfo.getReferredToFile(referenceFilePath).getParent().getParent(); 2497 if (parentRegionDir.toString().endsWith(region.getEncodedName())) { 2498 LOG.warn(hri + " start and stop keys are in the range of " + region 2499 + ". The region might not be cleaned up from hdfs when region " + region 2500 + " split failed. Hence deleting from hdfs."); 2501 HRegionFileSystem.deleteRegionFromFileSystem(getConf(), fs, 2502 regionDir.getParent(), hri); 2503 return; 2504 } 2505 } 2506 } 2507 } 2508 } 2509 LOG.info("Patching hbase:meta with .regioninfo: " + hbi.getHdfsHRI()); 2510 int numReplicas = admin.getTableDescriptor(hbi.getTableName()).getRegionReplication(); 2511 HBaseFsckRepair.fixMetaHoleOnlineAndAddReplicas(getConf(), hbi.getHdfsHRI(), 2512 admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)) 2513 .getLiveServerMetrics().keySet(), numReplicas); 2514 2515 tryAssignmentRepair(hbi, "Trying to reassign region..."); 2516 } 2517 2518 } else if (!inMeta && inHdfs && isDeployed) { 2519 errors.reportError(ERROR_CODE.NOT_IN_META, "Region " + descriptiveName 2520 + " not in META, but deployed on " + Joiner.on(", ").join(hbi.deployedOn)); 2521 debugLsr(hbi.getHdfsRegionDir()); 2522 if (hbi.getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) { 2523 // for replicas, this means that we should undeploy the region (we would have 2524 // gone over the primaries and fixed meta holes in first phase under 2525 // checkAndFixConsistency; we shouldn't get the condition !inMeta at 2526 // this stage unless unwanted replica) 2527 if (shouldFixAssignments()) { 2528 undeployRegionsForHbi(hbi); 2529 } 2530 } 2531 if (shouldFixMeta() && hbi.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) { 2532 if (!hbi.isHdfsRegioninfoPresent()) { 2533 LOG.error("This should have been repaired in table integrity repair phase"); 2534 return; 2535 } 2536 2537 LOG.info("Patching hbase:meta with with .regioninfo: " + hbi.getHdfsHRI()); 2538 int numReplicas = admin.getTableDescriptor(hbi.getTableName()).getRegionReplication(); 2539 HBaseFsckRepair.fixMetaHoleOnlineAndAddReplicas(getConf(), hbi.getHdfsHRI(), 2540 admin.getClusterMetrics(EnumSet.of(Option.LIVE_SERVERS)) 2541 .getLiveServerMetrics().keySet(), numReplicas); 2542 tryAssignmentRepair(hbi, "Trying to fix unassigned region..."); 2543 } 2544 2545 // ========== Cases where the region is in hbase:meta ============= 2546 } else if (inMeta && inHdfs && !isDeployed && splitParent) { 2547 // check whether this is an actual error, or just transient state where parent 2548 // is not cleaned 2549 if (hbi.metaEntry.splitA != null && hbi.metaEntry.splitB != null) { 2550 // check that split daughters are there 2551 HbckInfo infoA = this.regionInfoMap.get(hbi.metaEntry.splitA.getEncodedName()); 2552 HbckInfo infoB = this.regionInfoMap.get(hbi.metaEntry.splitB.getEncodedName()); 2553 if (infoA != null && infoB != null) { 2554 // we already processed or will process daughters. Move on, nothing to see here. 2555 hbi.setSkipChecks(true); 2556 return; 2557 } 2558 } 2559 2560 // For Replica region, we need to do a similar check. If replica is not split successfully, 2561 // error is going to be reported against primary daughter region. 2562 if (hbi.getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) { 2563 LOG.info("Region " + descriptiveName + " is a split parent in META, in HDFS, " 2564 + "and not deployed on any region server. This may be transient."); 2565 hbi.setSkipChecks(true); 2566 return; 2567 } 2568 2569 errors.reportError(ERROR_CODE.LINGERING_SPLIT_PARENT, "Region " 2570 + descriptiveName + " is a split parent in META, in HDFS, " 2571 + "and not deployed on any region server. This could be transient, " 2572 + "consider to run the catalog janitor first!"); 2573 if (shouldFixSplitParents()) { 2574 setShouldRerun(); 2575 resetSplitParent(hbi); 2576 } 2577 } else if (inMeta && !inHdfs && !isDeployed) { 2578 errors.reportError(ERROR_CODE.NOT_IN_HDFS_OR_DEPLOYED, "Region " 2579 + descriptiveName + " found in META, but not in HDFS " 2580 + "or deployed on any region server."); 2581 if (shouldFixMeta()) { 2582 deleteMetaRegion(hbi); 2583 } 2584 } else if (inMeta && !inHdfs && isDeployed) { 2585 errors.reportError(ERROR_CODE.NOT_IN_HDFS, "Region " + descriptiveName 2586 + " found in META, but not in HDFS, " + 2587 "and deployed on " + Joiner.on(", ").join(hbi.deployedOn)); 2588 // We treat HDFS as ground truth. Any information in meta is transient 2589 // and equivalent data can be regenerated. So, lets unassign and remove 2590 // these problems from META. 2591 if (shouldFixAssignments()) { 2592 errors.print("Trying to fix unassigned region..."); 2593 undeployRegions(hbi); 2594 } 2595 if (shouldFixMeta()) { 2596 // wait for it to complete 2597 deleteMetaRegion(hbi); 2598 } 2599 } else if (inMeta && inHdfs && !isDeployed && shouldBeDeployed) { 2600 errors.reportError(ERROR_CODE.NOT_DEPLOYED, "Region " + descriptiveName 2601 + " not deployed on any region server."); 2602 tryAssignmentRepair(hbi, "Trying to fix unassigned region..."); 2603 } else if (inMeta && inHdfs && isDeployed && !shouldBeDeployed) { 2604 errors.reportError(ERROR_CODE.SHOULD_NOT_BE_DEPLOYED, 2605 "Region " + descriptiveName + " should not be deployed according " + 2606 "to META, but is deployed on " + Joiner.on(", ").join(hbi.deployedOn)); 2607 if (shouldFixAssignments()) { 2608 errors.print("Trying to close the region " + descriptiveName); 2609 setShouldRerun(); 2610 HBaseFsckRepair.fixMultiAssignment(connection, hbi.metaEntry, hbi.deployedOn); 2611 } 2612 } else if (inMeta && inHdfs && isMultiplyDeployed) { 2613 errors.reportError(ERROR_CODE.MULTI_DEPLOYED, "Region " + descriptiveName 2614 + " is listed in hbase:meta on region server " + hbi.metaEntry.regionServer 2615 + " but is multiply assigned to region servers " + 2616 Joiner.on(", ").join(hbi.deployedOn)); 2617 // If we are trying to fix the errors 2618 if (shouldFixAssignments()) { 2619 errors.print("Trying to fix assignment error..."); 2620 setShouldRerun(); 2621 HBaseFsckRepair.fixMultiAssignment(connection, hbi.metaEntry, hbi.deployedOn); 2622 } 2623 } else if (inMeta && inHdfs && isDeployed && !deploymentMatchesMeta) { 2624 errors.reportError(ERROR_CODE.SERVER_DOES_NOT_MATCH_META, "Region " 2625 + descriptiveName + " listed in hbase:meta on region server " + 2626 hbi.metaEntry.regionServer + " but found on region server " + 2627 hbi.deployedOn.get(0)); 2628 // If we are trying to fix the errors 2629 if (shouldFixAssignments()) { 2630 errors.print("Trying to fix assignment error..."); 2631 setShouldRerun(); 2632 HBaseFsckRepair.fixMultiAssignment(connection, hbi.metaEntry, hbi.deployedOn); 2633 HBaseFsckRepair.waitUntilAssigned(admin, hbi.getHdfsHRI()); 2634 } 2635 } else { 2636 errors.reportError(ERROR_CODE.UNKNOWN, "Region " + descriptiveName + 2637 " is in an unforeseen state:" + 2638 " inMeta=" + inMeta + 2639 " inHdfs=" + inHdfs + 2640 " isDeployed=" + isDeployed + 2641 " isMultiplyDeployed=" + isMultiplyDeployed + 2642 " deploymentMatchesMeta=" + deploymentMatchesMeta + 2643 " shouldBeDeployed=" + shouldBeDeployed); 2644 } 2645 } 2646 2647 /** 2648 * Checks tables integrity. Goes over all regions and scans the tables. 2649 * Collects all the pieces for each table and checks if there are missing, 2650 * repeated or overlapping ones. 2651 * @throws IOException 2652 */ 2653 SortedMap<TableName, TableInfo> checkIntegrity() throws IOException { 2654 tablesInfo = new TreeMap<>(); 2655 LOG.debug("There are " + regionInfoMap.size() + " region info entries"); 2656 for (HbckInfo hbi : regionInfoMap.values()) { 2657 // Check only valid, working regions 2658 if (hbi.metaEntry == null) { 2659 // this assumes that consistency check has run loadMetaEntry 2660 Path p = hbi.getHdfsRegionDir(); 2661 if (p == null) { 2662 errors.report("No regioninfo in Meta or HDFS. " + hbi); 2663 } 2664 2665 // TODO test. 2666 continue; 2667 } 2668 if (hbi.metaEntry.regionServer == null) { 2669 errors.detail("Skipping region because no region server: " + hbi); 2670 continue; 2671 } 2672 if (hbi.metaEntry.isOffline()) { 2673 errors.detail("Skipping region because it is offline: " + hbi); 2674 continue; 2675 } 2676 if (hbi.containsOnlyHdfsEdits()) { 2677 errors.detail("Skipping region because it only contains edits" + hbi); 2678 continue; 2679 } 2680 2681 // Missing regionDir or over-deployment is checked elsewhere. Include 2682 // these cases in modTInfo, so we can evaluate those regions as part of 2683 // the region chain in META 2684 //if (hbi.foundRegionDir == null) continue; 2685 //if (hbi.deployedOn.size() != 1) continue; 2686 if (hbi.deployedOn.isEmpty()) continue; 2687 2688 // We should be safe here 2689 TableName tableName = hbi.metaEntry.getTable(); 2690 TableInfo modTInfo = tablesInfo.get(tableName); 2691 if (modTInfo == null) { 2692 modTInfo = new TableInfo(tableName); 2693 } 2694 for (ServerName server : hbi.deployedOn) { 2695 modTInfo.addServer(server); 2696 } 2697 2698 if (!hbi.isSkipChecks()) { 2699 modTInfo.addRegionInfo(hbi); 2700 } 2701 2702 tablesInfo.put(tableName, modTInfo); 2703 } 2704 2705 loadTableInfosForTablesWithNoRegion(); 2706 2707 logParallelMerge(); 2708 for (TableInfo tInfo : tablesInfo.values()) { 2709 TableIntegrityErrorHandler handler = tInfo.new IntegrityFixSuggester(tInfo, errors); 2710 if (!tInfo.checkRegionChain(handler)) { 2711 errors.report("Found inconsistency in table " + tInfo.getName()); 2712 } 2713 } 2714 return tablesInfo; 2715 } 2716 2717 /** Loads table info's for tables that may not have been included, since there are no 2718 * regions reported for the table, but table dir is there in hdfs 2719 */ 2720 private void loadTableInfosForTablesWithNoRegion() throws IOException { 2721 Map<String, TableDescriptor> allTables = new FSTableDescriptors(getConf()).getAll(); 2722 for (TableDescriptor htd : allTables.values()) { 2723 if (checkMetaOnly && !htd.isMetaTable()) { 2724 continue; 2725 } 2726 2727 TableName tableName = htd.getTableName(); 2728 if (isTableIncluded(tableName) && !tablesInfo.containsKey(tableName)) { 2729 TableInfo tableInfo = new TableInfo(tableName); 2730 tableInfo.htds.add(htd); 2731 tablesInfo.put(htd.getTableName(), tableInfo); 2732 } 2733 } 2734 } 2735 2736 /** 2737 * Merge hdfs data by moving from contained HbckInfo into targetRegionDir. 2738 * @return number of file move fixes done to merge regions. 2739 */ 2740 public int mergeRegionDirs(Path targetRegionDir, HbckInfo contained) throws IOException { 2741 int fileMoves = 0; 2742 String thread = Thread.currentThread().getName(); 2743 LOG.debug("[" + thread + "] Contained region dir after close and pause"); 2744 debugLsr(contained.getHdfsRegionDir()); 2745 2746 // rename the contained into the container. 2747 FileSystem fs = targetRegionDir.getFileSystem(getConf()); 2748 FileStatus[] dirs = null; 2749 try { 2750 dirs = fs.listStatus(contained.getHdfsRegionDir()); 2751 } catch (FileNotFoundException fnfe) { 2752 // region we are attempting to merge in is not present! Since this is a merge, there is 2753 // no harm skipping this region if it does not exist. 2754 if (!fs.exists(contained.getHdfsRegionDir())) { 2755 LOG.warn("[" + thread + "] HDFS region dir " + contained.getHdfsRegionDir() 2756 + " is missing. Assuming already sidelined or moved."); 2757 } else { 2758 sidelineRegionDir(fs, contained); 2759 } 2760 return fileMoves; 2761 } 2762 2763 if (dirs == null) { 2764 if (!fs.exists(contained.getHdfsRegionDir())) { 2765 LOG.warn("[" + thread + "] HDFS region dir " + contained.getHdfsRegionDir() 2766 + " already sidelined."); 2767 } else { 2768 sidelineRegionDir(fs, contained); 2769 } 2770 return fileMoves; 2771 } 2772 2773 for (FileStatus cf : dirs) { 2774 Path src = cf.getPath(); 2775 Path dst = new Path(targetRegionDir, src.getName()); 2776 2777 if (src.getName().equals(HRegionFileSystem.REGION_INFO_FILE)) { 2778 // do not copy the old .regioninfo file. 2779 continue; 2780 } 2781 2782 if (src.getName().equals(HConstants.HREGION_OLDLOGDIR_NAME)) { 2783 // do not copy the .oldlogs files 2784 continue; 2785 } 2786 2787 LOG.info("[" + thread + "] Moving files from " + src + " into containing region " + dst); 2788 // FileSystem.rename is inconsistent with directories -- if the 2789 // dst (foo/a) exists and is a dir, and the src (foo/b) is a dir, 2790 // it moves the src into the dst dir resulting in (foo/a/b). If 2791 // the dst does not exist, and the src a dir, src becomes dst. (foo/b) 2792 for (FileStatus hfile : fs.listStatus(src)) { 2793 boolean success = fs.rename(hfile.getPath(), dst); 2794 if (success) { 2795 fileMoves++; 2796 } 2797 } 2798 LOG.debug("[" + thread + "] Sideline directory contents:"); 2799 debugLsr(targetRegionDir); 2800 } 2801 2802 // if all success. 2803 sidelineRegionDir(fs, contained); 2804 LOG.info("[" + thread + "] Sidelined region dir "+ contained.getHdfsRegionDir() + " into " + 2805 getSidelineDir()); 2806 debugLsr(contained.getHdfsRegionDir()); 2807 2808 return fileMoves; 2809 } 2810 2811 2812 static class WorkItemOverlapMerge implements Callable<Void> { 2813 private TableIntegrityErrorHandler handler; 2814 Collection<HbckInfo> overlapgroup; 2815 2816 WorkItemOverlapMerge(Collection<HbckInfo> overlapgroup, TableIntegrityErrorHandler handler) { 2817 this.handler = handler; 2818 this.overlapgroup = overlapgroup; 2819 } 2820 2821 @Override 2822 public Void call() throws Exception { 2823 handler.handleOverlapGroup(overlapgroup); 2824 return null; 2825 } 2826 }; 2827 2828 2829 /** 2830 * Maintain information about a particular table. 2831 */ 2832 public class TableInfo { 2833 TableName tableName; 2834 TreeSet <ServerName> deployedOn; 2835 2836 // backwards regions 2837 final List<HbckInfo> backwards = new ArrayList<>(); 2838 2839 // sidelined big overlapped regions 2840 final Map<Path, HbckInfo> sidelinedRegions = new HashMap<>(); 2841 2842 // region split calculator 2843 final RegionSplitCalculator<HbckInfo> sc = new RegionSplitCalculator<>(cmp); 2844 2845 // Histogram of different TableDescriptors found. Ideally there is only one! 2846 final Set<TableDescriptor> htds = new HashSet<>(); 2847 2848 // key = start split, values = set of splits in problem group 2849 final Multimap<byte[], HbckInfo> overlapGroups = 2850 TreeMultimap.create(RegionSplitCalculator.BYTES_COMPARATOR, cmp); 2851 2852 // list of regions derived from meta entries. 2853 private ImmutableList<RegionInfo> regionsFromMeta = null; 2854 2855 TableInfo(TableName name) { 2856 this.tableName = name; 2857 deployedOn = new TreeSet <>(); 2858 } 2859 2860 /** 2861 * @return descriptor common to all regions. null if are none or multiple! 2862 */ 2863 private TableDescriptor getHTD() { 2864 if (htds.size() == 1) { 2865 return (TableDescriptor)htds.toArray()[0]; 2866 } else { 2867 LOG.error("None/Multiple table descriptors found for table '" 2868 + tableName + "' regions: " + htds); 2869 } 2870 return null; 2871 } 2872 2873 public void addRegionInfo(HbckInfo hir) { 2874 if (Bytes.equals(hir.getEndKey(), HConstants.EMPTY_END_ROW)) { 2875 // end key is absolute end key, just add it. 2876 // ignore replicas other than primary for these checks 2877 if (hir.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) sc.add(hir); 2878 return; 2879 } 2880 2881 // if not the absolute end key, check for cycle 2882 if (Bytes.compareTo(hir.getStartKey(), hir.getEndKey()) > 0) { 2883 errors.reportError( 2884 ERROR_CODE.REGION_CYCLE, 2885 String.format("The endkey for this region comes before the " 2886 + "startkey, startkey=%s, endkey=%s", 2887 Bytes.toStringBinary(hir.getStartKey()), 2888 Bytes.toStringBinary(hir.getEndKey())), this, hir); 2889 backwards.add(hir); 2890 return; 2891 } 2892 2893 // main case, add to split calculator 2894 // ignore replicas other than primary for these checks 2895 if (hir.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) sc.add(hir); 2896 } 2897 2898 public void addServer(ServerName server) { 2899 this.deployedOn.add(server); 2900 } 2901 2902 public TableName getName() { 2903 return tableName; 2904 } 2905 2906 public int getNumRegions() { 2907 return sc.getStarts().size() + backwards.size(); 2908 } 2909 2910 public synchronized ImmutableList<RegionInfo> getRegionsFromMeta() { 2911 // lazy loaded, synchronized to ensure a single load 2912 if (regionsFromMeta == null) { 2913 List<RegionInfo> regions = new ArrayList<>(); 2914 for (HbckInfo h : HBaseFsck.this.regionInfoMap.values()) { 2915 if (tableName.equals(h.getTableName())) { 2916 if (h.metaEntry != null) { 2917 regions.add(h.metaEntry); 2918 } 2919 } 2920 } 2921 regionsFromMeta = Ordering.from(RegionInfo.COMPARATOR).immutableSortedCopy(regions); 2922 } 2923 2924 return regionsFromMeta; 2925 } 2926 2927 private class IntegrityFixSuggester extends TableIntegrityErrorHandlerImpl { 2928 ErrorReporter errors; 2929 2930 IntegrityFixSuggester(TableInfo ti, ErrorReporter errors) { 2931 this.errors = errors; 2932 setTableInfo(ti); 2933 } 2934 2935 @Override 2936 public void handleRegionStartKeyNotEmpty(HbckInfo hi) throws IOException{ 2937 errors.reportError(ERROR_CODE.FIRST_REGION_STARTKEY_NOT_EMPTY, 2938 "First region should start with an empty key. You need to " 2939 + " create a new region and regioninfo in HDFS to plug the hole.", 2940 getTableInfo(), hi); 2941 } 2942 2943 @Override 2944 public void handleRegionEndKeyNotEmpty(byte[] curEndKey) throws IOException { 2945 errors.reportError(ERROR_CODE.LAST_REGION_ENDKEY_NOT_EMPTY, 2946 "Last region should end with an empty key. You need to " 2947 + "create a new region and regioninfo in HDFS to plug the hole.", getTableInfo()); 2948 } 2949 2950 @Override 2951 public void handleDegenerateRegion(HbckInfo hi) throws IOException{ 2952 errors.reportError(ERROR_CODE.DEGENERATE_REGION, 2953 "Region has the same start and end key.", getTableInfo(), hi); 2954 } 2955 2956 @Override 2957 public void handleDuplicateStartKeys(HbckInfo r1, HbckInfo r2) throws IOException{ 2958 byte[] key = r1.getStartKey(); 2959 // dup start key 2960 errors.reportError(ERROR_CODE.DUPE_STARTKEYS, 2961 "Multiple regions have the same startkey: " 2962 + Bytes.toStringBinary(key), getTableInfo(), r1); 2963 errors.reportError(ERROR_CODE.DUPE_STARTKEYS, 2964 "Multiple regions have the same startkey: " 2965 + Bytes.toStringBinary(key), getTableInfo(), r2); 2966 } 2967 2968 @Override 2969 public void handleSplit(HbckInfo r1, HbckInfo r2) throws IOException{ 2970 byte[] key = r1.getStartKey(); 2971 // dup start key 2972 errors.reportError(ERROR_CODE.DUPE_ENDKEYS, 2973 "Multiple regions have the same regionID: " 2974 + Bytes.toStringBinary(key), getTableInfo(), r1); 2975 errors.reportError(ERROR_CODE.DUPE_ENDKEYS, 2976 "Multiple regions have the same regionID: " 2977 + Bytes.toStringBinary(key), getTableInfo(), r2); 2978 } 2979 2980 @Override 2981 public void handleOverlapInRegionChain(HbckInfo hi1, HbckInfo hi2) throws IOException{ 2982 errors.reportError(ERROR_CODE.OVERLAP_IN_REGION_CHAIN, 2983 "There is an overlap in the region chain.", 2984 getTableInfo(), hi1, hi2); 2985 } 2986 2987 @Override 2988 public void handleHoleInRegionChain(byte[] holeStart, byte[] holeStop) throws IOException{ 2989 errors.reportError( 2990 ERROR_CODE.HOLE_IN_REGION_CHAIN, 2991 "There is a hole in the region chain between " 2992 + Bytes.toStringBinary(holeStart) + " and " 2993 + Bytes.toStringBinary(holeStop) 2994 + ". You need to create a new .regioninfo and region " 2995 + "dir in hdfs to plug the hole."); 2996 } 2997 }; 2998 2999 /** 3000 * This handler fixes integrity errors from hdfs information. There are 3001 * basically three classes of integrity problems 1) holes, 2) overlaps, and 3002 * 3) invalid regions. 3003 * 3004 * This class overrides methods that fix holes and the overlap group case. 3005 * Individual cases of particular overlaps are handled by the general 3006 * overlap group merge repair case. 3007 * 3008 * If hbase is online, this forces regions offline before doing merge 3009 * operations. 3010 */ 3011 private class HDFSIntegrityFixer extends IntegrityFixSuggester { 3012 Configuration conf; 3013 3014 boolean fixOverlaps = true; 3015 3016 HDFSIntegrityFixer(TableInfo ti, ErrorReporter errors, Configuration conf, 3017 boolean fixHoles, boolean fixOverlaps) { 3018 super(ti, errors); 3019 this.conf = conf; 3020 this.fixOverlaps = fixOverlaps; 3021 // TODO properly use fixHoles 3022 } 3023 3024 /** 3025 * This is a special case hole -- when the first region of a table is 3026 * missing from META, HBase doesn't acknowledge the existance of the 3027 * table. 3028 */ 3029 @Override 3030 public void handleRegionStartKeyNotEmpty(HbckInfo next) throws IOException { 3031 errors.reportError(ERROR_CODE.FIRST_REGION_STARTKEY_NOT_EMPTY, 3032 "First region should start with an empty key. Creating a new " + 3033 "region and regioninfo in HDFS to plug the hole.", 3034 getTableInfo(), next); 3035 TableDescriptor htd = getTableInfo().getHTD(); 3036 // from special EMPTY_START_ROW to next region's startKey 3037 RegionInfo newRegion = RegionInfoBuilder.newBuilder(htd.getTableName()) 3038 .setStartKey(HConstants.EMPTY_START_ROW) 3039 .setEndKey(next.getStartKey()) 3040 .build(); 3041 3042 // TODO test 3043 HRegion region = HBaseFsckRepair.createHDFSRegionDir(conf, newRegion, htd); 3044 LOG.info("Table region start key was not empty. Created new empty region: " 3045 + newRegion + " " +region); 3046 fixes++; 3047 } 3048 3049 @Override 3050 public void handleRegionEndKeyNotEmpty(byte[] curEndKey) throws IOException { 3051 errors.reportError(ERROR_CODE.LAST_REGION_ENDKEY_NOT_EMPTY, 3052 "Last region should end with an empty key. Creating a new " 3053 + "region and regioninfo in HDFS to plug the hole.", getTableInfo()); 3054 TableDescriptor htd = getTableInfo().getHTD(); 3055 // from curEndKey to EMPTY_START_ROW 3056 RegionInfo newRegion = RegionInfoBuilder.newBuilder(htd.getTableName()) 3057 .setStartKey(curEndKey) 3058 .setEndKey(HConstants.EMPTY_START_ROW) 3059 .build(); 3060 3061 HRegion region = HBaseFsckRepair.createHDFSRegionDir(conf, newRegion, htd); 3062 LOG.info("Table region end key was not empty. Created new empty region: " + newRegion 3063 + " " + region); 3064 fixes++; 3065 } 3066 3067 /** 3068 * There is a hole in the hdfs regions that violates the table integrity 3069 * rules. Create a new empty region that patches the hole. 3070 */ 3071 @Override 3072 public void handleHoleInRegionChain(byte[] holeStartKey, byte[] holeStopKey) throws IOException { 3073 errors.reportError( 3074 ERROR_CODE.HOLE_IN_REGION_CHAIN, 3075 "There is a hole in the region chain between " 3076 + Bytes.toStringBinary(holeStartKey) + " and " 3077 + Bytes.toStringBinary(holeStopKey) 3078 + ". Creating a new regioninfo and region " 3079 + "dir in hdfs to plug the hole."); 3080 TableDescriptor htd = getTableInfo().getHTD(); 3081 RegionInfo newRegion = RegionInfoBuilder.newBuilder(htd.getTableName()) 3082 .setStartKey(holeStartKey) 3083 .setEndKey(holeStopKey) 3084 .build(); 3085 HRegion region = HBaseFsckRepair.createHDFSRegionDir(conf, newRegion, htd); 3086 LOG.info("Plugged hole by creating new empty region: "+ newRegion + " " +region); 3087 fixes++; 3088 } 3089 3090 /** 3091 * This takes set of overlapping regions and merges them into a single 3092 * region. This covers cases like degenerate regions, shared start key, 3093 * general overlaps, duplicate ranges, and partial overlapping regions. 3094 * 3095 * Cases: 3096 * - Clean regions that overlap 3097 * - Only .oldlogs regions (can't find start/stop range, or figure out) 3098 * 3099 * This is basically threadsafe, except for the fixer increment in mergeOverlaps. 3100 */ 3101 @Override 3102 public void handleOverlapGroup(Collection<HbckInfo> overlap) 3103 throws IOException { 3104 Preconditions.checkNotNull(overlap); 3105 Preconditions.checkArgument(overlap.size() >0); 3106 3107 if (!this.fixOverlaps) { 3108 LOG.warn("Not attempting to repair overlaps."); 3109 return; 3110 } 3111 3112 if (overlap.size() > maxMerge) { 3113 LOG.warn("Overlap group has " + overlap.size() + " overlapping " + 3114 "regions which is greater than " + maxMerge + ", the max number of regions to merge"); 3115 if (sidelineBigOverlaps) { 3116 // we only sideline big overlapped groups that exceeds the max number of regions to merge 3117 sidelineBigOverlaps(overlap); 3118 } 3119 return; 3120 } 3121 if (shouldRemoveParents()) { 3122 removeParentsAndFixSplits(overlap); 3123 } 3124 mergeOverlaps(overlap); 3125 } 3126 3127 void removeParentsAndFixSplits(Collection<HbckInfo> overlap) throws IOException { 3128 Pair<byte[], byte[]> range = null; 3129 HbckInfo parent = null; 3130 HbckInfo daughterA = null; 3131 HbckInfo daughterB = null; 3132 Collection<HbckInfo> daughters = new ArrayList<HbckInfo>(overlap); 3133 3134 String thread = Thread.currentThread().getName(); 3135 LOG.info("== [" + thread + "] Attempting fix splits in overlap state."); 3136 3137 // we only can handle a single split per group at the time 3138 if (overlap.size() > 3) { 3139 LOG.info("Too many overlaps were found on this group, falling back to regular merge."); 3140 return; 3141 } 3142 3143 for (HbckInfo hi : overlap) { 3144 if (range == null) { 3145 range = new Pair<byte[], byte[]>(hi.getStartKey(), hi.getEndKey()); 3146 } else { 3147 if (RegionSplitCalculator.BYTES_COMPARATOR 3148 .compare(hi.getStartKey(), range.getFirst()) < 0) { 3149 range.setFirst(hi.getStartKey()); 3150 } 3151 if (RegionSplitCalculator.BYTES_COMPARATOR 3152 .compare(hi.getEndKey(), range.getSecond()) > 0) { 3153 range.setSecond(hi.getEndKey()); 3154 } 3155 } 3156 } 3157 3158 LOG.info("This group range is [" + Bytes.toStringBinary(range.getFirst()) + ", " 3159 + Bytes.toStringBinary(range.getSecond()) + "]"); 3160 3161 // attempt to find a possible parent for the edge case of a split 3162 for (HbckInfo hi : overlap) { 3163 if (Bytes.compareTo(hi.getHdfsHRI().getStartKey(), range.getFirst()) == 0 3164 && Bytes.compareTo(hi.getHdfsHRI().getEndKey(), range.getSecond()) == 0) { 3165 LOG.info("This is a parent for this group: " + hi.toString()); 3166 parent = hi; 3167 } 3168 } 3169 3170 // Remove parent regions from daughters collection 3171 if (parent != null) { 3172 daughters.remove(parent); 3173 } 3174 3175 // Lets verify that daughters share the regionID at split time and they 3176 // were created after the parent 3177 for (HbckInfo hi : daughters) { 3178 if (Bytes.compareTo(hi.getHdfsHRI().getStartKey(), range.getFirst()) == 0) { 3179 if (parent.getHdfsHRI().getRegionId() < hi.getHdfsHRI().getRegionId()) { 3180 daughterA = hi; 3181 } 3182 } 3183 if (Bytes.compareTo(hi.getHdfsHRI().getEndKey(), range.getSecond()) == 0) { 3184 if (parent.getHdfsHRI().getRegionId() < hi.getHdfsHRI().getRegionId()) { 3185 daughterB = hi; 3186 } 3187 } 3188 } 3189 3190 // daughters must share the same regionID and we should have a parent too 3191 if (daughterA.getHdfsHRI().getRegionId() != daughterB.getHdfsHRI().getRegionId() || parent == null) 3192 return; 3193 3194 FileSystem fs = FileSystem.get(conf); 3195 LOG.info("Found parent: " + parent.getRegionNameAsString()); 3196 LOG.info("Found potential daughter a: " + daughterA.getRegionNameAsString()); 3197 LOG.info("Found potential daughter b: " + daughterB.getRegionNameAsString()); 3198 LOG.info("Trying to fix parent in overlap by removing the parent."); 3199 try { 3200 closeRegion(parent); 3201 } catch (IOException ioe) { 3202 LOG.warn("Parent region could not be closed, continuing with regular merge...", ioe); 3203 return; 3204 } catch (InterruptedException ie) { 3205 LOG.warn("Parent region could not be closed, continuing with regular merge...", ie); 3206 return; 3207 } 3208 3209 try { 3210 offline(parent.getRegionName()); 3211 } catch (IOException ioe) { 3212 LOG.warn("Unable to offline parent region: " + parent.getRegionNameAsString() 3213 + ". Just continuing with regular merge... ", ioe); 3214 return; 3215 } 3216 3217 try { 3218 HBaseFsckRepair.removeParentInMeta(conf, parent.getHdfsHRI()); 3219 } catch (IOException ioe) { 3220 LOG.warn("Unable to remove parent region in META: " + parent.getRegionNameAsString() 3221 + ". Just continuing with regular merge... ", ioe); 3222 return; 3223 } 3224 3225 sidelineRegionDir(fs, parent); 3226 LOG.info("[" + thread + "] Sidelined parent region dir "+ parent.getHdfsRegionDir() + " into " + 3227 getSidelineDir()); 3228 debugLsr(parent.getHdfsRegionDir()); 3229 3230 // Make sure we don't have the parents and daughters around 3231 overlap.remove(parent); 3232 overlap.remove(daughterA); 3233 overlap.remove(daughterB); 3234 3235 LOG.info("Done fixing split."); 3236 3237 } 3238 3239 void mergeOverlaps(Collection<HbckInfo> overlap) 3240 throws IOException { 3241 String thread = Thread.currentThread().getName(); 3242 LOG.info("== [" + thread + "] Merging regions into one region: " 3243 + Joiner.on(",").join(overlap)); 3244 // get the min / max range and close all concerned regions 3245 Pair<byte[], byte[]> range = null; 3246 for (HbckInfo hi : overlap) { 3247 if (range == null) { 3248 range = new Pair<>(hi.getStartKey(), hi.getEndKey()); 3249 } else { 3250 if (RegionSplitCalculator.BYTES_COMPARATOR 3251 .compare(hi.getStartKey(), range.getFirst()) < 0) { 3252 range.setFirst(hi.getStartKey()); 3253 } 3254 if (RegionSplitCalculator.BYTES_COMPARATOR 3255 .compare(hi.getEndKey(), range.getSecond()) > 0) { 3256 range.setSecond(hi.getEndKey()); 3257 } 3258 } 3259 // need to close files so delete can happen. 3260 LOG.debug("[" + thread + "] Closing region before moving data around: " + hi); 3261 LOG.debug("[" + thread + "] Contained region dir before close"); 3262 debugLsr(hi.getHdfsRegionDir()); 3263 try { 3264 LOG.info("[" + thread + "] Closing region: " + hi); 3265 closeRegion(hi); 3266 } catch (IOException ioe) { 3267 LOG.warn("[" + thread + "] Was unable to close region " + hi 3268 + ". Just continuing... ", ioe); 3269 } catch (InterruptedException e) { 3270 LOG.warn("[" + thread + "] Was unable to close region " + hi 3271 + ". Just continuing... ", e); 3272 } 3273 3274 try { 3275 LOG.info("[" + thread + "] Offlining region: " + hi); 3276 offline(hi.getRegionName()); 3277 } catch (IOException ioe) { 3278 LOG.warn("[" + thread + "] Unable to offline region from master: " + hi 3279 + ". Just continuing... ", ioe); 3280 } 3281 } 3282 3283 // create new empty container region. 3284 TableDescriptor htd = getTableInfo().getHTD(); 3285 // from start key to end Key 3286 RegionInfo newRegion = RegionInfoBuilder.newBuilder(htd.getTableName()) 3287 .setStartKey(range.getFirst()) 3288 .setEndKey(range.getSecond()) 3289 .build(); 3290 HRegion region = HBaseFsckRepair.createHDFSRegionDir(conf, newRegion, htd); 3291 LOG.info("[" + thread + "] Created new empty container region: " + 3292 newRegion + " to contain regions: " + Joiner.on(",").join(overlap)); 3293 debugLsr(region.getRegionFileSystem().getRegionDir()); 3294 3295 // all target regions are closed, should be able to safely cleanup. 3296 boolean didFix= false; 3297 Path target = region.getRegionFileSystem().getRegionDir(); 3298 for (HbckInfo contained : overlap) { 3299 LOG.info("[" + thread + "] Merging " + contained + " into " + target ); 3300 int merges = mergeRegionDirs(target, contained); 3301 if (merges > 0) { 3302 didFix = true; 3303 } 3304 } 3305 if (didFix) { 3306 fixes++; 3307 } 3308 } 3309 3310 /** 3311 * Sideline some regions in a big overlap group so that it 3312 * will have fewer regions, and it is easier to merge them later on. 3313 * 3314 * @param bigOverlap the overlapped group with regions more than maxMerge 3315 * @throws IOException 3316 */ 3317 void sidelineBigOverlaps( 3318 Collection<HbckInfo> bigOverlap) throws IOException { 3319 int overlapsToSideline = bigOverlap.size() - maxMerge; 3320 if (overlapsToSideline > maxOverlapsToSideline) { 3321 overlapsToSideline = maxOverlapsToSideline; 3322 } 3323 List<HbckInfo> regionsToSideline = 3324 RegionSplitCalculator.findBigRanges(bigOverlap, overlapsToSideline); 3325 FileSystem fs = FileSystem.get(conf); 3326 for (HbckInfo regionToSideline: regionsToSideline) { 3327 try { 3328 LOG.info("Closing region: " + regionToSideline); 3329 closeRegion(regionToSideline); 3330 } catch (IOException ioe) { 3331 LOG.warn("Was unable to close region " + regionToSideline 3332 + ". Just continuing... ", ioe); 3333 } catch (InterruptedException e) { 3334 LOG.warn("Was unable to close region " + regionToSideline 3335 + ". Just continuing... ", e); 3336 } 3337 3338 try { 3339 LOG.info("Offlining region: " + regionToSideline); 3340 offline(regionToSideline.getRegionName()); 3341 } catch (IOException ioe) { 3342 LOG.warn("Unable to offline region from master: " + regionToSideline 3343 + ". Just continuing... ", ioe); 3344 } 3345 3346 LOG.info("Before sideline big overlapped region: " + regionToSideline.toString()); 3347 Path sidelineRegionDir = sidelineRegionDir(fs, TO_BE_LOADED, regionToSideline); 3348 if (sidelineRegionDir != null) { 3349 sidelinedRegions.put(sidelineRegionDir, regionToSideline); 3350 LOG.info("After sidelined big overlapped region: " 3351 + regionToSideline.getRegionNameAsString() 3352 + " to " + sidelineRegionDir.toString()); 3353 fixes++; 3354 } 3355 } 3356 } 3357 } 3358 3359 /** 3360 * Check the region chain (from META) of this table. We are looking for 3361 * holes, overlaps, and cycles. 3362 * @return false if there are errors 3363 * @throws IOException 3364 */ 3365 public boolean checkRegionChain(TableIntegrityErrorHandler handler) throws IOException { 3366 // When table is disabled no need to check for the region chain. Some of the regions 3367 // accidently if deployed, this below code might report some issues like missing start 3368 // or end regions or region hole in chain and may try to fix which is unwanted. 3369 if (isTableDisabled(this.tableName)) { 3370 return true; 3371 } 3372 int originalErrorsCount = errors.getErrorList().size(); 3373 Multimap<byte[], HbckInfo> regions = sc.calcCoverage(); 3374 SortedSet<byte[]> splits = sc.getSplits(); 3375 3376 byte[] prevKey = null; 3377 byte[] problemKey = null; 3378 3379 if (splits.isEmpty()) { 3380 // no region for this table 3381 handler.handleHoleInRegionChain(HConstants.EMPTY_START_ROW, HConstants.EMPTY_END_ROW); 3382 } 3383 3384 for (byte[] key : splits) { 3385 Collection<HbckInfo> ranges = regions.get(key); 3386 if (prevKey == null && !Bytes.equals(key, HConstants.EMPTY_BYTE_ARRAY)) { 3387 for (HbckInfo rng : ranges) { 3388 handler.handleRegionStartKeyNotEmpty(rng); 3389 } 3390 } 3391 3392 // check for degenerate ranges 3393 for (HbckInfo rng : ranges) { 3394 // special endkey case converts '' to null 3395 byte[] endKey = rng.getEndKey(); 3396 endKey = (endKey.length == 0) ? null : endKey; 3397 if (Bytes.equals(rng.getStartKey(),endKey)) { 3398 handler.handleDegenerateRegion(rng); 3399 } 3400 } 3401 3402 if (ranges.size() == 1) { 3403 // this split key is ok -- no overlap, not a hole. 3404 if (problemKey != null) { 3405 LOG.warn("reached end of problem group: " + Bytes.toStringBinary(key)); 3406 } 3407 problemKey = null; // fell through, no more problem. 3408 } else if (ranges.size() > 1) { 3409 // set the new problem key group name, if already have problem key, just 3410 // keep using it. 3411 if (problemKey == null) { 3412 // only for overlap regions. 3413 LOG.warn("Naming new problem group: " + Bytes.toStringBinary(key)); 3414 problemKey = key; 3415 } 3416 overlapGroups.putAll(problemKey, ranges); 3417 3418 // record errors 3419 ArrayList<HbckInfo> subRange = new ArrayList<>(ranges); 3420 // this dumb and n^2 but this shouldn't happen often 3421 for (HbckInfo r1 : ranges) { 3422 if (r1.getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) continue; 3423 subRange.remove(r1); 3424 for (HbckInfo r2 : subRange) { 3425 if (r2.getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) continue; 3426 // general case of same start key 3427 if (Bytes.compareTo(r1.getStartKey(), r2.getStartKey())==0) { 3428 handler.handleDuplicateStartKeys(r1,r2); 3429 } else if (Bytes.compareTo(r1.getEndKey(), r2.getStartKey())==0 && 3430 r1.getHdfsHRI().getRegionId() == r2.getHdfsHRI().getRegionId()) { 3431 LOG.info("this is a split, log to splits"); 3432 handler.handleSplit(r1, r2); 3433 } else { 3434 // overlap 3435 handler.handleOverlapInRegionChain(r1, r2); 3436 } 3437 } 3438 } 3439 3440 } else if (ranges.isEmpty()) { 3441 if (problemKey != null) { 3442 LOG.warn("reached end of problem group: " + Bytes.toStringBinary(key)); 3443 } 3444 problemKey = null; 3445 3446 byte[] holeStopKey = sc.getSplits().higher(key); 3447 // if higher key is null we reached the top. 3448 if (holeStopKey != null) { 3449 // hole 3450 handler.handleHoleInRegionChain(key, holeStopKey); 3451 } 3452 } 3453 prevKey = key; 3454 } 3455 3456 // When the last region of a table is proper and having an empty end key, 'prevKey' 3457 // will be null. 3458 if (prevKey != null) { 3459 handler.handleRegionEndKeyNotEmpty(prevKey); 3460 } 3461 3462 // TODO fold this into the TableIntegrityHandler 3463 if (getConf().getBoolean("hbasefsck.overlap.merge.parallel", true)) { 3464 boolean ok = handleOverlapsParallel(handler, prevKey); 3465 if (!ok) { 3466 return false; 3467 } 3468 } else { 3469 for (Collection<HbckInfo> overlap : overlapGroups.asMap().values()) { 3470 handler.handleOverlapGroup(overlap); 3471 } 3472 } 3473 3474 if (details) { 3475 // do full region split map dump 3476 errors.print("---- Table '" + this.tableName 3477 + "': region split map"); 3478 dump(splits, regions); 3479 errors.print("---- Table '" + this.tableName 3480 + "': overlap groups"); 3481 dumpOverlapProblems(overlapGroups); 3482 errors.print("There are " + overlapGroups.keySet().size() 3483 + " overlap groups with " + overlapGroups.size() 3484 + " overlapping regions"); 3485 } 3486 if (!sidelinedRegions.isEmpty()) { 3487 LOG.warn("Sidelined big overlapped regions, please bulk load them!"); 3488 errors.print("---- Table '" + this.tableName 3489 + "': sidelined big overlapped regions"); 3490 dumpSidelinedRegions(sidelinedRegions); 3491 } 3492 return errors.getErrorList().size() == originalErrorsCount; 3493 } 3494 3495 private boolean handleOverlapsParallel(TableIntegrityErrorHandler handler, byte[] prevKey) 3496 throws IOException { 3497 // we parallelize overlap handler for the case we have lots of groups to fix. We can 3498 // safely assume each group is independent. 3499 List<WorkItemOverlapMerge> merges = new ArrayList<>(overlapGroups.size()); 3500 List<Future<Void>> rets; 3501 for (Collection<HbckInfo> overlap : overlapGroups.asMap().values()) { 3502 // 3503 merges.add(new WorkItemOverlapMerge(overlap, handler)); 3504 } 3505 try { 3506 rets = executor.invokeAll(merges); 3507 } catch (InterruptedException e) { 3508 LOG.error("Overlap merges were interrupted", e); 3509 return false; 3510 } 3511 for(int i=0; i<merges.size(); i++) { 3512 WorkItemOverlapMerge work = merges.get(i); 3513 Future<Void> f = rets.get(i); 3514 try { 3515 f.get(); 3516 } catch(ExecutionException e) { 3517 LOG.warn("Failed to merge overlap group" + work, e.getCause()); 3518 } catch (InterruptedException e) { 3519 LOG.error("Waiting for overlap merges was interrupted", e); 3520 return false; 3521 } 3522 } 3523 return true; 3524 } 3525 3526 /** 3527 * This dumps data in a visually reasonable way for visual debugging 3528 * 3529 * @param splits 3530 * @param regions 3531 */ 3532 void dump(SortedSet<byte[]> splits, Multimap<byte[], HbckInfo> regions) { 3533 // we display this way because the last end key should be displayed as well. 3534 StringBuilder sb = new StringBuilder(); 3535 for (byte[] k : splits) { 3536 sb.setLength(0); // clear out existing buffer, if any. 3537 sb.append(Bytes.toStringBinary(k) + ":\t"); 3538 for (HbckInfo r : regions.get(k)) { 3539 sb.append("[ "+ r.toString() + ", " 3540 + Bytes.toStringBinary(r.getEndKey())+ "]\t"); 3541 } 3542 errors.print(sb.toString()); 3543 } 3544 } 3545 } 3546 3547 public void dumpOverlapProblems(Multimap<byte[], HbckInfo> regions) { 3548 // we display this way because the last end key should be displayed as 3549 // well. 3550 for (byte[] k : regions.keySet()) { 3551 errors.print(Bytes.toStringBinary(k) + ":"); 3552 for (HbckInfo r : regions.get(k)) { 3553 errors.print("[ " + r.toString() + ", " 3554 + Bytes.toStringBinary(r.getEndKey()) + "]"); 3555 } 3556 errors.print("----"); 3557 } 3558 } 3559 3560 public void dumpSidelinedRegions(Map<Path, HbckInfo> regions) { 3561 for (Map.Entry<Path, HbckInfo> entry: regions.entrySet()) { 3562 TableName tableName = entry.getValue().getTableName(); 3563 Path path = entry.getKey(); 3564 errors.print("This sidelined region dir should be bulk loaded: " 3565 + path.toString()); 3566 errors.print("Bulk load command looks like: " 3567 + "hbase org.apache.hadoop.hbase.tool.LoadIncrementalHFiles " 3568 + path.toUri().getPath() + " "+ tableName); 3569 } 3570 } 3571 3572 public Multimap<byte[], HbckInfo> getOverlapGroups( 3573 TableName table) { 3574 TableInfo ti = tablesInfo.get(table); 3575 return ti.overlapGroups; 3576 } 3577 3578 /** 3579 * Return a list of user-space table names whose metadata have not been 3580 * modified in the last few milliseconds specified by timelag 3581 * if any of the REGIONINFO_QUALIFIER, SERVER_QUALIFIER, STARTCODE_QUALIFIER, 3582 * SPLITA_QUALIFIER, SPLITB_QUALIFIER have not changed in the last 3583 * milliseconds specified by timelag, then the table is a candidate to be returned. 3584 * @return tables that have not been modified recently 3585 * @throws IOException if an error is encountered 3586 */ 3587 TableDescriptor[] getTables(AtomicInteger numSkipped) { 3588 List<TableName> tableNames = new ArrayList<>(); 3589 long now = EnvironmentEdgeManager.currentTime(); 3590 3591 for (HbckInfo hbi : regionInfoMap.values()) { 3592 MetaEntry info = hbi.metaEntry; 3593 3594 // if the start key is zero, then we have found the first region of a table. 3595 // pick only those tables that were not modified in the last few milliseconds. 3596 if (info != null && info.getStartKey().length == 0 && !info.isMetaRegion()) { 3597 if (info.modTime + timelag < now) { 3598 tableNames.add(info.getTable()); 3599 } else { 3600 numSkipped.incrementAndGet(); // one more in-flux table 3601 } 3602 } 3603 } 3604 return getTableDescriptors(tableNames); 3605 } 3606 3607 TableDescriptor[] getTableDescriptors(List<TableName> tableNames) { 3608 LOG.info("getTableDescriptors == tableNames => " + tableNames); 3609 try (Connection conn = ConnectionFactory.createConnection(getConf()); 3610 Admin admin = conn.getAdmin()) { 3611 List<TableDescriptor> tds = admin.listTableDescriptors(tableNames); 3612 return tds.toArray(new TableDescriptor[tds.size()]); 3613 } catch (IOException e) { 3614 LOG.debug("Exception getting table descriptors", e); 3615 } 3616 return new TableDescriptor[0]; 3617 } 3618 3619 /** 3620 * Gets the entry in regionInfo corresponding to the the given encoded 3621 * region name. If the region has not been seen yet, a new entry is added 3622 * and returned. 3623 */ 3624 private synchronized HbckInfo getOrCreateInfo(String name) { 3625 HbckInfo hbi = regionInfoMap.get(name); 3626 if (hbi == null) { 3627 hbi = new HbckInfo(null); 3628 regionInfoMap.put(name, hbi); 3629 } 3630 return hbi; 3631 } 3632 3633 private void checkAndFixReplication() throws IOException { 3634 ReplicationChecker checker = new ReplicationChecker(getConf(), zkw, connection, errors); 3635 checker.checkUnDeletedQueues(); 3636 3637 if (checker.hasUnDeletedQueues() && this.fixReplication) { 3638 checker.fixUnDeletedQueues(); 3639 setShouldRerun(); 3640 } 3641 } 3642 3643 /** 3644 * Check values in regionInfo for hbase:meta 3645 * Check if zero or more than one regions with hbase:meta are found. 3646 * If there are inconsistencies (i.e. zero or more than one regions 3647 * pretend to be holding the hbase:meta) try to fix that and report an error. 3648 * @throws IOException from HBaseFsckRepair functions 3649 * @throws KeeperException 3650 * @throws InterruptedException 3651 */ 3652 boolean checkMetaRegion() throws IOException, KeeperException, InterruptedException { 3653 Map<Integer, HbckInfo> metaRegions = new HashMap<>(); 3654 for (HbckInfo value : regionInfoMap.values()) { 3655 if (value.metaEntry != null && value.metaEntry.isMetaRegion()) { 3656 metaRegions.put(value.getReplicaId(), value); 3657 } 3658 } 3659 int metaReplication = admin.getTableDescriptor(TableName.META_TABLE_NAME) 3660 .getRegionReplication(); 3661 boolean noProblem = true; 3662 // There will be always entries in regionInfoMap corresponding to hbase:meta & its replicas 3663 // Check the deployed servers. It should be exactly one server for each replica. 3664 for (int i = 0; i < metaReplication; i++) { 3665 HbckInfo metaHbckInfo = metaRegions.remove(i); 3666 List<ServerName> servers = new ArrayList<>(); 3667 if (metaHbckInfo != null) { 3668 servers = metaHbckInfo.deployedOn; 3669 } 3670 if (servers.size() != 1) { 3671 noProblem = false; 3672 if (servers.isEmpty()) { 3673 assignMetaReplica(i); 3674 } else if (servers.size() > 1) { 3675 errors 3676 .reportError(ERROR_CODE.MULTI_META_REGION, "hbase:meta, replicaId " + 3677 metaHbckInfo.getReplicaId() + " is found on more than one region."); 3678 if (shouldFixAssignments()) { 3679 errors.print("Trying to fix a problem with hbase:meta, replicaId " + 3680 metaHbckInfo.getReplicaId() +".."); 3681 setShouldRerun(); 3682 // try fix it (treat is a dupe assignment) 3683 HBaseFsckRepair.fixMultiAssignment(connection, metaHbckInfo.metaEntry, servers); 3684 } 3685 } 3686 } 3687 } 3688 // unassign whatever is remaining in metaRegions. They are excess replicas. 3689 for (Map.Entry<Integer, HbckInfo> entry : metaRegions.entrySet()) { 3690 noProblem = false; 3691 errors.reportError(ERROR_CODE.SHOULD_NOT_BE_DEPLOYED, 3692 "hbase:meta replicas are deployed in excess. Configured " + metaReplication + 3693 ", deployed " + metaRegions.size()); 3694 if (shouldFixAssignments()) { 3695 errors.print("Trying to undeploy excess replica, replicaId: " + entry.getKey() + 3696 " of hbase:meta.."); 3697 setShouldRerun(); 3698 unassignMetaReplica(entry.getValue()); 3699 } 3700 } 3701 // if noProblem is false, rerun hbck with hopefully fixed META 3702 // if noProblem is true, no errors, so continue normally 3703 return noProblem; 3704 } 3705 3706 private void unassignMetaReplica(HbckInfo hi) throws IOException, InterruptedException, 3707 KeeperException { 3708 undeployRegions(hi); 3709 ZKUtil.deleteNode(zkw, zkw.znodePaths.getZNodeForReplica(hi.metaEntry.getReplicaId())); 3710 } 3711 3712 private void assignMetaReplica(int replicaId) 3713 throws IOException, KeeperException, InterruptedException { 3714 errors.reportError(ERROR_CODE.NO_META_REGION, "hbase:meta, replicaId " + 3715 replicaId +" is not found on any region."); 3716 if (shouldFixAssignments()) { 3717 errors.print("Trying to fix a problem with hbase:meta.."); 3718 setShouldRerun(); 3719 // try to fix it (treat it as unassigned region) 3720 RegionInfo h = RegionReplicaUtil.getRegionInfoForReplica( 3721 RegionInfoBuilder.FIRST_META_REGIONINFO, replicaId); 3722 HBaseFsckRepair.fixUnassigned(admin, h); 3723 HBaseFsckRepair.waitUntilAssigned(admin, h); 3724 } 3725 } 3726 3727 /** 3728 * Scan hbase:meta, adding all regions found to the regionInfo map. 3729 * @throws IOException if an error is encountered 3730 */ 3731 boolean loadMetaEntries() throws IOException { 3732 MetaTableAccessor.Visitor visitor = new MetaTableAccessor.Visitor() { 3733 int countRecord = 1; 3734 3735 // comparator to sort KeyValues with latest modtime 3736 final Comparator<Cell> comp = new Comparator<Cell>() { 3737 @Override 3738 public int compare(Cell k1, Cell k2) { 3739 return Long.compare(k1.getTimestamp(), k2.getTimestamp()); 3740 } 3741 }; 3742 3743 @Override 3744 public boolean visit(Result result) throws IOException { 3745 try { 3746 3747 // record the latest modification of this META record 3748 long ts = Collections.max(result.listCells(), comp).getTimestamp(); 3749 RegionLocations rl = MetaTableAccessor.getRegionLocations(result); 3750 if (rl == null) { 3751 emptyRegionInfoQualifiers.add(result); 3752 errors.reportError(ERROR_CODE.EMPTY_META_CELL, 3753 "Empty REGIONINFO_QUALIFIER found in hbase:meta"); 3754 return true; 3755 } 3756 ServerName sn = null; 3757 if (rl.getRegionLocation(RegionInfo.DEFAULT_REPLICA_ID) == null || 3758 rl.getRegionLocation(RegionInfo.DEFAULT_REPLICA_ID).getRegionInfo() == null) { 3759 emptyRegionInfoQualifiers.add(result); 3760 errors.reportError(ERROR_CODE.EMPTY_META_CELL, 3761 "Empty REGIONINFO_QUALIFIER found in hbase:meta"); 3762 return true; 3763 } 3764 RegionInfo hri = rl.getRegionLocation(RegionInfo.DEFAULT_REPLICA_ID).getRegionInfo(); 3765 if (!(isTableIncluded(hri.getTable()) 3766 || hri.isMetaRegion())) { 3767 return true; 3768 } 3769 PairOfSameType<RegionInfo> daughters = MetaTableAccessor.getDaughterRegions(result); 3770 for (HRegionLocation h : rl.getRegionLocations()) { 3771 if (h == null || h.getRegionInfo() == null) { 3772 continue; 3773 } 3774 sn = h.getServerName(); 3775 hri = h.getRegionInfo(); 3776 3777 MetaEntry m = null; 3778 if (hri.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) { 3779 m = new MetaEntry(hri, sn, ts, daughters.getFirst(), daughters.getSecond()); 3780 } else { 3781 m = new MetaEntry(hri, sn, ts, null, null); 3782 } 3783 HbckInfo previous = regionInfoMap.get(hri.getEncodedName()); 3784 if (previous == null) { 3785 regionInfoMap.put(hri.getEncodedName(), new HbckInfo(m)); 3786 } else if (previous.metaEntry == null) { 3787 previous.metaEntry = m; 3788 } else { 3789 throw new IOException("Two entries in hbase:meta are same " + previous); 3790 } 3791 } 3792 PairOfSameType<RegionInfo> mergeRegions = MetaTableAccessor.getMergeRegions(result); 3793 for (RegionInfo mergeRegion : new RegionInfo[] { 3794 mergeRegions.getFirst(), mergeRegions.getSecond() }) { 3795 if (mergeRegion != null) { 3796 // This region is already been merged 3797 HbckInfo hbInfo = getOrCreateInfo(mergeRegion.getEncodedName()); 3798 hbInfo.setMerged(true); 3799 } 3800 } 3801 3802 // show proof of progress to the user, once for every 100 records. 3803 if (countRecord % 100 == 0) { 3804 errors.progress(); 3805 } 3806 countRecord++; 3807 return true; 3808 } catch (RuntimeException e) { 3809 LOG.error("Result=" + result); 3810 throw e; 3811 } 3812 } 3813 }; 3814 if (!checkMetaOnly) { 3815 // Scan hbase:meta to pick up user regions 3816 MetaTableAccessor.fullScanRegions(connection, visitor); 3817 } 3818 3819 errors.print(""); 3820 return true; 3821 } 3822 3823 /** 3824 * Stores the regioninfo entries scanned from META 3825 */ 3826 static class MetaEntry extends HRegionInfo { 3827 ServerName regionServer; // server hosting this region 3828 long modTime; // timestamp of most recent modification metadata 3829 RegionInfo splitA, splitB; //split daughters 3830 3831 public MetaEntry(RegionInfo rinfo, ServerName regionServer, long modTime) { 3832 this(rinfo, regionServer, modTime, null, null); 3833 } 3834 3835 public MetaEntry(RegionInfo rinfo, ServerName regionServer, long modTime, 3836 RegionInfo splitA, RegionInfo splitB) { 3837 super(rinfo); 3838 this.regionServer = regionServer; 3839 this.modTime = modTime; 3840 this.splitA = splitA; 3841 this.splitB = splitB; 3842 } 3843 3844 @Override 3845 public boolean equals(Object o) { 3846 boolean superEq = super.equals(o); 3847 if (!superEq) { 3848 return superEq; 3849 } 3850 3851 MetaEntry me = (MetaEntry) o; 3852 if (!regionServer.equals(me.regionServer)) { 3853 return false; 3854 } 3855 return (modTime == me.modTime); 3856 } 3857 3858 @Override 3859 public int hashCode() { 3860 int hash = Arrays.hashCode(getRegionName()); 3861 hash = (int) (hash ^ getRegionId()); 3862 hash ^= Arrays.hashCode(getStartKey()); 3863 hash ^= Arrays.hashCode(getEndKey()); 3864 hash ^= Boolean.valueOf(isOffline()).hashCode(); 3865 hash ^= getTable().hashCode(); 3866 if (regionServer != null) { 3867 hash ^= regionServer.hashCode(); 3868 } 3869 hash = (int) (hash ^ modTime); 3870 return hash; 3871 } 3872 } 3873 3874 /** 3875 * Stores the regioninfo entries from HDFS 3876 */ 3877 static class HdfsEntry { 3878 RegionInfo hri; 3879 Path hdfsRegionDir = null; 3880 long hdfsRegionDirModTime = 0; 3881 boolean hdfsRegioninfoFilePresent = false; 3882 boolean hdfsOnlyEdits = false; 3883 } 3884 3885 /** 3886 * Stores the regioninfo retrieved from Online region servers. 3887 */ 3888 static class OnlineEntry { 3889 RegionInfo hri; 3890 ServerName hsa; 3891 3892 @Override 3893 public String toString() { 3894 return hsa.toString() + ";" + hri.getRegionNameAsString(); 3895 } 3896 } 3897 3898 /** 3899 * Maintain information about a particular region. It gathers information 3900 * from three places -- HDFS, META, and region servers. 3901 */ 3902 public static class HbckInfo implements KeyRange { 3903 private MetaEntry metaEntry = null; // info in META 3904 private HdfsEntry hdfsEntry = null; // info in HDFS 3905 private List<OnlineEntry> deployedEntries = Lists.newArrayList(); // on Region Server 3906 private List<ServerName> deployedOn = Lists.newArrayList(); // info on RS's 3907 private boolean skipChecks = false; // whether to skip further checks to this region info. 3908 private boolean isMerged = false;// whether this region has already been merged into another one 3909 private int deployedReplicaId = RegionInfo.DEFAULT_REPLICA_ID; 3910 private RegionInfo primaryHRIForDeployedReplica = null; 3911 3912 HbckInfo(MetaEntry metaEntry) { 3913 this.metaEntry = metaEntry; 3914 } 3915 3916 public synchronized int getReplicaId() { 3917 return metaEntry != null? metaEntry.getReplicaId(): deployedReplicaId; 3918 } 3919 3920 public synchronized void addServer(RegionInfo hri, ServerName server) { 3921 OnlineEntry rse = new OnlineEntry() ; 3922 rse.hri = hri; 3923 rse.hsa = server; 3924 this.deployedEntries.add(rse); 3925 this.deployedOn.add(server); 3926 // save the replicaId that we see deployed in the cluster 3927 this.deployedReplicaId = hri.getReplicaId(); 3928 this.primaryHRIForDeployedReplica = 3929 RegionReplicaUtil.getRegionInfoForDefaultReplica(hri); 3930 } 3931 3932 @Override 3933 public synchronized String toString() { 3934 StringBuilder sb = new StringBuilder(); 3935 sb.append("{ meta => "); 3936 sb.append((metaEntry != null)? metaEntry.getRegionNameAsString() : "null"); 3937 sb.append( ", hdfs => " + getHdfsRegionDir()); 3938 sb.append( ", deployed => " + Joiner.on(", ").join(deployedEntries)); 3939 sb.append( ", replicaId => " + getReplicaId()); 3940 sb.append(" }"); 3941 return sb.toString(); 3942 } 3943 3944 @Override 3945 public byte[] getStartKey() { 3946 if (this.metaEntry != null) { 3947 return this.metaEntry.getStartKey(); 3948 } else if (this.hdfsEntry != null) { 3949 return this.hdfsEntry.hri.getStartKey(); 3950 } else { 3951 LOG.error("Entry " + this + " has no meta or hdfs region start key."); 3952 return null; 3953 } 3954 } 3955 3956 @Override 3957 public byte[] getEndKey() { 3958 if (this.metaEntry != null) { 3959 return this.metaEntry.getEndKey(); 3960 } else if (this.hdfsEntry != null) { 3961 return this.hdfsEntry.hri.getEndKey(); 3962 } else { 3963 LOG.error("Entry " + this + " has no meta or hdfs region start key."); 3964 return null; 3965 } 3966 } 3967 3968 public TableName getTableName() { 3969 if (this.metaEntry != null) { 3970 return this.metaEntry.getTable(); 3971 } else if (this.hdfsEntry != null) { 3972 // we are only guaranteed to have a path and not an HRI for hdfsEntry, 3973 // so we get the name from the Path 3974 Path tableDir = this.hdfsEntry.hdfsRegionDir.getParent(); 3975 return FSUtils.getTableName(tableDir); 3976 } else { 3977 // return the info from the first online/deployed hri 3978 for (OnlineEntry e : deployedEntries) { 3979 return e.hri.getTable(); 3980 } 3981 return null; 3982 } 3983 } 3984 3985 public String getRegionNameAsString() { 3986 if (metaEntry != null) { 3987 return metaEntry.getRegionNameAsString(); 3988 } else if (hdfsEntry != null) { 3989 if (hdfsEntry.hri != null) { 3990 return hdfsEntry.hri.getRegionNameAsString(); 3991 } 3992 } else { 3993 // return the info from the first online/deployed hri 3994 for (OnlineEntry e : deployedEntries) { 3995 return e.hri.getRegionNameAsString(); 3996 } 3997 } 3998 return null; 3999 } 4000 4001 public byte[] getRegionName() { 4002 if (metaEntry != null) { 4003 return metaEntry.getRegionName(); 4004 } else if (hdfsEntry != null) { 4005 return hdfsEntry.hri.getRegionName(); 4006 } else { 4007 // return the info from the first online/deployed hri 4008 for (OnlineEntry e : deployedEntries) { 4009 return e.hri.getRegionName(); 4010 } 4011 return null; 4012 } 4013 } 4014 4015 public RegionInfo getPrimaryHRIForDeployedReplica() { 4016 return primaryHRIForDeployedReplica; 4017 } 4018 4019 Path getHdfsRegionDir() { 4020 if (hdfsEntry == null) { 4021 return null; 4022 } 4023 return hdfsEntry.hdfsRegionDir; 4024 } 4025 4026 boolean containsOnlyHdfsEdits() { 4027 if (hdfsEntry == null) { 4028 return false; 4029 } 4030 return hdfsEntry.hdfsOnlyEdits; 4031 } 4032 4033 boolean isHdfsRegioninfoPresent() { 4034 if (hdfsEntry == null) { 4035 return false; 4036 } 4037 return hdfsEntry.hdfsRegioninfoFilePresent; 4038 } 4039 4040 long getModTime() { 4041 if (hdfsEntry == null) { 4042 return 0; 4043 } 4044 return hdfsEntry.hdfsRegionDirModTime; 4045 } 4046 4047 RegionInfo getHdfsHRI() { 4048 if (hdfsEntry == null) { 4049 return null; 4050 } 4051 return hdfsEntry.hri; 4052 } 4053 4054 public void setSkipChecks(boolean skipChecks) { 4055 this.skipChecks = skipChecks; 4056 } 4057 4058 public boolean isSkipChecks() { 4059 return skipChecks; 4060 } 4061 4062 public void setMerged(boolean isMerged) { 4063 this.isMerged = isMerged; 4064 } 4065 4066 public boolean isMerged() { 4067 return this.isMerged; 4068 } 4069 } 4070 4071 final static Comparator<HbckInfo> cmp = new Comparator<HbckInfo>() { 4072 @Override 4073 public int compare(HbckInfo l, HbckInfo r) { 4074 if (l == r) { 4075 // same instance 4076 return 0; 4077 } 4078 4079 int tableCompare = l.getTableName().compareTo(r.getTableName()); 4080 if (tableCompare != 0) { 4081 return tableCompare; 4082 } 4083 4084 int startComparison = RegionSplitCalculator.BYTES_COMPARATOR.compare( 4085 l.getStartKey(), r.getStartKey()); 4086 if (startComparison != 0) { 4087 return startComparison; 4088 } 4089 4090 // Special case for absolute endkey 4091 byte[] endKey = r.getEndKey(); 4092 endKey = (endKey.length == 0) ? null : endKey; 4093 byte[] endKey2 = l.getEndKey(); 4094 endKey2 = (endKey2.length == 0) ? null : endKey2; 4095 int endComparison = RegionSplitCalculator.BYTES_COMPARATOR.compare( 4096 endKey2, endKey); 4097 4098 if (endComparison != 0) { 4099 return endComparison; 4100 } 4101 4102 // use regionId as tiebreaker. 4103 // Null is considered after all possible values so make it bigger. 4104 if (l.hdfsEntry == null && r.hdfsEntry == null) { 4105 return 0; 4106 } 4107 if (l.hdfsEntry == null && r.hdfsEntry != null) { 4108 return 1; 4109 } 4110 // l.hdfsEntry must not be null 4111 if (r.hdfsEntry == null) { 4112 return -1; 4113 } 4114 // both l.hdfsEntry and r.hdfsEntry must not be null. 4115 return Long.compare(l.hdfsEntry.hri.getRegionId(), r.hdfsEntry.hri.getRegionId()); 4116 } 4117 }; 4118 4119 /** 4120 * Prints summary of all tables found on the system. 4121 */ 4122 private void printTableSummary(SortedMap<TableName, TableInfo> tablesInfo) { 4123 StringBuilder sb = new StringBuilder(); 4124 int numOfSkippedRegions; 4125 errors.print("Summary:"); 4126 for (TableInfo tInfo : tablesInfo.values()) { 4127 numOfSkippedRegions = (skippedRegions.containsKey(tInfo.getName())) ? 4128 skippedRegions.get(tInfo.getName()).size() : 0; 4129 4130 if (errors.tableHasErrors(tInfo)) { 4131 errors.print("Table " + tInfo.getName() + " is inconsistent."); 4132 } else if (numOfSkippedRegions > 0){ 4133 errors.print("Table " + tInfo.getName() + " is okay (with " 4134 + numOfSkippedRegions + " skipped regions)."); 4135 } 4136 else { 4137 errors.print("Table " + tInfo.getName() + " is okay."); 4138 } 4139 errors.print(" Number of regions: " + tInfo.getNumRegions()); 4140 if (numOfSkippedRegions > 0) { 4141 Set<String> skippedRegionStrings = skippedRegions.get(tInfo.getName()); 4142 System.out.println(" Number of skipped regions: " + numOfSkippedRegions); 4143 System.out.println(" List of skipped regions:"); 4144 for(String sr : skippedRegionStrings) { 4145 System.out.println(" " + sr); 4146 } 4147 } 4148 sb.setLength(0); // clear out existing buffer, if any. 4149 sb.append(" Deployed on: "); 4150 for (ServerName server : tInfo.deployedOn) { 4151 sb.append(" " + server.toString()); 4152 } 4153 errors.print(sb.toString()); 4154 } 4155 } 4156 4157 static ErrorReporter getErrorReporter( 4158 final Configuration conf) throws ClassNotFoundException { 4159 Class<? extends ErrorReporter> reporter = conf.getClass("hbasefsck.errorreporter", PrintingErrorReporter.class, ErrorReporter.class); 4160 return ReflectionUtils.newInstance(reporter, conf); 4161 } 4162 4163 public interface ErrorReporter { 4164 enum ERROR_CODE { 4165 UNKNOWN, NO_META_REGION, NULL_META_REGION, NO_VERSION_FILE, NOT_IN_META_HDFS, NOT_IN_META, 4166 NOT_IN_META_OR_DEPLOYED, NOT_IN_HDFS_OR_DEPLOYED, NOT_IN_HDFS, SERVER_DOES_NOT_MATCH_META, 4167 NOT_DEPLOYED, 4168 MULTI_DEPLOYED, SHOULD_NOT_BE_DEPLOYED, MULTI_META_REGION, RS_CONNECT_FAILURE, 4169 FIRST_REGION_STARTKEY_NOT_EMPTY, LAST_REGION_ENDKEY_NOT_EMPTY, DUPE_STARTKEYS, 4170 HOLE_IN_REGION_CHAIN, OVERLAP_IN_REGION_CHAIN, REGION_CYCLE, DEGENERATE_REGION, 4171 ORPHAN_HDFS_REGION, LINGERING_SPLIT_PARENT, NO_TABLEINFO_FILE, LINGERING_REFERENCE_HFILE, 4172 LINGERING_HFILELINK, WRONG_USAGE, EMPTY_META_CELL, EXPIRED_TABLE_LOCK, BOUNDARIES_ERROR, 4173 ORPHAN_TABLE_STATE, NO_TABLE_STATE, UNDELETED_REPLICATION_QUEUE, DUPE_ENDKEYS, 4174 UNSUPPORTED_OPTION 4175 } 4176 void clear(); 4177 void report(String message); 4178 void reportError(String message); 4179 void reportError(ERROR_CODE errorCode, String message); 4180 void reportError(ERROR_CODE errorCode, String message, TableInfo table); 4181 void reportError(ERROR_CODE errorCode, String message, TableInfo table, HbckInfo info); 4182 void reportError( 4183 ERROR_CODE errorCode, 4184 String message, 4185 TableInfo table, 4186 HbckInfo info1, 4187 HbckInfo info2 4188 ); 4189 int summarize(); 4190 void detail(String details); 4191 ArrayList<ERROR_CODE> getErrorList(); 4192 void progress(); 4193 void print(String message); 4194 void resetErrors(); 4195 boolean tableHasErrors(TableInfo table); 4196 } 4197 4198 static class PrintingErrorReporter implements ErrorReporter { 4199 public int errorCount = 0; 4200 private int showProgress; 4201 // How frequently calls to progress() will create output 4202 private static final int progressThreshold = 100; 4203 4204 Set<TableInfo> errorTables = new HashSet<>(); 4205 4206 // for use by unit tests to verify which errors were discovered 4207 private ArrayList<ERROR_CODE> errorList = new ArrayList<>(); 4208 4209 @Override 4210 public void clear() { 4211 errorTables.clear(); 4212 errorList.clear(); 4213 errorCount = 0; 4214 } 4215 4216 @Override 4217 public synchronized void reportError(ERROR_CODE errorCode, String message) { 4218 if (errorCode == ERROR_CODE.WRONG_USAGE) { 4219 System.err.println(message); 4220 return; 4221 } 4222 4223 errorList.add(errorCode); 4224 if (!summary) { 4225 System.out.println("ERROR: " + message); 4226 } 4227 errorCount++; 4228 showProgress = 0; 4229 } 4230 4231 @Override 4232 public synchronized void reportError(ERROR_CODE errorCode, String message, TableInfo table) { 4233 errorTables.add(table); 4234 reportError(errorCode, message); 4235 } 4236 4237 @Override 4238 public synchronized void reportError(ERROR_CODE errorCode, String message, TableInfo table, 4239 HbckInfo info) { 4240 errorTables.add(table); 4241 String reference = "(region " + info.getRegionNameAsString() + ")"; 4242 reportError(errorCode, reference + " " + message); 4243 } 4244 4245 @Override 4246 public synchronized void reportError(ERROR_CODE errorCode, String message, TableInfo table, 4247 HbckInfo info1, HbckInfo info2) { 4248 errorTables.add(table); 4249 String reference = "(regions " + info1.getRegionNameAsString() 4250 + " and " + info2.getRegionNameAsString() + ")"; 4251 reportError(errorCode, reference + " " + message); 4252 } 4253 4254 @Override 4255 public synchronized void reportError(String message) { 4256 reportError(ERROR_CODE.UNKNOWN, message); 4257 } 4258 4259 /** 4260 * Report error information, but do not increment the error count. Intended for cases 4261 * where the actual error would have been reported previously. 4262 * @param message 4263 */ 4264 @Override 4265 public synchronized void report(String message) { 4266 if (! summary) { 4267 System.out.println("ERROR: " + message); 4268 } 4269 showProgress = 0; 4270 } 4271 4272 @Override 4273 public synchronized int summarize() { 4274 System.out.println(Integer.toString(errorCount) + 4275 " inconsistencies detected."); 4276 if (errorCount == 0) { 4277 System.out.println("Status: OK"); 4278 return 0; 4279 } else { 4280 System.out.println("Status: INCONSISTENT"); 4281 return -1; 4282 } 4283 } 4284 4285 @Override 4286 public ArrayList<ERROR_CODE> getErrorList() { 4287 return errorList; 4288 } 4289 4290 @Override 4291 public synchronized void print(String message) { 4292 if (!summary) { 4293 System.out.println(message); 4294 } 4295 } 4296 4297 @Override 4298 public boolean tableHasErrors(TableInfo table) { 4299 return errorTables.contains(table); 4300 } 4301 4302 @Override 4303 public void resetErrors() { 4304 errorCount = 0; 4305 } 4306 4307 @Override 4308 public synchronized void detail(String message) { 4309 if (details) { 4310 System.out.println(message); 4311 } 4312 showProgress = 0; 4313 } 4314 4315 @Override 4316 public synchronized void progress() { 4317 if (showProgress++ == progressThreshold) { 4318 if (!summary) { 4319 System.out.print("."); 4320 } 4321 showProgress = 0; 4322 } 4323 } 4324 } 4325 4326 /** 4327 * Contact a region server and get all information from it 4328 */ 4329 static class WorkItemRegion implements Callable<Void> { 4330 private final HBaseFsck hbck; 4331 private final ServerName rsinfo; 4332 private final ErrorReporter errors; 4333 private final ClusterConnection connection; 4334 4335 WorkItemRegion(HBaseFsck hbck, ServerName info, 4336 ErrorReporter errors, ClusterConnection connection) { 4337 this.hbck = hbck; 4338 this.rsinfo = info; 4339 this.errors = errors; 4340 this.connection = connection; 4341 } 4342 4343 @Override 4344 public synchronized Void call() throws IOException { 4345 errors.progress(); 4346 try { 4347 BlockingInterface server = connection.getAdmin(rsinfo); 4348 4349 // list all online regions from this region server 4350 List<RegionInfo> regions = ProtobufUtil.getOnlineRegions(server); 4351 regions = filterRegions(regions); 4352 4353 if (details) { 4354 errors.detail("RegionServer: " + rsinfo.getServerName() + 4355 " number of regions: " + regions.size()); 4356 for (RegionInfo rinfo: regions) { 4357 errors.detail(" " + rinfo.getRegionNameAsString() + 4358 " id: " + rinfo.getRegionId() + 4359 " encoded_name: " + rinfo.getEncodedName() + 4360 " start: " + Bytes.toStringBinary(rinfo.getStartKey()) + 4361 " end: " + Bytes.toStringBinary(rinfo.getEndKey())); 4362 } 4363 } 4364 4365 // check to see if the existence of this region matches the region in META 4366 for (RegionInfo r:regions) { 4367 HbckInfo hbi = hbck.getOrCreateInfo(r.getEncodedName()); 4368 hbi.addServer(r, rsinfo); 4369 } 4370 } catch (IOException e) { // unable to connect to the region server. 4371 errors.reportError(ERROR_CODE.RS_CONNECT_FAILURE, "RegionServer: " + rsinfo.getServerName() + 4372 " Unable to fetch region information. " + e); 4373 throw e; 4374 } 4375 return null; 4376 } 4377 4378 private List<RegionInfo> filterRegions(List<RegionInfo> regions) { 4379 List<RegionInfo> ret = Lists.newArrayList(); 4380 for (RegionInfo hri : regions) { 4381 if (hri.isMetaRegion() || (!hbck.checkMetaOnly 4382 && hbck.isTableIncluded(hri.getTable()))) { 4383 ret.add(hri); 4384 } 4385 } 4386 return ret; 4387 } 4388 } 4389 4390 /** 4391 * Contact hdfs and get all information about specified table directory into 4392 * regioninfo list. 4393 */ 4394 class WorkItemHdfsDir implements Callable<Void> { 4395 private FileStatus tableDir; 4396 private ErrorReporter errors; 4397 private FileSystem fs; 4398 4399 WorkItemHdfsDir(FileSystem fs, ErrorReporter errors, 4400 FileStatus status) { 4401 this.fs = fs; 4402 this.tableDir = status; 4403 this.errors = errors; 4404 } 4405 4406 @Override 4407 public synchronized Void call() throws InterruptedException, ExecutionException { 4408 final Vector<Exception> exceptions = new Vector<>(); 4409 4410 try { 4411 final FileStatus[] regionDirs = fs.listStatus(tableDir.getPath()); 4412 final List<Future<?>> futures = new ArrayList<>(regionDirs.length); 4413 4414 for (final FileStatus regionDir : regionDirs) { 4415 errors.progress(); 4416 final String encodedName = regionDir.getPath().getName(); 4417 // ignore directories that aren't hexadecimal 4418 if (!encodedName.toLowerCase(Locale.ROOT).matches("[0-9a-f]+")) { 4419 continue; 4420 } 4421 4422 if (!exceptions.isEmpty()) { 4423 break; 4424 } 4425 4426 futures.add(executor.submit(new Runnable() { 4427 @Override 4428 public void run() { 4429 try { 4430 LOG.debug("Loading region info from hdfs:"+ regionDir.getPath()); 4431 4432 Path regioninfoFile = new Path(regionDir.getPath(), HRegionFileSystem.REGION_INFO_FILE); 4433 boolean regioninfoFileExists = fs.exists(regioninfoFile); 4434 4435 if (!regioninfoFileExists) { 4436 // As tables become larger it is more and more likely that by the time you 4437 // reach a given region that it will be gone due to region splits/merges. 4438 if (!fs.exists(regionDir.getPath())) { 4439 LOG.warn("By the time we tried to process this region dir it was already gone: " 4440 + regionDir.getPath()); 4441 return; 4442 } 4443 } 4444 4445 HbckInfo hbi = HBaseFsck.this.getOrCreateInfo(encodedName); 4446 HdfsEntry he = new HdfsEntry(); 4447 synchronized (hbi) { 4448 if (hbi.getHdfsRegionDir() != null) { 4449 errors.print("Directory " + encodedName + " duplicate??" + 4450 hbi.getHdfsRegionDir()); 4451 } 4452 4453 he.hdfsRegionDir = regionDir.getPath(); 4454 he.hdfsRegionDirModTime = regionDir.getModificationTime(); 4455 he.hdfsRegioninfoFilePresent = regioninfoFileExists; 4456 // we add to orphan list when we attempt to read .regioninfo 4457 4458 // Set a flag if this region contains only edits 4459 // This is special case if a region is left after split 4460 he.hdfsOnlyEdits = true; 4461 FileStatus[] subDirs = fs.listStatus(regionDir.getPath()); 4462 Path ePath = WALSplitter.getRegionDirRecoveredEditsDir(regionDir.getPath()); 4463 for (FileStatus subDir : subDirs) { 4464 errors.progress(); 4465 String sdName = subDir.getPath().getName(); 4466 if (!sdName.startsWith(".") && !sdName.equals(ePath.getName())) { 4467 he.hdfsOnlyEdits = false; 4468 break; 4469 } 4470 } 4471 hbi.hdfsEntry = he; 4472 } 4473 } catch (Exception e) { 4474 LOG.error("Could not load region dir", e); 4475 exceptions.add(e); 4476 } 4477 } 4478 })); 4479 } 4480 4481 // Ensure all pending tasks are complete (or that we run into an exception) 4482 for (Future<?> f : futures) { 4483 if (!exceptions.isEmpty()) { 4484 break; 4485 } 4486 try { 4487 f.get(); 4488 } catch (ExecutionException e) { 4489 LOG.error("Unexpected exec exception! Should've been caught already. (Bug?)", e); 4490 // Shouldn't happen, we already logged/caught any exceptions in the Runnable 4491 }; 4492 } 4493 } catch (IOException e) { 4494 LOG.error("Cannot execute WorkItemHdfsDir for " + tableDir, e); 4495 exceptions.add(e); 4496 } finally { 4497 if (!exceptions.isEmpty()) { 4498 errors.reportError(ERROR_CODE.RS_CONNECT_FAILURE, "Table Directory: " 4499 + tableDir.getPath().getName() 4500 + " Unable to fetch all HDFS region information. "); 4501 // Just throw the first exception as an indication something bad happened 4502 // Don't need to propagate all the exceptions, we already logged them all anyway 4503 throw new ExecutionException("First exception in WorkItemHdfsDir", exceptions.firstElement()); 4504 } 4505 } 4506 return null; 4507 } 4508 } 4509 4510 /** 4511 * Contact hdfs and get all information about specified table directory into 4512 * regioninfo list. 4513 */ 4514 static class WorkItemHdfsRegionInfo implements Callable<Void> { 4515 private HbckInfo hbi; 4516 private HBaseFsck hbck; 4517 private ErrorReporter errors; 4518 4519 WorkItemHdfsRegionInfo(HbckInfo hbi, HBaseFsck hbck, ErrorReporter errors) { 4520 this.hbi = hbi; 4521 this.hbck = hbck; 4522 this.errors = errors; 4523 } 4524 4525 @Override 4526 public synchronized Void call() throws IOException { 4527 // only load entries that haven't been loaded yet. 4528 if (hbi.getHdfsHRI() == null) { 4529 try { 4530 errors.progress(); 4531 hbck.loadHdfsRegioninfo(hbi); 4532 } catch (IOException ioe) { 4533 String msg = "Orphan region in HDFS: Unable to load .regioninfo from table " 4534 + hbi.getTableName() + " in hdfs dir " 4535 + hbi.getHdfsRegionDir() 4536 + "! It may be an invalid format or version file. Treating as " 4537 + "an orphaned regiondir."; 4538 errors.reportError(ERROR_CODE.ORPHAN_HDFS_REGION, msg); 4539 try { 4540 hbck.debugLsr(hbi.getHdfsRegionDir()); 4541 } catch (IOException ioe2) { 4542 LOG.error("Unable to read directory " + hbi.getHdfsRegionDir(), ioe2); 4543 throw ioe2; 4544 } 4545 hbck.orphanHdfsDirs.add(hbi); 4546 throw ioe; 4547 } 4548 } 4549 return null; 4550 } 4551 }; 4552 4553 /** 4554 * Display the full report from fsck. This displays all live and dead region 4555 * servers, and all known regions. 4556 */ 4557 public static void setDisplayFullReport() { 4558 details = true; 4559 } 4560 4561 /** 4562 * Set exclusive mode. 4563 */ 4564 public static void setForceExclusive() { 4565 forceExclusive = true; 4566 } 4567 4568 /** 4569 * Only one instance of hbck can modify HBase at a time. 4570 */ 4571 public boolean isExclusive() { 4572 return fixAny || forceExclusive; 4573 } 4574 4575 /** 4576 * Set summary mode. 4577 * Print only summary of the tables and status (OK or INCONSISTENT) 4578 */ 4579 static void setSummary() { 4580 summary = true; 4581 } 4582 4583 /** 4584 * Set hbase:meta check mode. 4585 * Print only info about hbase:meta table deployment/state 4586 */ 4587 void setCheckMetaOnly() { 4588 checkMetaOnly = true; 4589 } 4590 4591 /** 4592 * Set region boundaries check mode. 4593 */ 4594 void setRegionBoundariesCheck() { 4595 checkRegionBoundaries = true; 4596 } 4597 4598 /** 4599 * Set replication fix mode. 4600 */ 4601 public void setFixReplication(boolean shouldFix) { 4602 fixReplication = shouldFix; 4603 fixAny |= shouldFix; 4604 } 4605 4606 /** 4607 * Check if we should rerun fsck again. This checks if we've tried to 4608 * fix something and we should rerun fsck tool again. 4609 * Display the full report from fsck. This displays all live and dead 4610 * region servers, and all known regions. 4611 */ 4612 void setShouldRerun() { 4613 rerun = true; 4614 } 4615 4616 boolean shouldRerun() { 4617 return rerun; 4618 } 4619 4620 /** 4621 * Fix inconsistencies found by fsck. This should try to fix errors (if any) 4622 * found by fsck utility. 4623 */ 4624 public void setFixAssignments(boolean shouldFix) { 4625 fixAssignments = shouldFix; 4626 fixAny |= shouldFix; 4627 } 4628 4629 boolean shouldFixAssignments() { 4630 return fixAssignments; 4631 } 4632 4633 public void setFixMeta(boolean shouldFix) { 4634 fixMeta = shouldFix; 4635 fixAny |= shouldFix; 4636 } 4637 4638 boolean shouldFixMeta() { 4639 return fixMeta; 4640 } 4641 4642 public void setFixEmptyMetaCells(boolean shouldFix) { 4643 fixEmptyMetaCells = shouldFix; 4644 fixAny |= shouldFix; 4645 } 4646 4647 boolean shouldFixEmptyMetaCells() { 4648 return fixEmptyMetaCells; 4649 } 4650 4651 public void setCheckHdfs(boolean checking) { 4652 checkHdfs = checking; 4653 } 4654 4655 boolean shouldCheckHdfs() { 4656 return checkHdfs; 4657 } 4658 4659 public void setFixHdfsHoles(boolean shouldFix) { 4660 fixHdfsHoles = shouldFix; 4661 fixAny |= shouldFix; 4662 } 4663 4664 boolean shouldFixHdfsHoles() { 4665 return fixHdfsHoles; 4666 } 4667 4668 public void setFixTableOrphans(boolean shouldFix) { 4669 fixTableOrphans = shouldFix; 4670 fixAny |= shouldFix; 4671 } 4672 4673 boolean shouldFixTableOrphans() { 4674 return fixTableOrphans; 4675 } 4676 4677 public void setFixHdfsOverlaps(boolean shouldFix) { 4678 fixHdfsOverlaps = shouldFix; 4679 fixAny |= shouldFix; 4680 } 4681 4682 boolean shouldFixHdfsOverlaps() { 4683 return fixHdfsOverlaps; 4684 } 4685 4686 public void setFixHdfsOrphans(boolean shouldFix) { 4687 fixHdfsOrphans = shouldFix; 4688 fixAny |= shouldFix; 4689 } 4690 4691 boolean shouldFixHdfsOrphans() { 4692 return fixHdfsOrphans; 4693 } 4694 4695 public void setFixVersionFile(boolean shouldFix) { 4696 fixVersionFile = shouldFix; 4697 fixAny |= shouldFix; 4698 } 4699 4700 public boolean shouldFixVersionFile() { 4701 return fixVersionFile; 4702 } 4703 4704 public void setSidelineBigOverlaps(boolean sbo) { 4705 this.sidelineBigOverlaps = sbo; 4706 } 4707 4708 public boolean shouldSidelineBigOverlaps() { 4709 return sidelineBigOverlaps; 4710 } 4711 4712 public void setFixSplitParents(boolean shouldFix) { 4713 fixSplitParents = shouldFix; 4714 fixAny |= shouldFix; 4715 } 4716 4717 public void setRemoveParents(boolean shouldFix) { 4718 removeParents = shouldFix; 4719 fixAny |= shouldFix; 4720 } 4721 4722 boolean shouldFixSplitParents() { 4723 return fixSplitParents; 4724 } 4725 4726 boolean shouldRemoveParents() { 4727 return removeParents; 4728 } 4729 4730 public void setFixReferenceFiles(boolean shouldFix) { 4731 fixReferenceFiles = shouldFix; 4732 fixAny |= shouldFix; 4733 } 4734 4735 boolean shouldFixReferenceFiles() { 4736 return fixReferenceFiles; 4737 } 4738 4739 public void setFixHFileLinks(boolean shouldFix) { 4740 fixHFileLinks = shouldFix; 4741 fixAny |= shouldFix; 4742 } 4743 4744 boolean shouldFixHFileLinks() { 4745 return fixHFileLinks; 4746 } 4747 4748 public boolean shouldIgnorePreCheckPermission() { 4749 return !fixAny || ignorePreCheckPermission; 4750 } 4751 4752 public void setIgnorePreCheckPermission(boolean ignorePreCheckPermission) { 4753 this.ignorePreCheckPermission = ignorePreCheckPermission; 4754 } 4755 4756 /** 4757 * @param mm maximum number of regions to merge into a single region. 4758 */ 4759 public void setMaxMerge(int mm) { 4760 this.maxMerge = mm; 4761 } 4762 4763 public int getMaxMerge() { 4764 return maxMerge; 4765 } 4766 4767 public void setMaxOverlapsToSideline(int mo) { 4768 this.maxOverlapsToSideline = mo; 4769 } 4770 4771 public int getMaxOverlapsToSideline() { 4772 return maxOverlapsToSideline; 4773 } 4774 4775 /** 4776 * Only check/fix tables specified by the list, 4777 * Empty list means all tables are included. 4778 */ 4779 boolean isTableIncluded(TableName table) { 4780 return (tablesIncluded.isEmpty()) || tablesIncluded.contains(table); 4781 } 4782 4783 public void includeTable(TableName table) { 4784 tablesIncluded.add(table); 4785 } 4786 4787 Set<TableName> getIncludedTables() { 4788 return new HashSet<>(tablesIncluded); 4789 } 4790 4791 /** 4792 * We are interested in only those tables that have not changed their state in 4793 * hbase:meta during the last few seconds specified by hbase.admin.fsck.timelag 4794 * @param seconds - the time in seconds 4795 */ 4796 public void setTimeLag(long seconds) { 4797 timelag = seconds * 1000; // convert to milliseconds 4798 } 4799 4800 /** 4801 * 4802 * @param sidelineDir - HDFS path to sideline data 4803 */ 4804 public void setSidelineDir(String sidelineDir) { 4805 this.sidelineDir = new Path(sidelineDir); 4806 } 4807 4808 protected HFileCorruptionChecker createHFileCorruptionChecker(boolean sidelineCorruptHFiles) throws IOException { 4809 return new HFileCorruptionChecker(getConf(), executor, sidelineCorruptHFiles); 4810 } 4811 4812 public HFileCorruptionChecker getHFilecorruptionChecker() { 4813 return hfcc; 4814 } 4815 4816 public void setHFileCorruptionChecker(HFileCorruptionChecker hfcc) { 4817 this.hfcc = hfcc; 4818 } 4819 4820 public void setRetCode(int code) { 4821 this.retcode = code; 4822 } 4823 4824 public int getRetCode() { 4825 return retcode; 4826 } 4827 4828 protected HBaseFsck printUsageAndExit() { 4829 StringWriter sw = new StringWriter(2048); 4830 PrintWriter out = new PrintWriter(sw); 4831 out.println(""); 4832 out.println("-----------------------------------------------------------------------"); 4833 out.println("NOTE: As of HBase version 2.0, the hbck tool is significantly changed."); 4834 out.println("In general, all Read-Only options are supported and can be be used"); 4835 out.println("safely. Most -fix/ -repair options are NOT supported. Please see usage"); 4836 out.println("below for details on which options are not supported."); 4837 out.println("-----------------------------------------------------------------------"); 4838 out.println(""); 4839 out.println("Usage: fsck [opts] {only tables}"); 4840 out.println(" where [opts] are:"); 4841 out.println(" -help Display help options (this)"); 4842 out.println(" -details Display full report of all regions."); 4843 out.println(" -timelag <timeInSeconds> Process only regions that " + 4844 " have not experienced any metadata updates in the last " + 4845 " <timeInSeconds> seconds."); 4846 out.println(" -sleepBeforeRerun <timeInSeconds> Sleep this many seconds" + 4847 " before checking if the fix worked if run with -fix"); 4848 out.println(" -summary Print only summary of the tables and status."); 4849 out.println(" -metaonly Only check the state of the hbase:meta table."); 4850 out.println(" -sidelineDir <hdfs://> HDFS path to backup existing meta."); 4851 out.println(" -boundaries Verify that regions boundaries are the same between META and store files."); 4852 out.println(" -exclusive Abort if another hbck is exclusive or fixing."); 4853 4854 out.println(""); 4855 out.println(" Datafile Repair options: (expert features, use with caution!)"); 4856 out.println(" -checkCorruptHFiles Check all Hfiles by opening them to make sure they are valid"); 4857 out.println(" -sidelineCorruptHFiles Quarantine corrupted HFiles. implies -checkCorruptHFiles"); 4858 4859 out.println(""); 4860 out.println(" Replication options"); 4861 out.println(" -fixReplication Deletes replication queues for removed peers"); 4862 4863 out.println(""); 4864 out.println(" Metadata Repair options supported as of version 2.0: (expert features, use with caution!)"); 4865 out.println(" -fixVersionFile Try to fix missing hbase.version file in hdfs."); 4866 out.println(" -fixReferenceFiles Try to offline lingering reference store files"); 4867 out.println(" -fixHFileLinks Try to offline lingering HFileLinks"); 4868 out.println(" -noHdfsChecking Don't load/check region info from HDFS." 4869 + " Assumes hbase:meta region info is good. Won't check/fix any HDFS issue, e.g. hole, orphan, or overlap"); 4870 out.println(" -ignorePreCheckPermission ignore filesystem permission pre-check"); 4871 4872 out.println(""); 4873 out.println("NOTE: Following options are NOT supported as of HBase version 2.0+."); 4874 out.println(""); 4875 out.println(" UNSUPPORTED Metadata Repair options: (expert features, use with caution!)"); 4876 out.println(" -fix Try to fix region assignments. This is for backwards compatiblity"); 4877 out.println(" -fixAssignments Try to fix region assignments. Replaces the old -fix"); 4878 out.println(" -fixMeta Try to fix meta problems. This assumes HDFS region info is good."); 4879 out.println(" -fixHdfsHoles Try to fix region holes in hdfs."); 4880 out.println(" -fixHdfsOrphans Try to fix region dirs with no .regioninfo file in hdfs"); 4881 out.println(" -fixTableOrphans Try to fix table dirs with no .tableinfo file in hdfs (online mode only)"); 4882 out.println(" -fixHdfsOverlaps Try to fix region overlaps in hdfs."); 4883 out.println(" -maxMerge <n> When fixing region overlaps, allow at most <n> regions to merge. (n=" + DEFAULT_MAX_MERGE +" by default)"); 4884 out.println(" -sidelineBigOverlaps When fixing region overlaps, allow to sideline big overlaps"); 4885 out.println(" -maxOverlapsToSideline <n> When fixing region overlaps, allow at most <n> regions to sideline per group. (n=" + DEFAULT_OVERLAPS_TO_SIDELINE +" by default)"); 4886 out.println(" -fixSplitParents Try to force offline split parents to be online."); 4887 out.println(" -removeParents Try to offline and sideline lingering parents and keep daughter regions."); 4888 out.println(" -fixEmptyMetaCells Try to fix hbase:meta entries not referencing any region" 4889 + " (empty REGIONINFO_QUALIFIER rows)"); 4890 4891 out.println(""); 4892 out.println(" UNSUPPORTED Metadata Repair shortcuts"); 4893 out.println(" -repair Shortcut for -fixAssignments -fixMeta -fixHdfsHoles " + 4894 "-fixHdfsOrphans -fixHdfsOverlaps -fixVersionFile -sidelineBigOverlaps -fixReferenceFiles" + 4895 "-fixHFileLinks"); 4896 out.println(" -repairHoles Shortcut for -fixAssignments -fixMeta -fixHdfsHoles"); 4897 4898 out.flush(); 4899 errors.reportError(ERROR_CODE.WRONG_USAGE, sw.toString()); 4900 4901 setRetCode(-2); 4902 return this; 4903 } 4904 4905 /** 4906 * Main program 4907 * 4908 * @param args 4909 * @throws Exception 4910 */ 4911 public static void main(String[] args) throws Exception { 4912 // create a fsck object 4913 Configuration conf = HBaseConfiguration.create(); 4914 Path hbasedir = FSUtils.getRootDir(conf); 4915 URI defaultFs = hbasedir.getFileSystem(conf).getUri(); 4916 FSUtils.setFsDefault(conf, new Path(defaultFs)); 4917 int ret = ToolRunner.run(new HBaseFsckTool(conf), args); 4918 System.exit(ret); 4919 } 4920 4921 /** 4922 * This is a Tool wrapper that gathers -Dxxx=yyy configuration settings from the command line. 4923 */ 4924 static class HBaseFsckTool extends Configured implements Tool { 4925 HBaseFsckTool(Configuration conf) { super(conf); } 4926 @Override 4927 public int run(String[] args) throws Exception { 4928 HBaseFsck hbck = new HBaseFsck(getConf()); 4929 hbck.exec(hbck.executor, args); 4930 hbck.close(); 4931 return hbck.getRetCode(); 4932 } 4933 }; 4934 4935 4936 public HBaseFsck exec(ExecutorService exec, String[] args) throws KeeperException, IOException, 4937 InterruptedException { 4938 long sleepBeforeRerun = DEFAULT_SLEEP_BEFORE_RERUN; 4939 4940 boolean checkCorruptHFiles = false; 4941 boolean sidelineCorruptHFiles = false; 4942 4943 // Process command-line args. 4944 for (int i = 0; i < args.length; i++) { 4945 String cmd = args[i]; 4946 if (cmd.equals("-help") || cmd.equals("-h")) { 4947 return printUsageAndExit(); 4948 } else if (cmd.equals("-details")) { 4949 setDisplayFullReport(); 4950 } else if (cmd.equals("-exclusive")) { 4951 setForceExclusive(); 4952 } else if (cmd.equals("-timelag")) { 4953 if (i == args.length - 1) { 4954 errors.reportError(ERROR_CODE.WRONG_USAGE, "HBaseFsck: -timelag needs a value."); 4955 return printUsageAndExit(); 4956 } 4957 try { 4958 long timelag = Long.parseLong(args[i+1]); 4959 setTimeLag(timelag); 4960 } catch (NumberFormatException e) { 4961 errors.reportError(ERROR_CODE.WRONG_USAGE, "-timelag needs a numeric value."); 4962 return printUsageAndExit(); 4963 } 4964 i++; 4965 } else if (cmd.equals("-sleepBeforeRerun")) { 4966 if (i == args.length - 1) { 4967 errors.reportError(ERROR_CODE.WRONG_USAGE, 4968 "HBaseFsck: -sleepBeforeRerun needs a value."); 4969 return printUsageAndExit(); 4970 } 4971 try { 4972 sleepBeforeRerun = Long.parseLong(args[i+1]); 4973 } catch (NumberFormatException e) { 4974 errors.reportError(ERROR_CODE.WRONG_USAGE, "-sleepBeforeRerun needs a numeric value."); 4975 return printUsageAndExit(); 4976 } 4977 i++; 4978 } else if (cmd.equals("-sidelineDir")) { 4979 if (i == args.length - 1) { 4980 errors.reportError(ERROR_CODE.WRONG_USAGE, "HBaseFsck: -sidelineDir needs a value."); 4981 return printUsageAndExit(); 4982 } 4983 i++; 4984 setSidelineDir(args[i]); 4985 } else if (cmd.equals("-fix")) { 4986 errors.reportError(ERROR_CODE.WRONG_USAGE, 4987 "This option is deprecated, please use -fixAssignments instead."); 4988 setFixAssignments(true); 4989 } else if (cmd.equals("-fixAssignments")) { 4990 setFixAssignments(true); 4991 } else if (cmd.equals("-fixMeta")) { 4992 setFixMeta(true); 4993 } else if (cmd.equals("-noHdfsChecking")) { 4994 setCheckHdfs(false); 4995 } else if (cmd.equals("-fixHdfsHoles")) { 4996 setFixHdfsHoles(true); 4997 } else if (cmd.equals("-fixHdfsOrphans")) { 4998 setFixHdfsOrphans(true); 4999 } else if (cmd.equals("-fixTableOrphans")) { 5000 setFixTableOrphans(true); 5001 } else if (cmd.equals("-fixHdfsOverlaps")) { 5002 setFixHdfsOverlaps(true); 5003 } else if (cmd.equals("-fixVersionFile")) { 5004 setFixVersionFile(true); 5005 } else if (cmd.equals("-sidelineBigOverlaps")) { 5006 setSidelineBigOverlaps(true); 5007 } else if (cmd.equals("-fixSplitParents")) { 5008 setFixSplitParents(true); 5009 } else if (cmd.equals("-removeParents")) { 5010 setRemoveParents(true); 5011 } else if (cmd.equals("-ignorePreCheckPermission")) { 5012 setIgnorePreCheckPermission(true); 5013 } else if (cmd.equals("-checkCorruptHFiles")) { 5014 checkCorruptHFiles = true; 5015 } else if (cmd.equals("-sidelineCorruptHFiles")) { 5016 sidelineCorruptHFiles = true; 5017 } else if (cmd.equals("-fixReferenceFiles")) { 5018 setFixReferenceFiles(true); 5019 } else if (cmd.equals("-fixHFileLinks")) { 5020 setFixHFileLinks(true); 5021 } else if (cmd.equals("-fixEmptyMetaCells")) { 5022 setFixEmptyMetaCells(true); 5023 } else if (cmd.equals("-repair")) { 5024 // this attempts to merge overlapping hdfs regions, needs testing 5025 // under load 5026 setFixHdfsHoles(true); 5027 setFixHdfsOrphans(true); 5028 setFixMeta(true); 5029 setFixAssignments(true); 5030 setFixHdfsOverlaps(true); 5031 setFixVersionFile(true); 5032 setSidelineBigOverlaps(true); 5033 setFixSplitParents(false); 5034 setCheckHdfs(true); 5035 setFixReferenceFiles(true); 5036 setFixHFileLinks(true); 5037 } else if (cmd.equals("-repairHoles")) { 5038 // this will make all missing hdfs regions available but may lose data 5039 setFixHdfsHoles(true); 5040 setFixHdfsOrphans(false); 5041 setFixMeta(true); 5042 setFixAssignments(true); 5043 setFixHdfsOverlaps(false); 5044 setSidelineBigOverlaps(false); 5045 setFixSplitParents(false); 5046 setCheckHdfs(true); 5047 } else if (cmd.equals("-maxOverlapsToSideline")) { 5048 if (i == args.length - 1) { 5049 errors.reportError(ERROR_CODE.WRONG_USAGE, 5050 "-maxOverlapsToSideline needs a numeric value argument."); 5051 return printUsageAndExit(); 5052 } 5053 try { 5054 int maxOverlapsToSideline = Integer.parseInt(args[i+1]); 5055 setMaxOverlapsToSideline(maxOverlapsToSideline); 5056 } catch (NumberFormatException e) { 5057 errors.reportError(ERROR_CODE.WRONG_USAGE, 5058 "-maxOverlapsToSideline needs a numeric value argument."); 5059 return printUsageAndExit(); 5060 } 5061 i++; 5062 } else if (cmd.equals("-maxMerge")) { 5063 if (i == args.length - 1) { 5064 errors.reportError(ERROR_CODE.WRONG_USAGE, 5065 "-maxMerge needs a numeric value argument."); 5066 return printUsageAndExit(); 5067 } 5068 try { 5069 int maxMerge = Integer.parseInt(args[i+1]); 5070 setMaxMerge(maxMerge); 5071 } catch (NumberFormatException e) { 5072 errors.reportError(ERROR_CODE.WRONG_USAGE, 5073 "-maxMerge needs a numeric value argument."); 5074 return printUsageAndExit(); 5075 } 5076 i++; 5077 } else if (cmd.equals("-summary")) { 5078 setSummary(); 5079 } else if (cmd.equals("-metaonly")) { 5080 setCheckMetaOnly(); 5081 } else if (cmd.equals("-boundaries")) { 5082 setRegionBoundariesCheck(); 5083 } else if (cmd.equals("-fixReplication")) { 5084 setFixReplication(true); 5085 } else if (cmd.startsWith("-")) { 5086 errors.reportError(ERROR_CODE.WRONG_USAGE, "Unrecognized option:" + cmd); 5087 return printUsageAndExit(); 5088 } else { 5089 includeTable(TableName.valueOf(cmd)); 5090 errors.print("Allow checking/fixes for table: " + cmd); 5091 } 5092 } 5093 5094 errors.print("HBaseFsck command line options: " + StringUtils.join(args, " ")); 5095 5096 // pre-check current user has FS write permission or not 5097 try { 5098 preCheckPermission(); 5099 } catch (AccessDeniedException ace) { 5100 Runtime.getRuntime().exit(-1); 5101 } catch (IOException ioe) { 5102 Runtime.getRuntime().exit(-1); 5103 } 5104 5105 // do the real work of hbck 5106 connect(); 5107 5108 // after connecting to server above, we have server version 5109 // check if unsupported option is specified based on server version 5110 if (!isOptionsSupported(args)) { 5111 return printUsageAndExit(); 5112 } 5113 5114 try { 5115 // if corrupt file mode is on, first fix them since they may be opened later 5116 if (checkCorruptHFiles || sidelineCorruptHFiles) { 5117 LOG.info("Checking all hfiles for corruption"); 5118 HFileCorruptionChecker hfcc = createHFileCorruptionChecker(sidelineCorruptHFiles); 5119 setHFileCorruptionChecker(hfcc); // so we can get result 5120 Collection<TableName> tables = getIncludedTables(); 5121 Collection<Path> tableDirs = new ArrayList<>(); 5122 Path rootdir = FSUtils.getRootDir(getConf()); 5123 if (tables.size() > 0) { 5124 for (TableName t : tables) { 5125 tableDirs.add(FSUtils.getTableDir(rootdir, t)); 5126 } 5127 } else { 5128 tableDirs = FSUtils.getTableDirs(FSUtils.getCurrentFileSystem(getConf()), rootdir); 5129 } 5130 hfcc.checkTables(tableDirs); 5131 hfcc.report(errors); 5132 } 5133 5134 // check and fix table integrity, region consistency. 5135 int code = onlineHbck(); 5136 setRetCode(code); 5137 // If we have changed the HBase state it is better to run hbck again 5138 // to see if we haven't broken something else in the process. 5139 // We run it only once more because otherwise we can easily fall into 5140 // an infinite loop. 5141 if (shouldRerun()) { 5142 try { 5143 LOG.info("Sleeping " + sleepBeforeRerun + "ms before re-checking after fix..."); 5144 Thread.sleep(sleepBeforeRerun); 5145 } catch (InterruptedException ie) { 5146 LOG.warn("Interrupted while sleeping"); 5147 return this; 5148 } 5149 // Just report 5150 setFixAssignments(false); 5151 setFixMeta(false); 5152 setFixHdfsHoles(false); 5153 setFixHdfsOverlaps(false); 5154 setFixVersionFile(false); 5155 setFixTableOrphans(false); 5156 errors.resetErrors(); 5157 code = onlineHbck(); 5158 setRetCode(code); 5159 } 5160 } finally { 5161 IOUtils.closeQuietly(this); 5162 } 5163 return this; 5164 } 5165 5166 private boolean isOptionsSupported(String[] args) { 5167 boolean result = true; 5168 String hbaseServerVersion = status.getHBaseVersion(); 5169 Object[] versionComponents = VersionInfo.getVersionComponents(hbaseServerVersion); 5170 if (versionComponents[0] instanceof Integer && ((Integer)versionComponents[0]) >= 2) { 5171 // Process command-line args. 5172 for (String arg : args) { 5173 if (unsupportedOptionsInV2.contains(arg)) { 5174 errors.reportError(ERROR_CODE.UNSUPPORTED_OPTION, 5175 "option '" + arg + "' is not " + "supportted!"); 5176 result = false; 5177 break; 5178 } 5179 } 5180 } 5181 return result; 5182 } 5183 5184 /** 5185 * ls -r for debugging purposes 5186 */ 5187 void debugLsr(Path p) throws IOException { 5188 debugLsr(getConf(), p, errors); 5189 } 5190 5191 /** 5192 * ls -r for debugging purposes 5193 */ 5194 public static void debugLsr(Configuration conf, 5195 Path p) throws IOException { 5196 debugLsr(conf, p, new PrintingErrorReporter()); 5197 } 5198 5199 /** 5200 * ls -r for debugging purposes 5201 */ 5202 public static void debugLsr(Configuration conf, 5203 Path p, ErrorReporter errors) throws IOException { 5204 if (!LOG.isDebugEnabled() || p == null) { 5205 return; 5206 } 5207 FileSystem fs = p.getFileSystem(conf); 5208 5209 if (!fs.exists(p)) { 5210 // nothing 5211 return; 5212 } 5213 errors.print(p.toString()); 5214 5215 if (fs.isFile(p)) { 5216 return; 5217 } 5218 5219 if (fs.getFileStatus(p).isDirectory()) { 5220 FileStatus[] fss= fs.listStatus(p); 5221 for (FileStatus status : fss) { 5222 debugLsr(conf, status.getPath(), errors); 5223 } 5224 } 5225 } 5226}