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