001/** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019package org.apache.hadoop.hbase.util; 020 021import edu.umd.cs.findbugs.annotations.CheckForNull; 022import java.io.ByteArrayInputStream; 023import java.io.DataInputStream; 024import java.io.EOFException; 025import java.io.FileNotFoundException; 026import java.io.IOException; 027import java.io.InputStream; 028import java.io.InterruptedIOException; 029import java.lang.reflect.InvocationTargetException; 030import java.lang.reflect.Method; 031import java.net.InetSocketAddress; 032import java.util.ArrayList; 033import java.util.Arrays; 034import java.util.Collections; 035import java.util.HashMap; 036import java.util.Iterator; 037import java.util.LinkedList; 038import java.util.List; 039import java.util.Locale; 040import java.util.Map; 041import java.util.Vector; 042import java.util.concurrent.ArrayBlockingQueue; 043import java.util.concurrent.ConcurrentHashMap; 044import java.util.concurrent.ExecutionException; 045import java.util.concurrent.ExecutorService; 046import java.util.concurrent.Executors; 047import java.util.concurrent.Future; 048import java.util.concurrent.FutureTask; 049import java.util.concurrent.ThreadPoolExecutor; 050import java.util.concurrent.TimeUnit; 051import java.util.regex.Pattern; 052import org.apache.hadoop.conf.Configuration; 053import org.apache.hadoop.fs.BlockLocation; 054import org.apache.hadoop.fs.FSDataInputStream; 055import org.apache.hadoop.fs.FSDataOutputStream; 056import org.apache.hadoop.fs.FileStatus; 057import org.apache.hadoop.fs.FileSystem; 058import org.apache.hadoop.fs.FileUtil; 059import org.apache.hadoop.fs.Path; 060import org.apache.hadoop.fs.PathFilter; 061import org.apache.hadoop.fs.permission.FsAction; 062import org.apache.hadoop.fs.permission.FsPermission; 063import org.apache.hadoop.hbase.ClusterId; 064import org.apache.hadoop.hbase.HColumnDescriptor; 065import org.apache.hadoop.hbase.HConstants; 066import org.apache.hadoop.hbase.HDFSBlocksDistribution; 067import org.apache.hadoop.hbase.HRegionInfo; 068import org.apache.hadoop.hbase.TableName; 069import org.apache.hadoop.hbase.client.RegionInfo; 070import org.apache.hadoop.hbase.exceptions.DeserializationException; 071import org.apache.hadoop.hbase.fs.HFileSystem; 072import org.apache.hadoop.hbase.io.HFileLink; 073import org.apache.hadoop.hbase.master.HMaster; 074import org.apache.hadoop.hbase.regionserver.HRegion; 075import org.apache.hadoop.hbase.regionserver.StoreFileInfo; 076import org.apache.hadoop.hbase.security.AccessDeniedException; 077import org.apache.hadoop.hbase.util.HBaseFsck.ErrorReporter; 078import org.apache.hadoop.hdfs.DFSClient; 079import org.apache.hadoop.hdfs.DFSHedgedReadMetrics; 080import org.apache.hadoop.hdfs.DistributedFileSystem; 081import org.apache.hadoop.hdfs.protocol.HdfsConstants; 082import org.apache.hadoop.io.IOUtils; 083import org.apache.hadoop.ipc.RemoteException; 084import org.apache.hadoop.security.UserGroupInformation; 085import org.apache.hadoop.util.Progressable; 086import org.apache.hadoop.util.ReflectionUtils; 087import org.apache.hadoop.util.StringUtils; 088import org.apache.yetus.audience.InterfaceAudience; 089import org.slf4j.Logger; 090import org.slf4j.LoggerFactory; 091 092import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting; 093import org.apache.hbase.thirdparty.com.google.common.base.Throwables; 094import org.apache.hbase.thirdparty.com.google.common.collect.Iterators; 095import org.apache.hbase.thirdparty.com.google.common.primitives.Ints; 096 097import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 098import org.apache.hadoop.hbase.shaded.protobuf.generated.FSProtos; 099 100/** 101 * Utility methods for interacting with the underlying file system. 102 */ 103@InterfaceAudience.Private 104public abstract class FSUtils extends CommonFSUtils { 105 private static final Logger LOG = LoggerFactory.getLogger(FSUtils.class); 106 107 private static final String THREAD_POOLSIZE = "hbase.client.localityCheck.threadPoolSize"; 108 private static final int DEFAULT_THREAD_POOLSIZE = 2; 109 110 /** Set to true on Windows platforms */ 111 @VisibleForTesting // currently only used in testing. TODO refactor into a test class 112 public static final boolean WINDOWS = System.getProperty("os.name").startsWith("Windows"); 113 114 protected FSUtils() { 115 super(); 116 } 117 118 /** 119 * @return True is <code>fs</code> is instance of DistributedFileSystem 120 * @throws IOException 121 */ 122 public static boolean isDistributedFileSystem(final FileSystem fs) throws IOException { 123 FileSystem fileSystem = fs; 124 // If passed an instance of HFileSystem, it fails instanceof DistributedFileSystem. 125 // Check its backing fs for dfs-ness. 126 if (fs instanceof HFileSystem) { 127 fileSystem = ((HFileSystem)fs).getBackingFs(); 128 } 129 return fileSystem instanceof DistributedFileSystem; 130 } 131 132 /** 133 * Compare path component of the Path URI; e.g. if hdfs://a/b/c and /a/b/c, it will compare the 134 * '/a/b/c' part. If you passed in 'hdfs://a/b/c and b/c, it would return true. Does not consider 135 * schema; i.e. if schemas different but path or subpath matches, the two will equate. 136 * @param pathToSearch Path we will be trying to match. 137 * @param pathTail 138 * @return True if <code>pathTail</code> is tail on the path of <code>pathToSearch</code> 139 */ 140 public static boolean isMatchingTail(final Path pathToSearch, final Path pathTail) { 141 if (pathToSearch.depth() != pathTail.depth()) return false; 142 Path tailPath = pathTail; 143 String tailName; 144 Path toSearch = pathToSearch; 145 String toSearchName; 146 boolean result = false; 147 do { 148 tailName = tailPath.getName(); 149 if (tailName == null || tailName.length() <= 0) { 150 result = true; 151 break; 152 } 153 toSearchName = toSearch.getName(); 154 if (toSearchName == null || toSearchName.length() <= 0) break; 155 // Move up a parent on each path for next go around. Path doesn't let us go off the end. 156 tailPath = tailPath.getParent(); 157 toSearch = toSearch.getParent(); 158 } while(tailName.equals(toSearchName)); 159 return result; 160 } 161 162 public static FSUtils getInstance(FileSystem fs, Configuration conf) { 163 String scheme = fs.getUri().getScheme(); 164 if (scheme == null) { 165 LOG.warn("Could not find scheme for uri " + 166 fs.getUri() + ", default to hdfs"); 167 scheme = "hdfs"; 168 } 169 Class<?> fsUtilsClass = conf.getClass("hbase.fsutil." + 170 scheme + ".impl", FSHDFSUtils.class); // Default to HDFS impl 171 FSUtils fsUtils = (FSUtils)ReflectionUtils.newInstance(fsUtilsClass, conf); 172 return fsUtils; 173 } 174 175 /** 176 * Delete the region directory if exists. 177 * @param conf 178 * @param hri 179 * @return True if deleted the region directory. 180 * @throws IOException 181 */ 182 public static boolean deleteRegionDir(final Configuration conf, final HRegionInfo hri) 183 throws IOException { 184 Path rootDir = getRootDir(conf); 185 FileSystem fs = rootDir.getFileSystem(conf); 186 return deleteDirectory(fs, 187 new Path(getTableDir(rootDir, hri.getTable()), hri.getEncodedName())); 188 } 189 190 /** 191 * Create the specified file on the filesystem. By default, this will: 192 * <ol> 193 * <li>overwrite the file if it exists</li> 194 * <li>apply the umask in the configuration (if it is enabled)</li> 195 * <li>use the fs configured buffer size (or 4096 if not set)</li> 196 * <li>use the configured column family replication or default replication if 197 * {@link HColumnDescriptor#DEFAULT_DFS_REPLICATION}</li> 198 * <li>use the default block size</li> 199 * <li>not track progress</li> 200 * </ol> 201 * @param conf configurations 202 * @param fs {@link FileSystem} on which to write the file 203 * @param path {@link Path} to the file to write 204 * @param perm permissions 205 * @param favoredNodes 206 * @return output stream to the created file 207 * @throws IOException if the file cannot be created 208 */ 209 public static FSDataOutputStream create(Configuration conf, FileSystem fs, Path path, 210 FsPermission perm, InetSocketAddress[] favoredNodes) throws IOException { 211 if (fs instanceof HFileSystem) { 212 FileSystem backingFs = ((HFileSystem)fs).getBackingFs(); 213 if (backingFs instanceof DistributedFileSystem) { 214 // Try to use the favoredNodes version via reflection to allow backwards- 215 // compatibility. 216 short replication = Short.parseShort(conf.get(HColumnDescriptor.DFS_REPLICATION, 217 String.valueOf(HColumnDescriptor.DEFAULT_DFS_REPLICATION))); 218 try { 219 return (FSDataOutputStream) (DistributedFileSystem.class.getDeclaredMethod("create", 220 Path.class, FsPermission.class, boolean.class, int.class, short.class, long.class, 221 Progressable.class, InetSocketAddress[].class).invoke(backingFs, path, perm, true, 222 getDefaultBufferSize(backingFs), 223 replication > 0 ? replication : getDefaultReplication(backingFs, path), 224 getDefaultBlockSize(backingFs, path), null, favoredNodes)); 225 } catch (InvocationTargetException ite) { 226 // Function was properly called, but threw it's own exception. 227 throw new IOException(ite.getCause()); 228 } catch (NoSuchMethodException e) { 229 LOG.debug("DFS Client does not support most favored nodes create; using default create"); 230 if (LOG.isTraceEnabled()) LOG.trace("Ignoring; use default create", e); 231 } catch (IllegalArgumentException e) { 232 LOG.debug("Ignoring (most likely Reflection related exception) " + e); 233 } catch (SecurityException e) { 234 LOG.debug("Ignoring (most likely Reflection related exception) " + e); 235 } catch (IllegalAccessException e) { 236 LOG.debug("Ignoring (most likely Reflection related exception) " + e); 237 } 238 } 239 } 240 return create(fs, path, perm, true); 241 } 242 243 /** 244 * Checks to see if the specified file system is available 245 * 246 * @param fs filesystem 247 * @throws IOException e 248 */ 249 public static void checkFileSystemAvailable(final FileSystem fs) 250 throws IOException { 251 if (!(fs instanceof DistributedFileSystem)) { 252 return; 253 } 254 IOException exception = null; 255 DistributedFileSystem dfs = (DistributedFileSystem) fs; 256 try { 257 if (dfs.exists(new Path("/"))) { 258 return; 259 } 260 } catch (IOException e) { 261 exception = e instanceof RemoteException ? 262 ((RemoteException)e).unwrapRemoteException() : e; 263 } 264 try { 265 fs.close(); 266 } catch (Exception e) { 267 LOG.error("file system close failed: ", e); 268 } 269 IOException io = new IOException("File system is not available"); 270 io.initCause(exception); 271 throw io; 272 } 273 274 /** 275 * We use reflection because {@link DistributedFileSystem#setSafeMode( 276 * HdfsConstants.SafeModeAction action, boolean isChecked)} is not in hadoop 1.1 277 * 278 * @param dfs 279 * @return whether we're in safe mode 280 * @throws IOException 281 */ 282 private static boolean isInSafeMode(DistributedFileSystem dfs) throws IOException { 283 boolean inSafeMode = false; 284 try { 285 Method m = DistributedFileSystem.class.getMethod("setSafeMode", new Class<?> []{ 286 org.apache.hadoop.hdfs.protocol.HdfsConstants.SafeModeAction.class, boolean.class}); 287 inSafeMode = (Boolean) m.invoke(dfs, 288 org.apache.hadoop.hdfs.protocol.HdfsConstants.SafeModeAction.SAFEMODE_GET, true); 289 } catch (Exception e) { 290 if (e instanceof IOException) throw (IOException) e; 291 292 // Check whether dfs is on safemode. 293 inSafeMode = dfs.setSafeMode( 294 org.apache.hadoop.hdfs.protocol.HdfsConstants.SafeModeAction.SAFEMODE_GET); 295 } 296 return inSafeMode; 297 } 298 299 /** 300 * Check whether dfs is in safemode. 301 * @param conf 302 * @throws IOException 303 */ 304 public static void checkDfsSafeMode(final Configuration conf) 305 throws IOException { 306 boolean isInSafeMode = false; 307 FileSystem fs = FileSystem.get(conf); 308 if (fs instanceof DistributedFileSystem) { 309 DistributedFileSystem dfs = (DistributedFileSystem)fs; 310 isInSafeMode = isInSafeMode(dfs); 311 } 312 if (isInSafeMode) { 313 throw new IOException("File system is in safemode, it can't be written now"); 314 } 315 } 316 317 /** 318 * Verifies current version of file system 319 * 320 * @param fs filesystem object 321 * @param rootdir root hbase directory 322 * @return null if no version file exists, version string otherwise. 323 * @throws IOException e 324 * @throws org.apache.hadoop.hbase.exceptions.DeserializationException 325 */ 326 public static String getVersion(FileSystem fs, Path rootdir) 327 throws IOException, DeserializationException { 328 Path versionFile = new Path(rootdir, HConstants.VERSION_FILE_NAME); 329 FileStatus[] status = null; 330 try { 331 // hadoop 2.0 throws FNFE if directory does not exist. 332 // hadoop 1.0 returns null if directory does not exist. 333 status = fs.listStatus(versionFile); 334 } catch (FileNotFoundException fnfe) { 335 return null; 336 } 337 if (status == null || status.length == 0) return null; 338 String version = null; 339 byte [] content = new byte [(int)status[0].getLen()]; 340 FSDataInputStream s = fs.open(versionFile); 341 try { 342 IOUtils.readFully(s, content, 0, content.length); 343 if (ProtobufUtil.isPBMagicPrefix(content)) { 344 version = parseVersionFrom(content); 345 } else { 346 // Presume it pre-pb format. 347 InputStream is = new ByteArrayInputStream(content); 348 DataInputStream dis = new DataInputStream(is); 349 try { 350 version = dis.readUTF(); 351 } finally { 352 dis.close(); 353 } 354 } 355 } catch (EOFException eof) { 356 LOG.warn("Version file was empty, odd, will try to set it."); 357 } finally { 358 s.close(); 359 } 360 return version; 361 } 362 363 /** 364 * Parse the content of the ${HBASE_ROOTDIR}/hbase.version file. 365 * @param bytes The byte content of the hbase.version file. 366 * @return The version found in the file as a String. 367 * @throws DeserializationException 368 */ 369 static String parseVersionFrom(final byte [] bytes) 370 throws DeserializationException { 371 ProtobufUtil.expectPBMagicPrefix(bytes); 372 int pblen = ProtobufUtil.lengthOfPBMagic(); 373 FSProtos.HBaseVersionFileContent.Builder builder = 374 FSProtos.HBaseVersionFileContent.newBuilder(); 375 try { 376 ProtobufUtil.mergeFrom(builder, bytes, pblen, bytes.length - pblen); 377 return builder.getVersion(); 378 } catch (IOException e) { 379 // Convert 380 throw new DeserializationException(e); 381 } 382 } 383 384 /** 385 * Create the content to write into the ${HBASE_ROOTDIR}/hbase.version file. 386 * @param version Version to persist 387 * @return Serialized protobuf with <code>version</code> content and a bit of pb magic for a prefix. 388 */ 389 static byte [] toVersionByteArray(final String version) { 390 FSProtos.HBaseVersionFileContent.Builder builder = 391 FSProtos.HBaseVersionFileContent.newBuilder(); 392 return ProtobufUtil.prependPBMagic(builder.setVersion(version).build().toByteArray()); 393 } 394 395 /** 396 * Verifies current version of file system 397 * 398 * @param fs file system 399 * @param rootdir root directory of HBase installation 400 * @param message if true, issues a message on System.out 401 * 402 * @throws IOException e 403 * @throws DeserializationException 404 */ 405 public static void checkVersion(FileSystem fs, Path rootdir, boolean message) 406 throws IOException, DeserializationException { 407 checkVersion(fs, rootdir, message, 0, HConstants.DEFAULT_VERSION_FILE_WRITE_ATTEMPTS); 408 } 409 410 /** 411 * Verifies current version of file system 412 * 413 * @param fs file system 414 * @param rootdir root directory of HBase installation 415 * @param message if true, issues a message on System.out 416 * @param wait wait interval 417 * @param retries number of times to retry 418 * 419 * @throws IOException e 420 * @throws DeserializationException 421 */ 422 public static void checkVersion(FileSystem fs, Path rootdir, 423 boolean message, int wait, int retries) 424 throws IOException, DeserializationException { 425 String version = getVersion(fs, rootdir); 426 if (version == null) { 427 if (!metaRegionExists(fs, rootdir)) { 428 // rootDir is empty (no version file and no root region) 429 // just create new version file (HBASE-1195) 430 setVersion(fs, rootdir, wait, retries); 431 return; 432 } 433 } else if (version.compareTo(HConstants.FILE_SYSTEM_VERSION) == 0) return; 434 435 // version is deprecated require migration 436 // Output on stdout so user sees it in terminal. 437 String msg = "HBase file layout needs to be upgraded." 438 + " You have version " + version 439 + " and I want version " + HConstants.FILE_SYSTEM_VERSION 440 + ". Consult http://hbase.apache.org/book.html for further information about upgrading HBase." 441 + " Is your hbase.rootdir valid? If so, you may need to run " 442 + "'hbase hbck -fixVersionFile'."; 443 if (message) { 444 System.out.println("WARNING! " + msg); 445 } 446 throw new FileSystemVersionException(msg); 447 } 448 449 /** 450 * Sets version of file system 451 * 452 * @param fs filesystem object 453 * @param rootdir hbase root 454 * @throws IOException e 455 */ 456 public static void setVersion(FileSystem fs, Path rootdir) 457 throws IOException { 458 setVersion(fs, rootdir, HConstants.FILE_SYSTEM_VERSION, 0, 459 HConstants.DEFAULT_VERSION_FILE_WRITE_ATTEMPTS); 460 } 461 462 /** 463 * Sets version of file system 464 * 465 * @param fs filesystem object 466 * @param rootdir hbase root 467 * @param wait time to wait for retry 468 * @param retries number of times to retry before failing 469 * @throws IOException e 470 */ 471 public static void setVersion(FileSystem fs, Path rootdir, int wait, int retries) 472 throws IOException { 473 setVersion(fs, rootdir, HConstants.FILE_SYSTEM_VERSION, wait, retries); 474 } 475 476 477 /** 478 * Sets version of file system 479 * 480 * @param fs filesystem object 481 * @param rootdir hbase root directory 482 * @param version version to set 483 * @param wait time to wait for retry 484 * @param retries number of times to retry before throwing an IOException 485 * @throws IOException e 486 */ 487 public static void setVersion(FileSystem fs, Path rootdir, String version, 488 int wait, int retries) throws IOException { 489 Path versionFile = new Path(rootdir, HConstants.VERSION_FILE_NAME); 490 Path tempVersionFile = new Path(rootdir, HConstants.HBASE_TEMP_DIRECTORY + Path.SEPARATOR + 491 HConstants.VERSION_FILE_NAME); 492 while (true) { 493 try { 494 // Write the version to a temporary file 495 FSDataOutputStream s = fs.create(tempVersionFile); 496 try { 497 s.write(toVersionByteArray(version)); 498 s.close(); 499 s = null; 500 // Move the temp version file to its normal location. Returns false 501 // if the rename failed. Throw an IOE in that case. 502 if (!fs.rename(tempVersionFile, versionFile)) { 503 throw new IOException("Unable to move temp version file to " + versionFile); 504 } 505 } finally { 506 // Cleaning up the temporary if the rename failed would be trying 507 // too hard. We'll unconditionally create it again the next time 508 // through anyway, files are overwritten by default by create(). 509 510 // Attempt to close the stream on the way out if it is still open. 511 try { 512 if (s != null) s.close(); 513 } catch (IOException ignore) { } 514 } 515 LOG.info("Created version file at " + rootdir.toString() + " with version=" + version); 516 return; 517 } catch (IOException e) { 518 if (retries > 0) { 519 LOG.debug("Unable to create version file at " + rootdir.toString() + ", retrying", e); 520 fs.delete(versionFile, false); 521 try { 522 if (wait > 0) { 523 Thread.sleep(wait); 524 } 525 } catch (InterruptedException ie) { 526 throw (InterruptedIOException)new InterruptedIOException().initCause(ie); 527 } 528 retries--; 529 } else { 530 throw e; 531 } 532 } 533 } 534 } 535 536 /** 537 * Checks that a cluster ID file exists in the HBase root directory 538 * @param fs the root directory FileSystem 539 * @param rootdir the HBase root directory in HDFS 540 * @param wait how long to wait between retries 541 * @return <code>true</code> if the file exists, otherwise <code>false</code> 542 * @throws IOException if checking the FileSystem fails 543 */ 544 public static boolean checkClusterIdExists(FileSystem fs, Path rootdir, 545 int wait) throws IOException { 546 while (true) { 547 try { 548 Path filePath = new Path(rootdir, HConstants.CLUSTER_ID_FILE_NAME); 549 return fs.exists(filePath); 550 } catch (IOException ioe) { 551 if (wait > 0) { 552 LOG.warn("Unable to check cluster ID file in " + rootdir.toString() + 553 ", retrying in "+wait+"msec: "+StringUtils.stringifyException(ioe)); 554 try { 555 Thread.sleep(wait); 556 } catch (InterruptedException e) { 557 throw (InterruptedIOException)new InterruptedIOException().initCause(e); 558 } 559 } else { 560 throw ioe; 561 } 562 } 563 } 564 } 565 566 /** 567 * Returns the value of the unique cluster ID stored for this HBase instance. 568 * @param fs the root directory FileSystem 569 * @param rootdir the path to the HBase root directory 570 * @return the unique cluster identifier 571 * @throws IOException if reading the cluster ID file fails 572 */ 573 public static ClusterId getClusterId(FileSystem fs, Path rootdir) 574 throws IOException { 575 Path idPath = new Path(rootdir, HConstants.CLUSTER_ID_FILE_NAME); 576 ClusterId clusterId = null; 577 FileStatus status = fs.exists(idPath)? fs.getFileStatus(idPath): null; 578 if (status != null) { 579 int len = Ints.checkedCast(status.getLen()); 580 byte [] content = new byte[len]; 581 FSDataInputStream in = fs.open(idPath); 582 try { 583 in.readFully(content); 584 } catch (EOFException eof) { 585 LOG.warn("Cluster ID file " + idPath.toString() + " was empty"); 586 } finally{ 587 in.close(); 588 } 589 try { 590 clusterId = ClusterId.parseFrom(content); 591 } catch (DeserializationException e) { 592 throw new IOException("content=" + Bytes.toString(content), e); 593 } 594 // If not pb'd, make it so. 595 if (!ProtobufUtil.isPBMagicPrefix(content)) { 596 String cid = null; 597 in = fs.open(idPath); 598 try { 599 cid = in.readUTF(); 600 clusterId = new ClusterId(cid); 601 } catch (EOFException eof) { 602 LOG.warn("Cluster ID file " + idPath.toString() + " was empty"); 603 } finally { 604 in.close(); 605 } 606 rewriteAsPb(fs, rootdir, idPath, clusterId); 607 } 608 return clusterId; 609 } else { 610 LOG.warn("Cluster ID file does not exist at " + idPath.toString()); 611 } 612 return clusterId; 613 } 614 615 /** 616 * @param cid 617 * @throws IOException 618 */ 619 private static void rewriteAsPb(final FileSystem fs, final Path rootdir, final Path p, 620 final ClusterId cid) 621 throws IOException { 622 // Rewrite the file as pb. Move aside the old one first, write new 623 // then delete the moved-aside file. 624 Path movedAsideName = new Path(p + "." + System.currentTimeMillis()); 625 if (!fs.rename(p, movedAsideName)) throw new IOException("Failed rename of " + p); 626 setClusterId(fs, rootdir, cid, 100); 627 if (!fs.delete(movedAsideName, false)) { 628 throw new IOException("Failed delete of " + movedAsideName); 629 } 630 LOG.debug("Rewrote the hbase.id file as pb"); 631 } 632 633 /** 634 * Writes a new unique identifier for this cluster to the "hbase.id" file 635 * in the HBase root directory 636 * @param fs the root directory FileSystem 637 * @param rootdir the path to the HBase root directory 638 * @param clusterId the unique identifier to store 639 * @param wait how long (in milliseconds) to wait between retries 640 * @throws IOException if writing to the FileSystem fails and no wait value 641 */ 642 public static void setClusterId(FileSystem fs, Path rootdir, ClusterId clusterId, 643 int wait) throws IOException { 644 while (true) { 645 try { 646 Path idFile = new Path(rootdir, HConstants.CLUSTER_ID_FILE_NAME); 647 Path tempIdFile = new Path(rootdir, HConstants.HBASE_TEMP_DIRECTORY + 648 Path.SEPARATOR + HConstants.CLUSTER_ID_FILE_NAME); 649 // Write the id file to a temporary location 650 FSDataOutputStream s = fs.create(tempIdFile); 651 try { 652 s.write(clusterId.toByteArray()); 653 s.close(); 654 s = null; 655 // Move the temporary file to its normal location. Throw an IOE if 656 // the rename failed 657 if (!fs.rename(tempIdFile, idFile)) { 658 throw new IOException("Unable to move temp version file to " + idFile); 659 } 660 } finally { 661 // Attempt to close the stream if still open on the way out 662 try { 663 if (s != null) s.close(); 664 } catch (IOException ignore) { } 665 } 666 if (LOG.isDebugEnabled()) { 667 LOG.debug("Created cluster ID file at " + idFile.toString() + " with ID: " + clusterId); 668 } 669 return; 670 } catch (IOException ioe) { 671 if (wait > 0) { 672 LOG.warn("Unable to create cluster ID file in " + rootdir.toString() + 673 ", retrying in " + wait + "msec: " + StringUtils.stringifyException(ioe)); 674 try { 675 Thread.sleep(wait); 676 } catch (InterruptedException e) { 677 throw (InterruptedIOException)new InterruptedIOException().initCause(e); 678 } 679 } else { 680 throw ioe; 681 } 682 } 683 } 684 } 685 686 /** 687 * If DFS, check safe mode and if so, wait until we clear it. 688 * @param conf configuration 689 * @param wait Sleep between retries 690 * @throws IOException e 691 */ 692 public static void waitOnSafeMode(final Configuration conf, 693 final long wait) 694 throws IOException { 695 FileSystem fs = FileSystem.get(conf); 696 if (!(fs instanceof DistributedFileSystem)) return; 697 DistributedFileSystem dfs = (DistributedFileSystem)fs; 698 // Make sure dfs is not in safe mode 699 while (isInSafeMode(dfs)) { 700 LOG.info("Waiting for dfs to exit safe mode..."); 701 try { 702 Thread.sleep(wait); 703 } catch (InterruptedException e) { 704 throw (InterruptedIOException)new InterruptedIOException().initCause(e); 705 } 706 } 707 } 708 709 /** 710 * Checks if meta region exists 711 * 712 * @param fs file system 713 * @param rootdir root directory of HBase installation 714 * @return true if exists 715 * @throws IOException e 716 */ 717 @SuppressWarnings("deprecation") 718 public static boolean metaRegionExists(FileSystem fs, Path rootdir) 719 throws IOException { 720 Path metaRegionDir = 721 HRegion.getRegionDir(rootdir, HRegionInfo.FIRST_META_REGIONINFO); 722 return fs.exists(metaRegionDir); 723 } 724 725 /** 726 * Compute HDFS blocks distribution of a given file, or a portion of the file 727 * @param fs file system 728 * @param status file status of the file 729 * @param start start position of the portion 730 * @param length length of the portion 731 * @return The HDFS blocks distribution 732 */ 733 static public HDFSBlocksDistribution computeHDFSBlocksDistribution( 734 final FileSystem fs, FileStatus status, long start, long length) 735 throws IOException { 736 HDFSBlocksDistribution blocksDistribution = new HDFSBlocksDistribution(); 737 BlockLocation [] blockLocations = 738 fs.getFileBlockLocations(status, start, length); 739 for(BlockLocation bl : blockLocations) { 740 String [] hosts = bl.getHosts(); 741 long len = bl.getLength(); 742 blocksDistribution.addHostsAndBlockWeight(hosts, len); 743 } 744 745 return blocksDistribution; 746 } 747 748 /** 749 * Update blocksDistribution with blockLocations 750 * @param blocksDistribution the hdfs blocks distribution 751 * @param blockLocations an array containing block location 752 */ 753 static public void addToHDFSBlocksDistribution( 754 HDFSBlocksDistribution blocksDistribution, BlockLocation[] blockLocations) 755 throws IOException { 756 for (BlockLocation bl : blockLocations) { 757 String[] hosts = bl.getHosts(); 758 long len = bl.getLength(); 759 blocksDistribution.addHostsAndBlockWeight(hosts, len); 760 } 761 } 762 763 // TODO move this method OUT of FSUtils. No dependencies to HMaster 764 /** 765 * Returns the total overall fragmentation percentage. Includes hbase:meta and 766 * -ROOT- as well. 767 * 768 * @param master The master defining the HBase root and file system. 769 * @return A map for each table and its percentage. 770 * @throws IOException When scanning the directory fails. 771 */ 772 public static int getTotalTableFragmentation(final HMaster master) 773 throws IOException { 774 Map<String, Integer> map = getTableFragmentation(master); 775 return map != null && map.size() > 0 ? map.get("-TOTAL-") : -1; 776 } 777 778 /** 779 * Runs through the HBase rootdir and checks how many stores for each table 780 * have more than one file in them. Checks -ROOT- and hbase:meta too. The total 781 * percentage across all tables is stored under the special key "-TOTAL-". 782 * 783 * @param master The master defining the HBase root and file system. 784 * @return A map for each table and its percentage. 785 * 786 * @throws IOException When scanning the directory fails. 787 */ 788 public static Map<String, Integer> getTableFragmentation( 789 final HMaster master) 790 throws IOException { 791 Path path = getRootDir(master.getConfiguration()); 792 // since HMaster.getFileSystem() is package private 793 FileSystem fs = path.getFileSystem(master.getConfiguration()); 794 return getTableFragmentation(fs, path); 795 } 796 797 /** 798 * Runs through the HBase rootdir and checks how many stores for each table 799 * have more than one file in them. Checks -ROOT- and hbase:meta too. The total 800 * percentage across all tables is stored under the special key "-TOTAL-". 801 * 802 * @param fs The file system to use. 803 * @param hbaseRootDir The root directory to scan. 804 * @return A map for each table and its percentage. 805 * @throws IOException When scanning the directory fails. 806 */ 807 public static Map<String, Integer> getTableFragmentation( 808 final FileSystem fs, final Path hbaseRootDir) 809 throws IOException { 810 Map<String, Integer> frags = new HashMap<>(); 811 int cfCountTotal = 0; 812 int cfFragTotal = 0; 813 PathFilter regionFilter = new RegionDirFilter(fs); 814 PathFilter familyFilter = new FamilyDirFilter(fs); 815 List<Path> tableDirs = getTableDirs(fs, hbaseRootDir); 816 for (Path d : tableDirs) { 817 int cfCount = 0; 818 int cfFrag = 0; 819 FileStatus[] regionDirs = fs.listStatus(d, regionFilter); 820 for (FileStatus regionDir : regionDirs) { 821 Path dd = regionDir.getPath(); 822 // else its a region name, now look in region for families 823 FileStatus[] familyDirs = fs.listStatus(dd, familyFilter); 824 for (FileStatus familyDir : familyDirs) { 825 cfCount++; 826 cfCountTotal++; 827 Path family = familyDir.getPath(); 828 // now in family make sure only one file 829 FileStatus[] familyStatus = fs.listStatus(family); 830 if (familyStatus.length > 1) { 831 cfFrag++; 832 cfFragTotal++; 833 } 834 } 835 } 836 // compute percentage per table and store in result list 837 frags.put(FSUtils.getTableName(d).getNameAsString(), 838 cfCount == 0? 0: Math.round((float) cfFrag / cfCount * 100)); 839 } 840 // set overall percentage for all tables 841 frags.put("-TOTAL-", 842 cfCountTotal == 0? 0: Math.round((float) cfFragTotal / cfCountTotal * 100)); 843 return frags; 844 } 845 846 /** 847 * A {@link PathFilter} that returns only regular files. 848 */ 849 static class FileFilter extends AbstractFileStatusFilter { 850 private final FileSystem fs; 851 852 public FileFilter(final FileSystem fs) { 853 this.fs = fs; 854 } 855 856 @Override 857 protected boolean accept(Path p, @CheckForNull Boolean isDir) { 858 try { 859 return isFile(fs, isDir, p); 860 } catch (IOException e) { 861 LOG.warn("unable to verify if path=" + p + " is a regular file", e); 862 return false; 863 } 864 } 865 } 866 867 /** 868 * Directory filter that doesn't include any of the directories in the specified blacklist 869 */ 870 public static class BlackListDirFilter extends AbstractFileStatusFilter { 871 private final FileSystem fs; 872 private List<String> blacklist; 873 874 /** 875 * Create a filter on the givem filesystem with the specified blacklist 876 * @param fs filesystem to filter 877 * @param directoryNameBlackList list of the names of the directories to filter. If 878 * <tt>null</tt>, all directories are returned 879 */ 880 @SuppressWarnings("unchecked") 881 public BlackListDirFilter(final FileSystem fs, final List<String> directoryNameBlackList) { 882 this.fs = fs; 883 blacklist = 884 (List<String>) (directoryNameBlackList == null ? Collections.emptyList() 885 : directoryNameBlackList); 886 } 887 888 @Override 889 protected boolean accept(Path p, @CheckForNull Boolean isDir) { 890 if (!isValidName(p.getName())) { 891 return false; 892 } 893 894 try { 895 return isDirectory(fs, isDir, p); 896 } catch (IOException e) { 897 LOG.warn("An error occurred while verifying if [" + p.toString() 898 + "] is a valid directory. Returning 'not valid' and continuing.", e); 899 return false; 900 } 901 } 902 903 protected boolean isValidName(final String name) { 904 return !blacklist.contains(name); 905 } 906 } 907 908 /** 909 * A {@link PathFilter} that only allows directories. 910 */ 911 public static class DirFilter extends BlackListDirFilter { 912 913 public DirFilter(FileSystem fs) { 914 super(fs, null); 915 } 916 } 917 918 /** 919 * A {@link PathFilter} that returns usertable directories. To get all directories use the 920 * {@link BlackListDirFilter} with a <tt>null</tt> blacklist 921 */ 922 public static class UserTableDirFilter extends BlackListDirFilter { 923 public UserTableDirFilter(FileSystem fs) { 924 super(fs, HConstants.HBASE_NON_TABLE_DIRS); 925 } 926 927 @Override 928 protected boolean isValidName(final String name) { 929 if (!super.isValidName(name)) 930 return false; 931 932 try { 933 TableName.isLegalTableQualifierName(Bytes.toBytes(name)); 934 } catch (IllegalArgumentException e) { 935 LOG.info("INVALID NAME " + name); 936 return false; 937 } 938 return true; 939 } 940 } 941 942 /** 943 * Recover file lease. Used when a file might be suspect 944 * to be had been left open by another process. 945 * @param fs FileSystem handle 946 * @param p Path of file to recover lease 947 * @param conf Configuration handle 948 * @throws IOException 949 */ 950 public abstract void recoverFileLease(final FileSystem fs, final Path p, 951 Configuration conf, CancelableProgressable reporter) throws IOException; 952 953 public static List<Path> getTableDirs(final FileSystem fs, final Path rootdir) 954 throws IOException { 955 List<Path> tableDirs = new LinkedList<>(); 956 957 for(FileStatus status : 958 fs.globStatus(new Path(rootdir, 959 new Path(HConstants.BASE_NAMESPACE_DIR, "*")))) { 960 tableDirs.addAll(FSUtils.getLocalTableDirs(fs, status.getPath())); 961 } 962 return tableDirs; 963 } 964 965 /** 966 * @param fs 967 * @param rootdir 968 * @return All the table directories under <code>rootdir</code>. Ignore non table hbase folders such as 969 * .logs, .oldlogs, .corrupt folders. 970 * @throws IOException 971 */ 972 public static List<Path> getLocalTableDirs(final FileSystem fs, final Path rootdir) 973 throws IOException { 974 // presumes any directory under hbase.rootdir is a table 975 FileStatus[] dirs = fs.listStatus(rootdir, new UserTableDirFilter(fs)); 976 List<Path> tabledirs = new ArrayList<>(dirs.length); 977 for (FileStatus dir: dirs) { 978 tabledirs.add(dir.getPath()); 979 } 980 return tabledirs; 981 } 982 983 /** 984 * Filter for all dirs that don't start with '.' 985 */ 986 public static class RegionDirFilter extends AbstractFileStatusFilter { 987 // This pattern will accept 0.90+ style hex region dirs and older numeric region dir names. 988 final public static Pattern regionDirPattern = Pattern.compile("^[0-9a-f]*$"); 989 final FileSystem fs; 990 991 public RegionDirFilter(FileSystem fs) { 992 this.fs = fs; 993 } 994 995 @Override 996 protected boolean accept(Path p, @CheckForNull Boolean isDir) { 997 if (!regionDirPattern.matcher(p.getName()).matches()) { 998 return false; 999 } 1000 1001 try { 1002 return isDirectory(fs, isDir, p); 1003 } catch (IOException ioe) { 1004 // Maybe the file was moved or the fs was disconnected. 1005 LOG.warn("Skipping file " + p +" due to IOException", ioe); 1006 return false; 1007 } 1008 } 1009 } 1010 1011 /** 1012 * Given a particular table dir, return all the regiondirs inside it, excluding files such as 1013 * .tableinfo 1014 * @param fs A file system for the Path 1015 * @param tableDir Path to a specific table directory <hbase.rootdir>/<tabledir> 1016 * @return List of paths to valid region directories in table dir. 1017 * @throws IOException 1018 */ 1019 public static List<Path> getRegionDirs(final FileSystem fs, final Path tableDir) throws IOException { 1020 // assumes we are in a table dir. 1021 List<FileStatus> rds = listStatusWithStatusFilter(fs, tableDir, new RegionDirFilter(fs)); 1022 if (rds == null) { 1023 return new ArrayList<>(); 1024 } 1025 List<Path> regionDirs = new ArrayList<>(rds.size()); 1026 for (FileStatus rdfs: rds) { 1027 Path rdPath = rdfs.getPath(); 1028 regionDirs.add(rdPath); 1029 } 1030 return regionDirs; 1031 } 1032 1033 public static Path getRegionDir(Path tableDir, RegionInfo region) { 1034 return new Path(tableDir, ServerRegionReplicaUtil.getRegionInfoForFs(region).getEncodedName()); 1035 } 1036 1037 /** 1038 * Filter for all dirs that are legal column family names. This is generally used for colfam 1039 * dirs <hbase.rootdir>/<tabledir>/<regiondir>/<colfamdir>. 1040 */ 1041 public static class FamilyDirFilter extends AbstractFileStatusFilter { 1042 final FileSystem fs; 1043 1044 public FamilyDirFilter(FileSystem fs) { 1045 this.fs = fs; 1046 } 1047 1048 @Override 1049 protected boolean accept(Path p, @CheckForNull Boolean isDir) { 1050 try { 1051 // throws IAE if invalid 1052 HColumnDescriptor.isLegalFamilyName(Bytes.toBytes(p.getName())); 1053 } catch (IllegalArgumentException iae) { 1054 // path name is an invalid family name and thus is excluded. 1055 return false; 1056 } 1057 1058 try { 1059 return isDirectory(fs, isDir, p); 1060 } catch (IOException ioe) { 1061 // Maybe the file was moved or the fs was disconnected. 1062 LOG.warn("Skipping file " + p +" due to IOException", ioe); 1063 return false; 1064 } 1065 } 1066 } 1067 1068 /** 1069 * Given a particular region dir, return all the familydirs inside it 1070 * 1071 * @param fs A file system for the Path 1072 * @param regionDir Path to a specific region directory 1073 * @return List of paths to valid family directories in region dir. 1074 * @throws IOException 1075 */ 1076 public static List<Path> getFamilyDirs(final FileSystem fs, final Path regionDir) throws IOException { 1077 // assumes we are in a region dir. 1078 FileStatus[] fds = fs.listStatus(regionDir, new FamilyDirFilter(fs)); 1079 List<Path> familyDirs = new ArrayList<>(fds.length); 1080 for (FileStatus fdfs: fds) { 1081 Path fdPath = fdfs.getPath(); 1082 familyDirs.add(fdPath); 1083 } 1084 return familyDirs; 1085 } 1086 1087 public static List<Path> getReferenceFilePaths(final FileSystem fs, final Path familyDir) throws IOException { 1088 List<FileStatus> fds = listStatusWithStatusFilter(fs, familyDir, new ReferenceFileFilter(fs)); 1089 if (fds == null) { 1090 return new ArrayList<>(); 1091 } 1092 List<Path> referenceFiles = new ArrayList<>(fds.size()); 1093 for (FileStatus fdfs: fds) { 1094 Path fdPath = fdfs.getPath(); 1095 referenceFiles.add(fdPath); 1096 } 1097 return referenceFiles; 1098 } 1099 1100 /** 1101 * Filter for HFiles that excludes reference files. 1102 */ 1103 public static class HFileFilter extends AbstractFileStatusFilter { 1104 final FileSystem fs; 1105 1106 public HFileFilter(FileSystem fs) { 1107 this.fs = fs; 1108 } 1109 1110 @Override 1111 protected boolean accept(Path p, @CheckForNull Boolean isDir) { 1112 if (!StoreFileInfo.isHFile(p)) { 1113 return false; 1114 } 1115 1116 try { 1117 return isFile(fs, isDir, p); 1118 } catch (IOException ioe) { 1119 // Maybe the file was moved or the fs was disconnected. 1120 LOG.warn("Skipping file " + p +" due to IOException", ioe); 1121 return false; 1122 } 1123 } 1124 } 1125 1126 /** 1127 * Filter for HFileLinks (StoreFiles and HFiles not included). 1128 * the filter itself does not consider if a link is file or not. 1129 */ 1130 public static class HFileLinkFilter implements PathFilter { 1131 1132 @Override 1133 public boolean accept(Path p) { 1134 return HFileLink.isHFileLink(p); 1135 } 1136 } 1137 1138 public static class ReferenceFileFilter extends AbstractFileStatusFilter { 1139 1140 private final FileSystem fs; 1141 1142 public ReferenceFileFilter(FileSystem fs) { 1143 this.fs = fs; 1144 } 1145 1146 @Override 1147 protected boolean accept(Path p, @CheckForNull Boolean isDir) { 1148 if (!StoreFileInfo.isReference(p)) { 1149 return false; 1150 } 1151 1152 try { 1153 // only files can be references. 1154 return isFile(fs, isDir, p); 1155 } catch (IOException ioe) { 1156 // Maybe the file was moved or the fs was disconnected. 1157 LOG.warn("Skipping file " + p +" due to IOException", ioe); 1158 return false; 1159 } 1160 } 1161 } 1162 1163 /** 1164 * Runs through the HBase rootdir/tablename and creates a reverse lookup map for 1165 * table StoreFile names to the full Path. 1166 * <br> 1167 * Example...<br> 1168 * Key = 3944417774205889744 <br> 1169 * Value = hdfs://localhost:51169/user/userid/-ROOT-/70236052/info/3944417774205889744 1170 * 1171 * @param map map to add values. If null, this method will create and populate one to return 1172 * @param fs The file system to use. 1173 * @param hbaseRootDir The root directory to scan. 1174 * @param tableName name of the table to scan. 1175 * @return Map keyed by StoreFile name with a value of the full Path. 1176 * @throws IOException When scanning the directory fails. 1177 * @throws InterruptedException 1178 */ 1179 public static Map<String, Path> getTableStoreFilePathMap(Map<String, Path> map, 1180 final FileSystem fs, final Path hbaseRootDir, TableName tableName) 1181 throws IOException, InterruptedException { 1182 return getTableStoreFilePathMap(map, fs, hbaseRootDir, tableName, null, null, null); 1183 } 1184 1185 /** 1186 * Runs through the HBase rootdir/tablename and creates a reverse lookup map for 1187 * table StoreFile names to the full Path. Note that because this method can be called 1188 * on a 'live' HBase system that we will skip files that no longer exist by the time 1189 * we traverse them and similarly the user of the result needs to consider that some 1190 * entries in this map may not exist by the time this call completes. 1191 * <br> 1192 * Example...<br> 1193 * Key = 3944417774205889744 <br> 1194 * Value = hdfs://localhost:51169/user/userid/-ROOT-/70236052/info/3944417774205889744 1195 * 1196 * @param resultMap map to add values. If null, this method will create and populate one to return 1197 * @param fs The file system to use. 1198 * @param hbaseRootDir The root directory to scan. 1199 * @param tableName name of the table to scan. 1200 * @param sfFilter optional path filter to apply to store files 1201 * @param executor optional executor service to parallelize this operation 1202 * @param errors ErrorReporter instance or null 1203 * @return Map keyed by StoreFile name with a value of the full Path. 1204 * @throws IOException When scanning the directory fails. 1205 * @throws InterruptedException 1206 */ 1207 public static Map<String, Path> getTableStoreFilePathMap( 1208 Map<String, Path> resultMap, 1209 final FileSystem fs, final Path hbaseRootDir, TableName tableName, final PathFilter sfFilter, 1210 ExecutorService executor, final ErrorReporter errors) throws IOException, InterruptedException { 1211 1212 final Map<String, Path> finalResultMap = 1213 resultMap == null ? new ConcurrentHashMap<>(128, 0.75f, 32) : resultMap; 1214 1215 // only include the directory paths to tables 1216 Path tableDir = FSUtils.getTableDir(hbaseRootDir, tableName); 1217 // Inside a table, there are compaction.dir directories to skip. Otherwise, all else 1218 // should be regions. 1219 final FamilyDirFilter familyFilter = new FamilyDirFilter(fs); 1220 final Vector<Exception> exceptions = new Vector<>(); 1221 1222 try { 1223 List<FileStatus> regionDirs = FSUtils.listStatusWithStatusFilter(fs, tableDir, new RegionDirFilter(fs)); 1224 if (regionDirs == null) { 1225 return finalResultMap; 1226 } 1227 1228 final List<Future<?>> futures = new ArrayList<>(regionDirs.size()); 1229 1230 for (FileStatus regionDir : regionDirs) { 1231 if (null != errors) { 1232 errors.progress(); 1233 } 1234 final Path dd = regionDir.getPath(); 1235 1236 if (!exceptions.isEmpty()) { 1237 break; 1238 } 1239 1240 Runnable getRegionStoreFileMapCall = new Runnable() { 1241 @Override 1242 public void run() { 1243 try { 1244 HashMap<String,Path> regionStoreFileMap = new HashMap<>(); 1245 List<FileStatus> familyDirs = FSUtils.listStatusWithStatusFilter(fs, dd, familyFilter); 1246 if (familyDirs == null) { 1247 if (!fs.exists(dd)) { 1248 LOG.warn("Skipping region because it no longer exists: " + dd); 1249 } else { 1250 LOG.warn("Skipping region because it has no family dirs: " + dd); 1251 } 1252 return; 1253 } 1254 for (FileStatus familyDir : familyDirs) { 1255 if (null != errors) { 1256 errors.progress(); 1257 } 1258 Path family = familyDir.getPath(); 1259 if (family.getName().equals(HConstants.RECOVERED_EDITS_DIR)) { 1260 continue; 1261 } 1262 // now in family, iterate over the StoreFiles and 1263 // put in map 1264 FileStatus[] familyStatus = fs.listStatus(family); 1265 for (FileStatus sfStatus : familyStatus) { 1266 if (null != errors) { 1267 errors.progress(); 1268 } 1269 Path sf = sfStatus.getPath(); 1270 if (sfFilter == null || sfFilter.accept(sf)) { 1271 regionStoreFileMap.put( sf.getName(), sf); 1272 } 1273 } 1274 } 1275 finalResultMap.putAll(regionStoreFileMap); 1276 } catch (Exception e) { 1277 LOG.error("Could not get region store file map for region: " + dd, e); 1278 exceptions.add(e); 1279 } 1280 } 1281 }; 1282 1283 // If executor is available, submit async tasks to exec concurrently, otherwise 1284 // just do serial sync execution 1285 if (executor != null) { 1286 Future<?> future = executor.submit(getRegionStoreFileMapCall); 1287 futures.add(future); 1288 } else { 1289 FutureTask<?> future = new FutureTask<>(getRegionStoreFileMapCall, null); 1290 future.run(); 1291 futures.add(future); 1292 } 1293 } 1294 1295 // Ensure all pending tasks are complete (or that we run into an exception) 1296 for (Future<?> f : futures) { 1297 if (!exceptions.isEmpty()) { 1298 break; 1299 } 1300 try { 1301 f.get(); 1302 } catch (ExecutionException e) { 1303 LOG.error("Unexpected exec exception! Should've been caught already. (Bug?)", e); 1304 // Shouldn't happen, we already logged/caught any exceptions in the Runnable 1305 } 1306 } 1307 } catch (IOException e) { 1308 LOG.error("Cannot execute getTableStoreFilePathMap for " + tableName, e); 1309 exceptions.add(e); 1310 } finally { 1311 if (!exceptions.isEmpty()) { 1312 // Just throw the first exception as an indication something bad happened 1313 // Don't need to propagate all the exceptions, we already logged them all anyway 1314 Throwables.propagateIfInstanceOf(exceptions.firstElement(), IOException.class); 1315 throw Throwables.propagate(exceptions.firstElement()); 1316 } 1317 } 1318 1319 return finalResultMap; 1320 } 1321 1322 public static int getRegionReferenceFileCount(final FileSystem fs, final Path p) { 1323 int result = 0; 1324 try { 1325 for (Path familyDir:getFamilyDirs(fs, p)){ 1326 result += getReferenceFilePaths(fs, familyDir).size(); 1327 } 1328 } catch (IOException e) { 1329 LOG.warn("Error Counting reference files.", e); 1330 } 1331 return result; 1332 } 1333 1334 /** 1335 * Runs through the HBase rootdir and creates a reverse lookup map for 1336 * table StoreFile names to the full Path. 1337 * <br> 1338 * Example...<br> 1339 * Key = 3944417774205889744 <br> 1340 * Value = hdfs://localhost:51169/user/userid/-ROOT-/70236052/info/3944417774205889744 1341 * 1342 * @param fs The file system to use. 1343 * @param hbaseRootDir The root directory to scan. 1344 * @return Map keyed by StoreFile name with a value of the full Path. 1345 * @throws IOException When scanning the directory fails. 1346 * @throws InterruptedException 1347 */ 1348 public static Map<String, Path> getTableStoreFilePathMap( 1349 final FileSystem fs, final Path hbaseRootDir) 1350 throws IOException, InterruptedException { 1351 return getTableStoreFilePathMap(fs, hbaseRootDir, null, null, null); 1352 } 1353 1354 /** 1355 * Runs through the HBase rootdir and creates a reverse lookup map for 1356 * table StoreFile names to the full Path. 1357 * <br> 1358 * Example...<br> 1359 * Key = 3944417774205889744 <br> 1360 * Value = hdfs://localhost:51169/user/userid/-ROOT-/70236052/info/3944417774205889744 1361 * 1362 * @param fs The file system to use. 1363 * @param hbaseRootDir The root directory to scan. 1364 * @param sfFilter optional path filter to apply to store files 1365 * @param executor optional executor service to parallelize this operation 1366 * @param errors ErrorReporter instance or null 1367 * @return Map keyed by StoreFile name with a value of the full Path. 1368 * @throws IOException When scanning the directory fails. 1369 * @throws InterruptedException 1370 */ 1371 public static Map<String, Path> getTableStoreFilePathMap( 1372 final FileSystem fs, final Path hbaseRootDir, PathFilter sfFilter, 1373 ExecutorService executor, ErrorReporter errors) 1374 throws IOException, InterruptedException { 1375 ConcurrentHashMap<String, Path> map = new ConcurrentHashMap<>(1024, 0.75f, 32); 1376 1377 // if this method looks similar to 'getTableFragmentation' that is because 1378 // it was borrowed from it. 1379 1380 // only include the directory paths to tables 1381 for (Path tableDir : FSUtils.getTableDirs(fs, hbaseRootDir)) { 1382 getTableStoreFilePathMap(map, fs, hbaseRootDir, 1383 FSUtils.getTableName(tableDir), sfFilter, executor, errors); 1384 } 1385 return map; 1386 } 1387 1388 /** 1389 * Filters FileStatuses in an array and returns a list 1390 * 1391 * @param input An array of FileStatuses 1392 * @param filter A required filter to filter the array 1393 * @return A list of FileStatuses 1394 */ 1395 public static List<FileStatus> filterFileStatuses(FileStatus[] input, 1396 FileStatusFilter filter) { 1397 if (input == null) return null; 1398 return filterFileStatuses(Iterators.forArray(input), filter); 1399 } 1400 1401 /** 1402 * Filters FileStatuses in an iterator and returns a list 1403 * 1404 * @param input An iterator of FileStatuses 1405 * @param filter A required filter to filter the array 1406 * @return A list of FileStatuses 1407 */ 1408 public static List<FileStatus> filterFileStatuses(Iterator<FileStatus> input, 1409 FileStatusFilter filter) { 1410 if (input == null) return null; 1411 ArrayList<FileStatus> results = new ArrayList<>(); 1412 while (input.hasNext()) { 1413 FileStatus f = input.next(); 1414 if (filter.accept(f)) { 1415 results.add(f); 1416 } 1417 } 1418 return results; 1419 } 1420 1421 /** 1422 * Calls fs.listStatus() and treats FileNotFoundException as non-fatal 1423 * This accommodates differences between hadoop versions, where hadoop 1 1424 * does not throw a FileNotFoundException, and return an empty FileStatus[] 1425 * while Hadoop 2 will throw FileNotFoundException. 1426 * 1427 * @param fs file system 1428 * @param dir directory 1429 * @param filter file status filter 1430 * @return null if dir is empty or doesn't exist, otherwise FileStatus list 1431 */ 1432 public static List<FileStatus> listStatusWithStatusFilter(final FileSystem fs, 1433 final Path dir, final FileStatusFilter filter) throws IOException { 1434 FileStatus [] status = null; 1435 try { 1436 status = fs.listStatus(dir); 1437 } catch (FileNotFoundException fnfe) { 1438 // if directory doesn't exist, return null 1439 if (LOG.isTraceEnabled()) { 1440 LOG.trace(dir + " doesn't exist"); 1441 } 1442 } 1443 1444 if (status == null || status.length < 1) { 1445 return null; 1446 } 1447 1448 if (filter == null) { 1449 return Arrays.asList(status); 1450 } else { 1451 List<FileStatus> status2 = filterFileStatuses(status, filter); 1452 if (status2 == null || status2.isEmpty()) { 1453 return null; 1454 } else { 1455 return status2; 1456 } 1457 } 1458 } 1459 1460 /** 1461 * Throw an exception if an action is not permitted by a user on a file. 1462 * 1463 * @param ugi 1464 * the user 1465 * @param file 1466 * the file 1467 * @param action 1468 * the action 1469 */ 1470 public static void checkAccess(UserGroupInformation ugi, FileStatus file, 1471 FsAction action) throws AccessDeniedException { 1472 if (ugi.getShortUserName().equals(file.getOwner())) { 1473 if (file.getPermission().getUserAction().implies(action)) { 1474 return; 1475 } 1476 } else if (contains(ugi.getGroupNames(), file.getGroup())) { 1477 if (file.getPermission().getGroupAction().implies(action)) { 1478 return; 1479 } 1480 } else if (file.getPermission().getOtherAction().implies(action)) { 1481 return; 1482 } 1483 throw new AccessDeniedException("Permission denied:" + " action=" + action 1484 + " path=" + file.getPath() + " user=" + ugi.getShortUserName()); 1485 } 1486 1487 private static boolean contains(String[] groups, String user) { 1488 for (String group : groups) { 1489 if (group.equals(user)) { 1490 return true; 1491 } 1492 } 1493 return false; 1494 } 1495 1496 /** 1497 * This function is to scan the root path of the file system to get the 1498 * degree of locality for each region on each of the servers having at least 1499 * one block of that region. 1500 * This is used by the tool {@link org.apache.hadoop.hbase.master.RegionPlacementMaintainer} 1501 * 1502 * @param conf 1503 * the configuration to use 1504 * @return the mapping from region encoded name to a map of server names to 1505 * locality fraction 1506 * @throws IOException 1507 * in case of file system errors or interrupts 1508 */ 1509 public static Map<String, Map<String, Float>> getRegionDegreeLocalityMappingFromFS( 1510 final Configuration conf) throws IOException { 1511 return getRegionDegreeLocalityMappingFromFS( 1512 conf, null, 1513 conf.getInt(THREAD_POOLSIZE, DEFAULT_THREAD_POOLSIZE)); 1514 1515 } 1516 1517 /** 1518 * This function is to scan the root path of the file system to get the 1519 * degree of locality for each region on each of the servers having at least 1520 * one block of that region. 1521 * 1522 * @param conf 1523 * the configuration to use 1524 * @param desiredTable 1525 * the table you wish to scan locality for 1526 * @param threadPoolSize 1527 * the thread pool size to use 1528 * @return the mapping from region encoded name to a map of server names to 1529 * locality fraction 1530 * @throws IOException 1531 * in case of file system errors or interrupts 1532 */ 1533 public static Map<String, Map<String, Float>> getRegionDegreeLocalityMappingFromFS( 1534 final Configuration conf, final String desiredTable, int threadPoolSize) 1535 throws IOException { 1536 Map<String, Map<String, Float>> regionDegreeLocalityMapping = new ConcurrentHashMap<>(); 1537 getRegionLocalityMappingFromFS(conf, desiredTable, threadPoolSize, null, 1538 regionDegreeLocalityMapping); 1539 return regionDegreeLocalityMapping; 1540 } 1541 1542 /** 1543 * This function is to scan the root path of the file system to get either the 1544 * mapping between the region name and its best locality region server or the 1545 * degree of locality of each region on each of the servers having at least 1546 * one block of that region. The output map parameters are both optional. 1547 * 1548 * @param conf 1549 * the configuration to use 1550 * @param desiredTable 1551 * the table you wish to scan locality for 1552 * @param threadPoolSize 1553 * the thread pool size to use 1554 * @param regionToBestLocalityRSMapping 1555 * the map into which to put the best locality mapping or null 1556 * @param regionDegreeLocalityMapping 1557 * the map into which to put the locality degree mapping or null, 1558 * must be a thread-safe implementation 1559 * @throws IOException 1560 * in case of file system errors or interrupts 1561 */ 1562 private static void getRegionLocalityMappingFromFS( 1563 final Configuration conf, final String desiredTable, 1564 int threadPoolSize, 1565 Map<String, String> regionToBestLocalityRSMapping, 1566 Map<String, Map<String, Float>> regionDegreeLocalityMapping) 1567 throws IOException { 1568 FileSystem fs = FileSystem.get(conf); 1569 Path rootPath = FSUtils.getRootDir(conf); 1570 long startTime = EnvironmentEdgeManager.currentTime(); 1571 Path queryPath; 1572 // The table files are in ${hbase.rootdir}/data/<namespace>/<table>/* 1573 if (null == desiredTable) { 1574 queryPath = new Path(new Path(rootPath, HConstants.BASE_NAMESPACE_DIR).toString() + "/*/*/*/"); 1575 } else { 1576 queryPath = new Path(FSUtils.getTableDir(rootPath, TableName.valueOf(desiredTable)).toString() + "/*/"); 1577 } 1578 1579 // reject all paths that are not appropriate 1580 PathFilter pathFilter = new PathFilter() { 1581 @Override 1582 public boolean accept(Path path) { 1583 // this is the region name; it may get some noise data 1584 if (null == path) { 1585 return false; 1586 } 1587 1588 // no parent? 1589 Path parent = path.getParent(); 1590 if (null == parent) { 1591 return false; 1592 } 1593 1594 String regionName = path.getName(); 1595 if (null == regionName) { 1596 return false; 1597 } 1598 1599 if (!regionName.toLowerCase(Locale.ROOT).matches("[0-9a-f]+")) { 1600 return false; 1601 } 1602 return true; 1603 } 1604 }; 1605 1606 FileStatus[] statusList = fs.globStatus(queryPath, pathFilter); 1607 1608 if (null == statusList) { 1609 return; 1610 } else { 1611 LOG.debug("Query Path: " + queryPath + " ; # list of files: " + 1612 statusList.length); 1613 } 1614 1615 // lower the number of threads in case we have very few expected regions 1616 threadPoolSize = Math.min(threadPoolSize, statusList.length); 1617 1618 // run in multiple threads 1619 ThreadPoolExecutor tpe = new ThreadPoolExecutor(threadPoolSize, 1620 threadPoolSize, 60, TimeUnit.SECONDS, 1621 new ArrayBlockingQueue<>(statusList.length)); 1622 try { 1623 // ignore all file status items that are not of interest 1624 for (FileStatus regionStatus : statusList) { 1625 if (null == regionStatus) { 1626 continue; 1627 } 1628 1629 if (!regionStatus.isDirectory()) { 1630 continue; 1631 } 1632 1633 Path regionPath = regionStatus.getPath(); 1634 if (null == regionPath) { 1635 continue; 1636 } 1637 1638 tpe.execute(new FSRegionScanner(fs, regionPath, 1639 regionToBestLocalityRSMapping, regionDegreeLocalityMapping)); 1640 } 1641 } finally { 1642 tpe.shutdown(); 1643 int threadWakeFrequency = conf.getInt(HConstants.THREAD_WAKE_FREQUENCY, 1644 60 * 1000); 1645 try { 1646 // here we wait until TPE terminates, which is either naturally or by 1647 // exceptions in the execution of the threads 1648 while (!tpe.awaitTermination(threadWakeFrequency, 1649 TimeUnit.MILLISECONDS)) { 1650 // printing out rough estimate, so as to not introduce 1651 // AtomicInteger 1652 LOG.info("Locality checking is underway: { Scanned Regions : " 1653 + tpe.getCompletedTaskCount() + "/" 1654 + tpe.getTaskCount() + " }"); 1655 } 1656 } catch (InterruptedException e) { 1657 throw (InterruptedIOException)new InterruptedIOException().initCause(e); 1658 } 1659 } 1660 1661 long overhead = EnvironmentEdgeManager.currentTime() - startTime; 1662 String overheadMsg = "Scan DFS for locality info takes " + overhead + " ms"; 1663 1664 LOG.info(overheadMsg); 1665 } 1666 1667 /** 1668 * Do our short circuit read setup. 1669 * Checks buffer size to use and whether to do checksumming in hbase or hdfs. 1670 * @param conf 1671 */ 1672 public static void setupShortCircuitRead(final Configuration conf) { 1673 // Check that the user has not set the "dfs.client.read.shortcircuit.skip.checksum" property. 1674 boolean shortCircuitSkipChecksum = 1675 conf.getBoolean("dfs.client.read.shortcircuit.skip.checksum", false); 1676 boolean useHBaseChecksum = conf.getBoolean(HConstants.HBASE_CHECKSUM_VERIFICATION, true); 1677 if (shortCircuitSkipChecksum) { 1678 LOG.warn("Configuration \"dfs.client.read.shortcircuit.skip.checksum\" should not " + 1679 "be set to true." + (useHBaseChecksum ? " HBase checksum doesn't require " + 1680 "it, see https://issues.apache.org/jira/browse/HBASE-6868." : "")); 1681 assert !shortCircuitSkipChecksum; //this will fail if assertions are on 1682 } 1683 checkShortCircuitReadBufferSize(conf); 1684 } 1685 1686 /** 1687 * Check if short circuit read buffer size is set and if not, set it to hbase value. 1688 * @param conf 1689 */ 1690 public static void checkShortCircuitReadBufferSize(final Configuration conf) { 1691 final int defaultSize = HConstants.DEFAULT_BLOCKSIZE * 2; 1692 final int notSet = -1; 1693 // DFSConfigKeys.DFS_CLIENT_READ_SHORTCIRCUIT_BUFFER_SIZE_KEY is only defined in h2 1694 final String dfsKey = "dfs.client.read.shortcircuit.buffer.size"; 1695 int size = conf.getInt(dfsKey, notSet); 1696 // If a size is set, return -- we will use it. 1697 if (size != notSet) return; 1698 // But short circuit buffer size is normally not set. Put in place the hbase wanted size. 1699 int hbaseSize = conf.getInt("hbase." + dfsKey, defaultSize); 1700 conf.setIfUnset(dfsKey, Integer.toString(hbaseSize)); 1701 } 1702 1703 /** 1704 * @param c 1705 * @return The DFSClient DFSHedgedReadMetrics instance or null if can't be found or not on hdfs. 1706 * @throws IOException 1707 */ 1708 public static DFSHedgedReadMetrics getDFSHedgedReadMetrics(final Configuration c) 1709 throws IOException { 1710 if (!isHDFS(c)) return null; 1711 // getHedgedReadMetrics is package private. Get the DFSClient instance that is internal 1712 // to the DFS FS instance and make the method getHedgedReadMetrics accessible, then invoke it 1713 // to get the singleton instance of DFSHedgedReadMetrics shared by DFSClients. 1714 final String name = "getHedgedReadMetrics"; 1715 DFSClient dfsclient = ((DistributedFileSystem)FileSystem.get(c)).getClient(); 1716 Method m; 1717 try { 1718 m = dfsclient.getClass().getDeclaredMethod(name); 1719 } catch (NoSuchMethodException e) { 1720 LOG.warn("Failed find method " + name + " in dfsclient; no hedged read metrics: " + 1721 e.getMessage()); 1722 return null; 1723 } catch (SecurityException e) { 1724 LOG.warn("Failed find method " + name + " in dfsclient; no hedged read metrics: " + 1725 e.getMessage()); 1726 return null; 1727 } 1728 m.setAccessible(true); 1729 try { 1730 return (DFSHedgedReadMetrics)m.invoke(dfsclient); 1731 } catch (IllegalAccessException e) { 1732 LOG.warn("Failed invoking method " + name + " on dfsclient; no hedged read metrics: " + 1733 e.getMessage()); 1734 return null; 1735 } catch (IllegalArgumentException e) { 1736 LOG.warn("Failed invoking method " + name + " on dfsclient; no hedged read metrics: " + 1737 e.getMessage()); 1738 return null; 1739 } catch (InvocationTargetException e) { 1740 LOG.warn("Failed invoking method " + name + " on dfsclient; no hedged read metrics: " + 1741 e.getMessage()); 1742 return null; 1743 } 1744 } 1745 1746 public static List<Path> copyFilesParallel(FileSystem srcFS, Path src, FileSystem dstFS, Path dst, 1747 Configuration conf, int threads) throws IOException { 1748 ExecutorService pool = Executors.newFixedThreadPool(threads); 1749 List<Future<Void>> futures = new ArrayList<>(); 1750 List<Path> traversedPaths; 1751 try { 1752 traversedPaths = copyFiles(srcFS, src, dstFS, dst, conf, pool, futures); 1753 for (Future<Void> future : futures) { 1754 future.get(); 1755 } 1756 } catch (ExecutionException | InterruptedException | IOException e) { 1757 throw new IOException("copy snapshot reference files failed", e); 1758 } finally { 1759 pool.shutdownNow(); 1760 } 1761 return traversedPaths; 1762 } 1763 1764 private static List<Path> copyFiles(FileSystem srcFS, Path src, FileSystem dstFS, Path dst, 1765 Configuration conf, ExecutorService pool, List<Future<Void>> futures) throws IOException { 1766 List<Path> traversedPaths = new ArrayList<>(); 1767 traversedPaths.add(dst); 1768 FileStatus currentFileStatus = srcFS.getFileStatus(src); 1769 if (currentFileStatus.isDirectory()) { 1770 if (!dstFS.mkdirs(dst)) { 1771 throw new IOException("create dir failed: " + dst); 1772 } 1773 FileStatus[] subPaths = srcFS.listStatus(src); 1774 for (FileStatus subPath : subPaths) { 1775 traversedPaths.addAll(copyFiles(srcFS, subPath.getPath(), dstFS, 1776 new Path(dst, subPath.getPath().getName()), conf, pool, futures)); 1777 } 1778 } else { 1779 Future<Void> future = pool.submit(() -> { 1780 FileUtil.copy(srcFS, src, dstFS, dst, false, false, conf); 1781 return null; 1782 }); 1783 futures.add(future); 1784 } 1785 return traversedPaths; 1786 } 1787}