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.regionserver; 019 020import edu.umd.cs.findbugs.annotations.Nullable; 021import java.io.FileNotFoundException; 022import java.io.IOException; 023import java.io.InterruptedIOException; 024import java.util.ArrayList; 025import java.util.Collection; 026import java.util.List; 027import java.util.Objects; 028import java.util.Optional; 029import java.util.UUID; 030import org.apache.hadoop.conf.Configuration; 031import org.apache.hadoop.fs.FSDataInputStream; 032import org.apache.hadoop.fs.FSDataOutputStream; 033import org.apache.hadoop.fs.FileStatus; 034import org.apache.hadoop.fs.FileSystem; 035import org.apache.hadoop.fs.FileUtil; 036import org.apache.hadoop.fs.LocatedFileStatus; 037import org.apache.hadoop.fs.Path; 038import org.apache.hadoop.fs.permission.FsPermission; 039import org.apache.hadoop.hbase.Cell; 040import org.apache.hadoop.hbase.HConstants; 041import org.apache.hadoop.hbase.PrivateCellUtil; 042import org.apache.hadoop.hbase.backup.HFileArchiver; 043import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; 044import org.apache.hadoop.hbase.client.RegionInfo; 045import org.apache.hadoop.hbase.client.TableDescriptor; 046import org.apache.hadoop.hbase.fs.HFileSystem; 047import org.apache.hadoop.hbase.io.Reference; 048import org.apache.hadoop.hbase.util.Bytes; 049import org.apache.hadoop.hbase.util.CommonFSUtils; 050import org.apache.hadoop.hbase.util.FSUtils; 051import org.apache.hadoop.hbase.util.Pair; 052import org.apache.hadoop.hbase.util.ServerRegionReplicaUtil; 053import org.apache.yetus.audience.InterfaceAudience; 054import org.slf4j.Logger; 055import org.slf4j.LoggerFactory; 056 057import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 058 059/** 060 * View to an on-disk Region. 061 * Provides the set of methods necessary to interact with the on-disk region data. 062 */ 063@InterfaceAudience.Private 064public class HRegionFileSystem { 065 private static final Logger LOG = LoggerFactory.getLogger(HRegionFileSystem.class); 066 067 /** Name of the region info file that resides just under the region directory. */ 068 public final static String REGION_INFO_FILE = ".regioninfo"; 069 070 /** Temporary subdirectory of the region directory used for merges. */ 071 public static final String REGION_MERGES_DIR = ".merges"; 072 073 /** Temporary subdirectory of the region directory used for splits. */ 074 public static final String REGION_SPLITS_DIR = ".splits"; 075 076 /** Temporary subdirectory of the region directory used for compaction output. */ 077 static final String REGION_TEMP_DIR = ".tmp"; 078 079 private final RegionInfo regionInfo; 080 //regionInfo for interacting with FS (getting encodedName, etc) 081 final RegionInfo regionInfoForFs; 082 final Configuration conf; 083 private final Path tableDir; 084 final FileSystem fs; 085 private final Path regionDir; 086 087 /** 088 * In order to handle NN connectivity hiccups, one need to retry non-idempotent operation at the 089 * client level. 090 */ 091 private final int hdfsClientRetriesNumber; 092 private final int baseSleepBeforeRetries; 093 private static final int DEFAULT_HDFS_CLIENT_RETRIES_NUMBER = 10; 094 private static final int DEFAULT_BASE_SLEEP_BEFORE_RETRIES = 1000; 095 096 /** 097 * Create a view to the on-disk region 098 * @param conf the {@link Configuration} to use 099 * @param fs {@link FileSystem} that contains the region 100 * @param tableDir {@link Path} to where the table is being stored 101 * @param regionInfo {@link RegionInfo} for region 102 */ 103 HRegionFileSystem(final Configuration conf, final FileSystem fs, final Path tableDir, 104 final RegionInfo regionInfo) { 105 this.fs = fs; 106 this.conf = conf; 107 this.tableDir = Objects.requireNonNull(tableDir, "tableDir is null"); 108 this.regionInfo = Objects.requireNonNull(regionInfo, "regionInfo is null"); 109 this.regionInfoForFs = ServerRegionReplicaUtil.getRegionInfoForFs(regionInfo); 110 this.regionDir = FSUtils.getRegionDirFromTableDir(tableDir, regionInfo); 111 this.hdfsClientRetriesNumber = conf.getInt("hdfs.client.retries.number", 112 DEFAULT_HDFS_CLIENT_RETRIES_NUMBER); 113 this.baseSleepBeforeRetries = conf.getInt("hdfs.client.sleep.before.retries", 114 DEFAULT_BASE_SLEEP_BEFORE_RETRIES); 115 } 116 117 /** @return the underlying {@link FileSystem} */ 118 public FileSystem getFileSystem() { 119 return this.fs; 120 } 121 122 /** @return the {@link RegionInfo} that describe this on-disk region view */ 123 public RegionInfo getRegionInfo() { 124 return this.regionInfo; 125 } 126 127 public RegionInfo getRegionInfoForFS() { 128 return this.regionInfoForFs; 129 } 130 131 /** @return {@link Path} to the region's root directory. */ 132 public Path getTableDir() { 133 return this.tableDir; 134 } 135 136 /** @return {@link Path} to the region directory. */ 137 public Path getRegionDir() { 138 return regionDir; 139 } 140 141 // =========================================================================== 142 // Temp Helpers 143 // =========================================================================== 144 /** @return {@link Path} to the region's temp directory, used for file creations */ 145 Path getTempDir() { 146 return new Path(getRegionDir(), REGION_TEMP_DIR); 147 } 148 149 /** 150 * Clean up any temp detritus that may have been left around from previous operation attempts. 151 */ 152 void cleanupTempDir() throws IOException { 153 deleteDir(getTempDir()); 154 } 155 156 // =========================================================================== 157 // Store/StoreFile Helpers 158 // =========================================================================== 159 /** 160 * Returns the directory path of the specified family 161 * @param familyName Column Family Name 162 * @return {@link Path} to the directory of the specified family 163 */ 164 public Path getStoreDir(final String familyName) { 165 return new Path(this.getRegionDir(), familyName); 166 } 167 168 /** 169 * Create the store directory for the specified family name 170 * @param familyName Column Family Name 171 * @return {@link Path} to the directory of the specified family 172 * @throws IOException if the directory creation fails. 173 */ 174 Path createStoreDir(final String familyName) throws IOException { 175 Path storeDir = getStoreDir(familyName); 176 if(!fs.exists(storeDir) && !createDir(storeDir)) 177 throw new IOException("Failed creating "+storeDir); 178 return storeDir; 179 } 180 181 /** 182 * Set the directory of CF to the specified storage policy. <br> 183 * <i>"LAZY_PERSIST"</i>, <i>"ALL_SSD"</i>, <i>"ONE_SSD"</i>, <i>"HOT"</i>, <i>"WARM"</i>, 184 * <i>"COLD"</i> <br> 185 * <br> 186 * See {@link org.apache.hadoop.hdfs.protocol.HdfsConstants} for more details. 187 * @param familyName The name of column family. 188 * @param policyName The name of the storage policy: 'HOT', 'COLD', etc. 189 * See see hadoop 2.6+ org.apache.hadoop.hdfs.protocol.HdfsConstants for possible list e.g 190 * 'COLD', 'WARM', 'HOT', 'ONE_SSD', 'ALL_SSD', 'LAZY_PERSIST'. 191 */ 192 public void setStoragePolicy(String familyName, String policyName) { 193 CommonFSUtils.setStoragePolicy(this.fs, getStoreDir(familyName), policyName); 194 } 195 196 /** 197 * Get the storage policy of the directory of CF. 198 * @param familyName The name of column family. 199 * @return Storage policy name, or {@code null} if not using {@link HFileSystem} or exception 200 * thrown when trying to get policy 201 */ 202 @Nullable 203 public String getStoragePolicyName(String familyName) { 204 if (this.fs instanceof HFileSystem) { 205 Path storeDir = getStoreDir(familyName); 206 return ((HFileSystem) this.fs).getStoragePolicyName(storeDir); 207 } 208 209 return null; 210 } 211 212 /** 213 * Returns the store files available for the family. 214 * This methods performs the filtering based on the valid store files. 215 * @param familyName Column Family Name 216 * @return a set of {@link StoreFileInfo} for the specified family. 217 */ 218 public Collection<StoreFileInfo> getStoreFiles(final byte[] familyName) throws IOException { 219 return getStoreFiles(Bytes.toString(familyName)); 220 } 221 222 public Collection<StoreFileInfo> getStoreFiles(final String familyName) throws IOException { 223 return getStoreFiles(familyName, true); 224 } 225 226 /** 227 * Returns the store files available for the family. 228 * This methods performs the filtering based on the valid store files. 229 * @param familyName Column Family Name 230 * @return a set of {@link StoreFileInfo} for the specified family. 231 */ 232 public Collection<StoreFileInfo> getStoreFiles(final String familyName, final boolean validate) 233 throws IOException { 234 Path familyDir = getStoreDir(familyName); 235 FileStatus[] files = CommonFSUtils.listStatus(this.fs, familyDir); 236 if (files == null) { 237 if (LOG.isTraceEnabled()) { 238 LOG.trace("No StoreFiles for: " + familyDir); 239 } 240 return null; 241 } 242 243 ArrayList<StoreFileInfo> storeFiles = new ArrayList<>(files.length); 244 for (FileStatus status: files) { 245 if (validate && !StoreFileInfo.isValid(status)) { 246 // recovered.hfiles directory is expected inside CF path when hbase.wal.split.to.hfile to 247 // true, refer HBASE-23740 248 if (!HConstants.RECOVERED_HFILES_DIR.equals(status.getPath().getName())) { 249 LOG.warn("Invalid StoreFile: {}", status.getPath()); 250 } 251 continue; 252 } 253 StoreFileInfo info = ServerRegionReplicaUtil.getStoreFileInfo(conf, fs, regionInfo, 254 regionInfoForFs, familyName, status.getPath()); 255 storeFiles.add(info); 256 257 } 258 return storeFiles; 259 } 260 261 /** 262 * Returns the store files' LocatedFileStatus which available for the family. 263 * This methods performs the filtering based on the valid store files. 264 * @param familyName Column Family Name 265 * @return a list of store files' LocatedFileStatus for the specified family. 266 */ 267 public static List<LocatedFileStatus> getStoreFilesLocatedStatus( 268 final HRegionFileSystem regionfs, final String familyName, 269 final boolean validate) throws IOException { 270 Path familyDir = regionfs.getStoreDir(familyName); 271 List<LocatedFileStatus> locatedFileStatuses = CommonFSUtils.listLocatedStatus( 272 regionfs.getFileSystem(), familyDir); 273 if (locatedFileStatuses == null) { 274 if (LOG.isTraceEnabled()) { 275 LOG.trace("No StoreFiles for: " + familyDir); 276 } 277 return null; 278 } 279 280 List<LocatedFileStatus> validStoreFiles = Lists.newArrayList(); 281 for (LocatedFileStatus status : locatedFileStatuses) { 282 if (validate && !StoreFileInfo.isValid(status)) { 283 // recovered.hfiles directory is expected inside CF path when hbase.wal.split.to.hfile to 284 // true, refer HBASE-23740 285 if (!HConstants.RECOVERED_HFILES_DIR.equals(status.getPath().getName())) { 286 LOG.warn("Invalid StoreFile: {}", status.getPath()); 287 } 288 } else { 289 validStoreFiles.add(status); 290 } 291 } 292 return validStoreFiles; 293 } 294 295 /** 296 * Return Qualified Path of the specified family/file 297 * 298 * @param familyName Column Family Name 299 * @param fileName File Name 300 * @return The qualified Path for the specified family/file 301 */ 302 Path getStoreFilePath(final String familyName, final String fileName) { 303 Path familyDir = getStoreDir(familyName); 304 return new Path(familyDir, fileName).makeQualified(fs.getUri(), fs.getWorkingDirectory()); 305 } 306 307 /** 308 * Return the store file information of the specified family/file. 309 * 310 * @param familyName Column Family Name 311 * @param fileName File Name 312 * @return The {@link StoreFileInfo} for the specified family/file 313 */ 314 StoreFileInfo getStoreFileInfo(final String familyName, final String fileName) 315 throws IOException { 316 Path familyDir = getStoreDir(familyName); 317 return ServerRegionReplicaUtil.getStoreFileInfo(conf, fs, regionInfo, 318 regionInfoForFs, familyName, new Path(familyDir, fileName)); 319 } 320 321 /** 322 * Returns true if the specified family has reference files 323 * @param familyName Column Family Name 324 * @return true if family contains reference files 325 * @throws IOException 326 */ 327 public boolean hasReferences(final String familyName) throws IOException { 328 Path storeDir = getStoreDir(familyName); 329 FileStatus[] files = CommonFSUtils.listStatus(fs, storeDir); 330 if (files != null) { 331 for(FileStatus stat: files) { 332 if(stat.isDirectory()) { 333 continue; 334 } 335 if (StoreFileInfo.isReference(stat.getPath())) { 336 LOG.trace("Reference {}", stat.getPath()); 337 return true; 338 } 339 } 340 } 341 return false; 342 } 343 344 /** 345 * Check whether region has Reference file 346 * @param htd table desciptor of the region 347 * @return true if region has reference file 348 * @throws IOException 349 */ 350 public boolean hasReferences(final TableDescriptor htd) throws IOException { 351 for (ColumnFamilyDescriptor family : htd.getColumnFamilies()) { 352 if (hasReferences(family.getNameAsString())) { 353 return true; 354 } 355 } 356 return false; 357 } 358 359 /** 360 * @return the set of families present on disk 361 * @throws IOException 362 */ 363 public Collection<String> getFamilies() throws IOException { 364 FileStatus[] fds = 365 CommonFSUtils.listStatus(fs, getRegionDir(), new FSUtils.FamilyDirFilter(fs)); 366 if (fds == null) return null; 367 368 ArrayList<String> families = new ArrayList<>(fds.length); 369 for (FileStatus status : fds) { 370 families.add(status.getPath().getName()); 371 } 372 373 return families; 374 } 375 376 /** 377 * Remove the region family from disk, archiving the store files. 378 * @param familyName Column Family Name 379 * @throws IOException if an error occours during the archiving 380 */ 381 public void deleteFamily(final String familyName) throws IOException { 382 // archive family store files 383 HFileArchiver.archiveFamily(fs, conf, regionInfoForFs, tableDir, Bytes.toBytes(familyName)); 384 385 // delete the family folder 386 Path familyDir = getStoreDir(familyName); 387 if(fs.exists(familyDir) && !deleteDir(familyDir)) 388 throw new IOException("Could not delete family " + familyName 389 + " from FileSystem for region " + regionInfoForFs.getRegionNameAsString() + "(" 390 + regionInfoForFs.getEncodedName() + ")"); 391 } 392 393 /** 394 * Generate a unique file name, used by createTempName() and commitStoreFile() 395 * @param suffix extra information to append to the generated name 396 * @return Unique file name 397 */ 398 private static String generateUniqueName(final String suffix) { 399 String name = UUID.randomUUID().toString().replaceAll("-", ""); 400 if (suffix != null) name += suffix; 401 return name; 402 } 403 404 /** 405 * Generate a unique temporary Path. Used in conjuction with commitStoreFile() 406 * to get a safer file creation. 407 * <code> 408 * Path file = fs.createTempName(); 409 * ...StoreFile.Writer(file)... 410 * fs.commitStoreFile("family", file); 411 * </code> 412 * 413 * @return Unique {@link Path} of the temporary file 414 */ 415 public Path createTempName() { 416 return createTempName(null); 417 } 418 419 /** 420 * Generate a unique temporary Path. Used in conjuction with commitStoreFile() 421 * to get a safer file creation. 422 * <code> 423 * Path file = fs.createTempName(); 424 * ...StoreFile.Writer(file)... 425 * fs.commitStoreFile("family", file); 426 * </code> 427 * 428 * @param suffix extra information to append to the generated name 429 * @return Unique {@link Path} of the temporary file 430 */ 431 public Path createTempName(final String suffix) { 432 return new Path(getTempDir(), generateUniqueName(suffix)); 433 } 434 435 /** 436 * Move the file from a build/temp location to the main family store directory. 437 * @param familyName Family that will gain the file 438 * @param buildPath {@link Path} to the file to commit. 439 * @return The new {@link Path} of the committed file 440 * @throws IOException 441 */ 442 public Path commitStoreFile(final String familyName, final Path buildPath) throws IOException { 443 Path dstPath = preCommitStoreFile(familyName, buildPath, -1, false); 444 return commitStoreFile(buildPath, dstPath); 445 } 446 447 /** 448 * Generate the filename in the main family store directory for moving the file from a build/temp 449 * location. 450 * @param familyName Family that will gain the file 451 * @param buildPath {@link Path} to the file to commit. 452 * @param seqNum Sequence Number to append to the file name (less then 0 if no sequence number) 453 * @param generateNewName False if you want to keep the buildPath name 454 * @return The new {@link Path} of the to be committed file 455 * @throws IOException 456 */ 457 private Path preCommitStoreFile(final String familyName, final Path buildPath, 458 final long seqNum, final boolean generateNewName) throws IOException { 459 Path storeDir = getStoreDir(familyName); 460 if(!fs.exists(storeDir) && !createDir(storeDir)) 461 throw new IOException("Failed creating " + storeDir); 462 463 String name = buildPath.getName(); 464 if (generateNewName) { 465 name = generateUniqueName((seqNum < 0) ? null : "_SeqId_" + seqNum + "_"); 466 } 467 Path dstPath = new Path(storeDir, name); 468 if (!fs.exists(buildPath)) { 469 throw new FileNotFoundException(buildPath.toString()); 470 } 471 if (LOG.isDebugEnabled()) { 472 LOG.debug("Committing " + buildPath + " as " + dstPath); 473 } 474 return dstPath; 475 } 476 477 /* 478 * Moves file from staging dir to region dir 479 * @param buildPath {@link Path} to the file to commit. 480 * @param dstPath {@link Path} to the file under region dir 481 * @return The {@link Path} of the committed file 482 * @throws IOException 483 */ 484 Path commitStoreFile(final Path buildPath, Path dstPath) throws IOException { 485 // buildPath exists, therefore not doing an exists() check. 486 if (!rename(buildPath, dstPath)) { 487 throw new IOException("Failed rename of " + buildPath + " to " + dstPath); 488 } 489 return dstPath; 490 } 491 492 /** 493 * Archives the specified store file from the specified family. 494 * @param familyName Family that contains the store files 495 * @param filePath {@link Path} to the store file to remove 496 * @throws IOException if the archiving fails 497 */ 498 public void removeStoreFile(final String familyName, final Path filePath) 499 throws IOException { 500 HFileArchiver.archiveStoreFile(this.conf, this.fs, this.regionInfoForFs, 501 this.tableDir, Bytes.toBytes(familyName), filePath); 502 } 503 504 /** 505 * Closes and archives the specified store files from the specified family. 506 * @param familyName Family that contains the store files 507 * @param storeFiles set of store files to remove 508 * @throws IOException if the archiving fails 509 */ 510 public void removeStoreFiles(String familyName, Collection<HStoreFile> storeFiles) 511 throws IOException { 512 HFileArchiver.archiveStoreFiles(this.conf, this.fs, this.regionInfoForFs, 513 this.tableDir, Bytes.toBytes(familyName), storeFiles); 514 } 515 516 /** 517 * Bulk load: Add a specified store file to the specified family. 518 * If the source file is on the same different file-system is moved from the 519 * source location to the destination location, otherwise is copied over. 520 * 521 * @param familyName Family that will gain the file 522 * @param srcPath {@link Path} to the file to import 523 * @param seqNum Bulk Load sequence number 524 * @return The destination {@link Path} of the bulk loaded file 525 * @throws IOException 526 */ 527 Pair<Path, Path> bulkLoadStoreFile(final String familyName, Path srcPath, long seqNum) 528 throws IOException { 529 // Copy the file if it's on another filesystem 530 FileSystem srcFs = srcPath.getFileSystem(conf); 531 srcPath = srcFs.resolvePath(srcPath); 532 FileSystem realSrcFs = srcPath.getFileSystem(conf); 533 FileSystem desFs = fs instanceof HFileSystem ? ((HFileSystem)fs).getBackingFs() : fs; 534 535 // We can't compare FileSystem instances as equals() includes UGI instance 536 // as part of the comparison and won't work when doing SecureBulkLoad 537 // TODO deal with viewFS 538 if (!FSUtils.isSameHdfs(conf, realSrcFs, desFs)) { 539 LOG.info("Bulk-load file " + srcPath + " is on different filesystem than " + 540 "the destination store. Copying file over to destination filesystem."); 541 Path tmpPath = createTempName(); 542 FileUtil.copy(realSrcFs, srcPath, fs, tmpPath, false, conf); 543 LOG.info("Copied " + srcPath + " to temporary path on destination filesystem: " + tmpPath); 544 srcPath = tmpPath; 545 } 546 547 return new Pair<>(srcPath, preCommitStoreFile(familyName, srcPath, seqNum, true)); 548 } 549 550 // =========================================================================== 551 // Splits Helpers 552 // =========================================================================== 553 /** @return {@link Path} to the temp directory used during split operations */ 554 Path getSplitsDir() { 555 return new Path(getRegionDir(), REGION_SPLITS_DIR); 556 } 557 558 public Path getSplitsDir(final RegionInfo hri) { 559 return new Path(getSplitsDir(), hri.getEncodedName()); 560 } 561 562 /** 563 * Clean up any split detritus that may have been left around from previous split attempts. 564 */ 565 void cleanupSplitsDir() throws IOException { 566 deleteDir(getSplitsDir()); 567 } 568 569 /** 570 * Clean up any split detritus that may have been left around from previous 571 * split attempts. 572 * Call this method on initial region deploy. 573 * @throws IOException 574 */ 575 void cleanupAnySplitDetritus() throws IOException { 576 Path splitdir = this.getSplitsDir(); 577 if (!fs.exists(splitdir)) return; 578 // Look at the splitdir. It could have the encoded names of the daughter 579 // regions we tried to make. See if the daughter regions actually got made 580 // out under the tabledir. If here under splitdir still, then the split did 581 // not complete. Try and do cleanup. This code WILL NOT catch the case 582 // where we successfully created daughter a but regionserver crashed during 583 // the creation of region b. In this case, there'll be an orphan daughter 584 // dir in the filesystem. TOOD: Fix. 585 FileStatus[] daughters = CommonFSUtils.listStatus(fs, splitdir, new FSUtils.DirFilter(fs)); 586 if (daughters != null) { 587 for (FileStatus daughter: daughters) { 588 Path daughterDir = new Path(getTableDir(), daughter.getPath().getName()); 589 if (fs.exists(daughterDir) && !deleteDir(daughterDir)) { 590 throw new IOException("Failed delete of " + daughterDir); 591 } 592 } 593 } 594 cleanupSplitsDir(); 595 LOG.info("Cleaned up old failed split transaction detritus: " + splitdir); 596 } 597 598 /** 599 * Remove daughter region 600 * @param regionInfo daughter {@link RegionInfo} 601 * @throws IOException 602 */ 603 void cleanupDaughterRegion(final RegionInfo regionInfo) throws IOException { 604 Path regionDir = new Path(this.tableDir, regionInfo.getEncodedName()); 605 if (this.fs.exists(regionDir) && !deleteDir(regionDir)) { 606 throw new IOException("Failed delete of " + regionDir); 607 } 608 } 609 610 /** 611 * Commit a daughter region, moving it from the split temporary directory 612 * to the proper location in the filesystem. 613 * 614 * @param regionInfo daughter {@link org.apache.hadoop.hbase.client.RegionInfo} 615 * @throws IOException 616 */ 617 public Path commitDaughterRegion(final RegionInfo regionInfo) 618 throws IOException { 619 Path regionDir = new Path(this.tableDir, regionInfo.getEncodedName()); 620 Path daughterTmpDir = this.getSplitsDir(regionInfo); 621 622 if (fs.exists(daughterTmpDir)) { 623 624 // Write HRI to a file in case we need to recover hbase:meta 625 Path regionInfoFile = new Path(daughterTmpDir, REGION_INFO_FILE); 626 byte[] regionInfoContent = getRegionInfoFileContent(regionInfo); 627 writeRegionInfoFileContent(conf, fs, regionInfoFile, regionInfoContent); 628 629 // Move the daughter temp dir to the table dir 630 if (!rename(daughterTmpDir, regionDir)) { 631 throw new IOException("Unable to rename " + daughterTmpDir + " to " + regionDir); 632 } 633 } 634 635 return regionDir; 636 } 637 638 /** 639 * Create the region splits directory. 640 */ 641 public void createSplitsDir(RegionInfo daughterA, RegionInfo daughterB) throws IOException { 642 Path splitdir = getSplitsDir(); 643 if (fs.exists(splitdir)) { 644 LOG.info("The " + splitdir + " directory exists. Hence deleting it to recreate it"); 645 if (!deleteDir(splitdir)) { 646 throw new IOException("Failed deletion of " + splitdir + " before creating them again."); 647 } 648 } 649 // splitDir doesn't exists now. No need to do an exists() call for it. 650 if (!createDir(splitdir)) { 651 throw new IOException("Failed create of " + splitdir); 652 } 653 Path daughterATmpDir = getSplitsDir(daughterA); 654 if (!createDir(daughterATmpDir)) { 655 throw new IOException("Failed create of " + daughterATmpDir); 656 } 657 Path daughterBTmpDir = getSplitsDir(daughterB); 658 if (!createDir(daughterBTmpDir)) { 659 throw new IOException("Failed create of " + daughterBTmpDir); 660 } 661 } 662 663 /** 664 * Write out a split reference. Package local so it doesnt leak out of 665 * regionserver. 666 * @param hri {@link RegionInfo} of the destination 667 * @param familyName Column Family Name 668 * @param f File to split. 669 * @param splitRow Split Row 670 * @param top True if we are referring to the top half of the hfile. 671 * @param splitPolicy A split policy instance; be careful! May not be full populated; e.g. if 672 * this method is invoked on the Master side, then the RegionSplitPolicy will 673 * NOT have a reference to a Region. 674 * @return Path to created reference. 675 * @throws IOException 676 */ 677 public Path splitStoreFile(RegionInfo hri, String familyName, HStoreFile f, byte[] splitRow, 678 boolean top, RegionSplitPolicy splitPolicy) throws IOException { 679 if (splitPolicy == null || !splitPolicy.skipStoreFileRangeCheck(familyName)) { 680 // Check whether the split row lies in the range of the store file 681 // If it is outside the range, return directly. 682 f.initReader(); 683 try { 684 if (top) { 685 //check if larger than last key. 686 Cell splitKey = PrivateCellUtil.createFirstOnRow(splitRow); 687 Optional<Cell> lastKey = f.getLastKey(); 688 // If lastKey is null means storefile is empty. 689 if (!lastKey.isPresent()) { 690 return null; 691 } 692 if (f.getComparator().compare(splitKey, lastKey.get()) > 0) { 693 return null; 694 } 695 } else { 696 //check if smaller than first key 697 Cell splitKey = PrivateCellUtil.createLastOnRow(splitRow); 698 Optional<Cell> firstKey = f.getFirstKey(); 699 // If firstKey is null means storefile is empty. 700 if (!firstKey.isPresent()) { 701 return null; 702 } 703 if (f.getComparator().compare(splitKey, firstKey.get()) < 0) { 704 return null; 705 } 706 } 707 } finally { 708 f.closeStoreFile(f.getCacheConf() != null ? f.getCacheConf().shouldEvictOnClose() : true); 709 } 710 } 711 712 Path splitDir = new Path(getSplitsDir(hri), familyName); 713 // A reference to the bottom half of the hsf store file. 714 Reference r = 715 top ? Reference.createTopReference(splitRow): Reference.createBottomReference(splitRow); 716 // Add the referred-to regions name as a dot separated suffix. 717 // See REF_NAME_REGEX regex above. The referred-to regions name is 718 // up in the path of the passed in <code>f</code> -- parentdir is family, 719 // then the directory above is the region name. 720 String parentRegionName = regionInfoForFs.getEncodedName(); 721 // Write reference with same file id only with the other region name as 722 // suffix and into the new region location (under same family). 723 Path p = new Path(splitDir, f.getPath().getName() + "." + parentRegionName); 724 return r.write(fs, p); 725 } 726 727 // =========================================================================== 728 // Merge Helpers 729 // =========================================================================== 730 /** @return {@link Path} to the temp directory used during merge operations */ 731 public Path getMergesDir() { 732 return new Path(getRegionDir(), REGION_MERGES_DIR); 733 } 734 735 Path getMergesDir(final RegionInfo hri) { 736 return new Path(getMergesDir(), hri.getEncodedName()); 737 } 738 739 /** 740 * Clean up any merge detritus that may have been left around from previous merge attempts. 741 */ 742 void cleanupMergesDir() throws IOException { 743 deleteDir(getMergesDir()); 744 } 745 746 /** 747 * Remove merged region 748 * @param mergedRegion {@link RegionInfo} 749 * @throws IOException 750 */ 751 public void cleanupMergedRegion(final RegionInfo mergedRegion) throws IOException { 752 Path regionDir = new Path(this.tableDir, mergedRegion.getEncodedName()); 753 if (this.fs.exists(regionDir) && !this.fs.delete(regionDir, true)) { 754 throw new IOException("Failed delete of " + regionDir); 755 } 756 } 757 758 static boolean mkdirs(FileSystem fs, Configuration conf, Path dir) throws IOException { 759 if (FSUtils.isDistributedFileSystem(fs) || 760 !conf.getBoolean(HConstants.ENABLE_DATA_FILE_UMASK, false)) { 761 return fs.mkdirs(dir); 762 } 763 FsPermission perms = CommonFSUtils.getFilePermissions(fs, conf, HConstants.DATA_FILE_UMASK_KEY); 764 return fs.mkdirs(dir, perms); 765 } 766 767 /** 768 * Create the region merges directory, a temporary directory to accumulate 769 * merges in. 770 * @throws IOException If merges dir already exists or we fail to create it. 771 * @see HRegionFileSystem#cleanupMergesDir() 772 */ 773 public void createMergesDir() throws IOException { 774 Path mergesdir = getMergesDir(); 775 if (fs.exists(mergesdir)) { 776 LOG.info("{} directory exists. Deleting it to recreate it anew", mergesdir); 777 if (!fs.delete(mergesdir, true)) { 778 throw new IOException("Failed deletion of " + mergesdir + " before recreate."); 779 } 780 } 781 if (!mkdirs(fs, conf, mergesdir)) { 782 throw new IOException("Failed create of " + mergesdir); 783 } 784 } 785 786 /** 787 * Write out a merge reference under the given merges directory. Package local 788 * so it doesnt leak out of regionserver. 789 * @param mergedRegion {@link RegionInfo} of the merged region 790 * @param familyName Column Family Name 791 * @param f File to create reference. 792 * @param mergedDir 793 * @return Path to created reference. 794 * @throws IOException 795 */ 796 public Path mergeStoreFile(RegionInfo mergedRegion, String familyName, HStoreFile f, 797 Path mergedDir) throws IOException { 798 Path referenceDir = new Path(new Path(mergedDir, 799 mergedRegion.getEncodedName()), familyName); 800 // A whole reference to the store file. 801 Reference r = Reference.createTopReference(regionInfoForFs.getStartKey()); 802 // Add the referred-to regions name as a dot separated suffix. 803 // See REF_NAME_REGEX regex above. The referred-to regions name is 804 // up in the path of the passed in <code>f</code> -- parentdir is family, 805 // then the directory above is the region name. 806 String mergingRegionName = regionInfoForFs.getEncodedName(); 807 // Write reference with same file id only with the other region name as 808 // suffix and into the new region location (under same family). 809 Path p = new Path(referenceDir, f.getPath().getName() + "." 810 + mergingRegionName); 811 return r.write(fs, p); 812 } 813 814 /** 815 * Commit a merged region, moving it from the merges temporary directory to 816 * the proper location in the filesystem. 817 * @param mergedRegionInfo merged region {@link RegionInfo} 818 * @throws IOException 819 */ 820 public void commitMergedRegion(final RegionInfo mergedRegionInfo) throws IOException { 821 Path regionDir = new Path(this.tableDir, mergedRegionInfo.getEncodedName()); 822 Path mergedRegionTmpDir = this.getMergesDir(mergedRegionInfo); 823 // Move the tmp dir to the expected location 824 if (mergedRegionTmpDir != null && fs.exists(mergedRegionTmpDir)) { 825 826 // Write HRI to a file in case we need to recover hbase:meta 827 Path regionInfoFile = new Path(mergedRegionTmpDir, REGION_INFO_FILE); 828 byte[] regionInfoContent = getRegionInfoFileContent(regionInfo); 829 writeRegionInfoFileContent(conf, fs, regionInfoFile, regionInfoContent); 830 831 if (!fs.rename(mergedRegionTmpDir, regionDir)) { 832 throw new IOException("Unable to rename " + mergedRegionTmpDir + " to " 833 + regionDir); 834 } 835 } 836 } 837 838 // =========================================================================== 839 // Create/Open/Delete Helpers 840 // =========================================================================== 841 /** 842 * Log the current state of the region 843 * @param LOG log to output information 844 * @throws IOException if an unexpected exception occurs 845 */ 846 void logFileSystemState(final Logger LOG) throws IOException { 847 CommonFSUtils.logFileSystemState(fs, this.getRegionDir(), LOG); 848 } 849 850 /** 851 * @param hri 852 * @return Content of the file we write out to the filesystem under a region 853 * @throws IOException 854 */ 855 private static byte[] getRegionInfoFileContent(final RegionInfo hri) throws IOException { 856 return RegionInfo.toDelimitedByteArray(hri); 857 } 858 859 /** 860 * Create a {@link RegionInfo} from the serialized version on-disk. 861 * @param fs {@link FileSystem} that contains the Region Info file 862 * @param regionDir {@link Path} to the Region Directory that contains the Info file 863 * @return An {@link RegionInfo} instance gotten from the Region Info file. 864 * @throws IOException if an error occurred during file open/read operation. 865 */ 866 public static RegionInfo loadRegionInfoFileContent(final FileSystem fs, final Path regionDir) 867 throws IOException { 868 FSDataInputStream in = fs.open(new Path(regionDir, REGION_INFO_FILE)); 869 try { 870 return RegionInfo.parseFrom(in); 871 } finally { 872 in.close(); 873 } 874 } 875 876 /** 877 * Write the .regioninfo file on-disk. 878 * <p/> 879 * Overwrites if exists already. 880 */ 881 private static void writeRegionInfoFileContent(final Configuration conf, final FileSystem fs, 882 final Path regionInfoFile, final byte[] content) throws IOException { 883 // First check to get the permissions 884 FsPermission perms = CommonFSUtils.getFilePermissions(fs, conf, HConstants.DATA_FILE_UMASK_KEY); 885 // Write the RegionInfo file content 886 try (FSDataOutputStream out = FSUtils.create(conf, fs, regionInfoFile, perms, null)) { 887 out.write(content); 888 } 889 } 890 891 /** 892 * Write out an info file under the stored region directory. Useful recovering mangled regions. 893 * If the regionInfo already exists on-disk, then we fast exit. 894 */ 895 void checkRegionInfoOnFilesystem() throws IOException { 896 // Compose the content of the file so we can compare to length in filesystem. If not same, 897 // rewrite it (it may have been written in the old format using Writables instead of pb). The 898 // pb version is much shorter -- we write now w/o the toString version -- so checking length 899 // only should be sufficient. I don't want to read the file every time to check if it pb 900 // serialized. 901 byte[] content = getRegionInfoFileContent(regionInfoForFs); 902 903 // Verify if the region directory exists before opening a region. We need to do this since if 904 // the region directory doesn't exist we will re-create the region directory and a new HRI 905 // when HRegion.openHRegion() is called. 906 try { 907 FileStatus status = fs.getFileStatus(getRegionDir()); 908 } catch (FileNotFoundException e) { 909 LOG.warn(getRegionDir() + " doesn't exist for region: " + regionInfoForFs.getEncodedName() + 910 " on table " + regionInfo.getTable()); 911 } 912 913 try { 914 Path regionInfoFile = new Path(getRegionDir(), REGION_INFO_FILE); 915 FileStatus status = fs.getFileStatus(regionInfoFile); 916 if (status != null && status.getLen() == content.length) { 917 // Then assume the content good and move on. 918 // NOTE: that the length is not sufficient to define the the content matches. 919 return; 920 } 921 922 LOG.info("Rewriting .regioninfo file at: " + regionInfoFile); 923 if (!fs.delete(regionInfoFile, false)) { 924 throw new IOException("Unable to remove existing " + regionInfoFile); 925 } 926 } catch (FileNotFoundException e) { 927 LOG.warn(REGION_INFO_FILE + " file not found for region: " + regionInfoForFs.getEncodedName() + 928 " on table " + regionInfo.getTable()); 929 } 930 931 // Write HRI to a file in case we need to recover hbase:meta 932 writeRegionInfoOnFilesystem(content, true); 933 } 934 935 /** 936 * Write out an info file under the region directory. Useful recovering mangled regions. 937 * @param useTempDir indicate whether or not using the region .tmp dir for a safer file creation. 938 */ 939 private void writeRegionInfoOnFilesystem(boolean useTempDir) throws IOException { 940 byte[] content = getRegionInfoFileContent(regionInfoForFs); 941 writeRegionInfoOnFilesystem(content, useTempDir); 942 } 943 944 /** 945 * Write out an info file under the region directory. Useful recovering mangled regions. 946 * @param regionInfoContent serialized version of the {@link RegionInfo} 947 * @param useTempDir indicate whether or not using the region .tmp dir for a safer file creation. 948 */ 949 private void writeRegionInfoOnFilesystem(final byte[] regionInfoContent, 950 final boolean useTempDir) throws IOException { 951 Path regionInfoFile = new Path(getRegionDir(), REGION_INFO_FILE); 952 if (useTempDir) { 953 // Create in tmpDir and then move into place in case we crash after 954 // create but before close. If we don't successfully close the file, 955 // subsequent region reopens will fail the below because create is 956 // registered in NN. 957 958 // And then create the file 959 Path tmpPath = new Path(getTempDir(), REGION_INFO_FILE); 960 961 // If datanode crashes or if the RS goes down just before the close is called while trying to 962 // close the created regioninfo file in the .tmp directory then on next 963 // creation we will be getting AlreadyCreatedException. 964 // Hence delete and create the file if exists. 965 if (CommonFSUtils.isExists(fs, tmpPath)) { 966 CommonFSUtils.delete(fs, tmpPath, true); 967 } 968 969 // Write HRI to a file in case we need to recover hbase:meta 970 writeRegionInfoFileContent(conf, fs, tmpPath, regionInfoContent); 971 972 // Move the created file to the original path 973 if (fs.exists(tmpPath) && !rename(tmpPath, regionInfoFile)) { 974 throw new IOException("Unable to rename " + tmpPath + " to " + regionInfoFile); 975 } 976 } else { 977 // Write HRI to a file in case we need to recover hbase:meta 978 writeRegionInfoFileContent(conf, fs, regionInfoFile, regionInfoContent); 979 } 980 } 981 982 /** 983 * Create a new Region on file-system. 984 * @param conf the {@link Configuration} to use 985 * @param fs {@link FileSystem} from which to add the region 986 * @param tableDir {@link Path} to where the table is being stored 987 * @param regionInfo {@link RegionInfo} for region to be added 988 * @throws IOException if the region creation fails due to a FileSystem exception. 989 */ 990 public static HRegionFileSystem createRegionOnFileSystem(final Configuration conf, 991 final FileSystem fs, final Path tableDir, final RegionInfo regionInfo) throws IOException { 992 HRegionFileSystem regionFs = new HRegionFileSystem(conf, fs, tableDir, regionInfo); 993 994 // We only create a .regioninfo and the region directory if this is the default region replica 995 if (regionInfo.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) { 996 Path regionDir = regionFs.getRegionDir(); 997 if (fs.exists(regionDir)) { 998 LOG.warn("Trying to create a region that already exists on disk: " + regionDir); 999 } else { 1000 // Create the region directory 1001 if (!createDirOnFileSystem(fs, conf, regionDir)) { 1002 LOG.warn("Unable to create the region directory: " + regionDir); 1003 throw new IOException("Unable to create region directory: " + regionDir); 1004 } 1005 } 1006 1007 // Write HRI to a file in case we need to recover hbase:meta 1008 regionFs.writeRegionInfoOnFilesystem(false); 1009 } else { 1010 if (LOG.isDebugEnabled()) 1011 LOG.debug("Skipping creation of .regioninfo file for " + regionInfo); 1012 } 1013 return regionFs; 1014 } 1015 1016 /** 1017 * Open Region from file-system. 1018 * @param conf the {@link Configuration} to use 1019 * @param fs {@link FileSystem} from which to add the region 1020 * @param tableDir {@link Path} to where the table is being stored 1021 * @param regionInfo {@link RegionInfo} for region to be added 1022 * @param readOnly True if you don't want to edit the region data 1023 * @throws IOException if the region creation fails due to a FileSystem exception. 1024 */ 1025 public static HRegionFileSystem openRegionFromFileSystem(final Configuration conf, 1026 final FileSystem fs, final Path tableDir, final RegionInfo regionInfo, boolean readOnly) 1027 throws IOException { 1028 HRegionFileSystem regionFs = new HRegionFileSystem(conf, fs, tableDir, regionInfo); 1029 Path regionDir = regionFs.getRegionDir(); 1030 1031 if (!fs.exists(regionDir)) { 1032 LOG.warn("Trying to open a region that do not exists on disk: " + regionDir); 1033 throw new IOException("The specified region do not exists on disk: " + regionDir); 1034 } 1035 1036 if (!readOnly) { 1037 // Cleanup temporary directories 1038 regionFs.cleanupTempDir(); 1039 regionFs.cleanupSplitsDir(); 1040 regionFs.cleanupMergesDir(); 1041 1042 // If it doesn't exists, Write HRI to a file, in case we need to recover hbase:meta 1043 // Only create HRI if we are the default replica 1044 if (regionInfo.getReplicaId() == RegionInfo.DEFAULT_REPLICA_ID) { 1045 regionFs.checkRegionInfoOnFilesystem(); 1046 } else { 1047 if (LOG.isDebugEnabled()) { 1048 LOG.debug("Skipping creation of .regioninfo file for " + regionInfo); 1049 } 1050 } 1051 } 1052 1053 return regionFs; 1054 } 1055 1056 /** 1057 * Remove the region from the table directory, archiving the region's hfiles. 1058 * @param conf the {@link Configuration} to use 1059 * @param fs {@link FileSystem} from which to remove the region 1060 * @param tableDir {@link Path} to where the table is being stored 1061 * @param regionInfo {@link RegionInfo} for region to be deleted 1062 * @throws IOException if the request cannot be completed 1063 */ 1064 public static void deleteRegionFromFileSystem(final Configuration conf, 1065 final FileSystem fs, final Path tableDir, final RegionInfo regionInfo) throws IOException { 1066 HRegionFileSystem regionFs = new HRegionFileSystem(conf, fs, tableDir, regionInfo); 1067 Path regionDir = regionFs.getRegionDir(); 1068 1069 if (!fs.exists(regionDir)) { 1070 LOG.warn("Trying to delete a region that do not exists on disk: " + regionDir); 1071 return; 1072 } 1073 1074 if (LOG.isDebugEnabled()) { 1075 LOG.debug("DELETING region " + regionDir); 1076 } 1077 1078 // Archive region 1079 Path rootDir = CommonFSUtils.getRootDir(conf); 1080 HFileArchiver.archiveRegion(fs, rootDir, tableDir, regionDir); 1081 1082 // Delete empty region dir 1083 if (!fs.delete(regionDir, true)) { 1084 LOG.warn("Failed delete of " + regionDir); 1085 } 1086 } 1087 1088 /** 1089 * Creates a directory. Assumes the user has already checked for this directory existence. 1090 * @param dir 1091 * @return the result of fs.mkdirs(). In case underlying fs throws an IOException, it checks 1092 * whether the directory exists or not, and returns true if it exists. 1093 * @throws IOException 1094 */ 1095 boolean createDir(Path dir) throws IOException { 1096 int i = 0; 1097 IOException lastIOE = null; 1098 do { 1099 try { 1100 return mkdirs(fs, conf, dir); 1101 } catch (IOException ioe) { 1102 lastIOE = ioe; 1103 if (fs.exists(dir)) return true; // directory is present 1104 try { 1105 sleepBeforeRetry("Create Directory", i+1); 1106 } catch (InterruptedException e) { 1107 throw (InterruptedIOException)new InterruptedIOException().initCause(e); 1108 } 1109 } 1110 } while (++i <= hdfsClientRetriesNumber); 1111 throw new IOException("Exception in createDir", lastIOE); 1112 } 1113 1114 /** 1115 * Renames a directory. Assumes the user has already checked for this directory existence. 1116 * @param srcpath 1117 * @param dstPath 1118 * @return true if rename is successful. 1119 * @throws IOException 1120 */ 1121 boolean rename(Path srcpath, Path dstPath) throws IOException { 1122 IOException lastIOE = null; 1123 int i = 0; 1124 do { 1125 try { 1126 return fs.rename(srcpath, dstPath); 1127 } catch (IOException ioe) { 1128 lastIOE = ioe; 1129 if (!fs.exists(srcpath) && fs.exists(dstPath)) return true; // successful move 1130 // dir is not there, retry after some time. 1131 try { 1132 sleepBeforeRetry("Rename Directory", i+1); 1133 } catch (InterruptedException e) { 1134 throw (InterruptedIOException)new InterruptedIOException().initCause(e); 1135 } 1136 } 1137 } while (++i <= hdfsClientRetriesNumber); 1138 1139 throw new IOException("Exception in rename", lastIOE); 1140 } 1141 1142 /** 1143 * Deletes a directory. Assumes the user has already checked for this directory existence. 1144 * @param dir 1145 * @return true if the directory is deleted. 1146 * @throws IOException 1147 */ 1148 boolean deleteDir(Path dir) throws IOException { 1149 IOException lastIOE = null; 1150 int i = 0; 1151 do { 1152 try { 1153 return fs.delete(dir, true); 1154 } catch (IOException ioe) { 1155 lastIOE = ioe; 1156 if (!fs.exists(dir)) return true; 1157 // dir is there, retry deleting after some time. 1158 try { 1159 sleepBeforeRetry("Delete Directory", i+1); 1160 } catch (InterruptedException e) { 1161 throw (InterruptedIOException)new InterruptedIOException().initCause(e); 1162 } 1163 } 1164 } while (++i <= hdfsClientRetriesNumber); 1165 1166 throw new IOException("Exception in DeleteDir", lastIOE); 1167 } 1168 1169 /** 1170 * sleeping logic; handles the interrupt exception. 1171 */ 1172 private void sleepBeforeRetry(String msg, int sleepMultiplier) throws InterruptedException { 1173 sleepBeforeRetry(msg, sleepMultiplier, baseSleepBeforeRetries, hdfsClientRetriesNumber); 1174 } 1175 1176 /** 1177 * Creates a directory for a filesystem and configuration object. Assumes the user has already 1178 * checked for this directory existence. 1179 * @param fs 1180 * @param conf 1181 * @param dir 1182 * @return the result of fs.mkdirs(). In case underlying fs throws an IOException, it checks 1183 * whether the directory exists or not, and returns true if it exists. 1184 * @throws IOException 1185 */ 1186 private static boolean createDirOnFileSystem(FileSystem fs, Configuration conf, Path dir) 1187 throws IOException { 1188 int i = 0; 1189 IOException lastIOE = null; 1190 int hdfsClientRetriesNumber = conf.getInt("hdfs.client.retries.number", 1191 DEFAULT_HDFS_CLIENT_RETRIES_NUMBER); 1192 int baseSleepBeforeRetries = conf.getInt("hdfs.client.sleep.before.retries", 1193 DEFAULT_BASE_SLEEP_BEFORE_RETRIES); 1194 do { 1195 try { 1196 return fs.mkdirs(dir); 1197 } catch (IOException ioe) { 1198 lastIOE = ioe; 1199 if (fs.exists(dir)) return true; // directory is present 1200 try { 1201 sleepBeforeRetry("Create Directory", i+1, baseSleepBeforeRetries, hdfsClientRetriesNumber); 1202 } catch (InterruptedException e) { 1203 throw (InterruptedIOException)new InterruptedIOException().initCause(e); 1204 } 1205 } 1206 } while (++i <= hdfsClientRetriesNumber); 1207 1208 throw new IOException("Exception in createDir", lastIOE); 1209 } 1210 1211 /** 1212 * sleeping logic for static methods; handles the interrupt exception. Keeping a static version 1213 * for this to avoid re-looking for the integer values. 1214 */ 1215 private static void sleepBeforeRetry(String msg, int sleepMultiplier, int baseSleepBeforeRetries, 1216 int hdfsClientRetriesNumber) throws InterruptedException { 1217 if (sleepMultiplier > hdfsClientRetriesNumber) { 1218 if (LOG.isDebugEnabled()) { 1219 LOG.debug(msg + ", retries exhausted"); 1220 } 1221 return; 1222 } 1223 if (LOG.isDebugEnabled()) { 1224 LOG.debug(msg + ", sleeping " + baseSleepBeforeRetries + " times " + sleepMultiplier); 1225 } 1226 Thread.sleep((long)baseSleepBeforeRetries * sleepMultiplier); 1227 } 1228}