View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.master;
20  
21  import java.io.IOException;
22  import java.io.InterruptedIOException;
23  import java.util.ArrayList;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Set;
27  import java.util.concurrent.locks.Lock;
28  import java.util.concurrent.locks.ReentrantLock;
29  
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  import org.apache.hadoop.hbase.classification.InterfaceAudience;
33  import org.apache.hadoop.conf.Configuration;
34  import org.apache.hadoop.fs.FileStatus;
35  import org.apache.hadoop.fs.FileSystem;
36  import org.apache.hadoop.fs.Path;
37  import org.apache.hadoop.fs.PathFilter;
38  import org.apache.hadoop.fs.permission.FsPermission;
39  import org.apache.hadoop.hbase.ClusterId;
40  import org.apache.hadoop.hbase.TableName;
41  import org.apache.hadoop.hbase.HColumnDescriptor;
42  import org.apache.hadoop.hbase.HConstants;
43  import org.apache.hadoop.hbase.HRegionInfo;
44  import org.apache.hadoop.hbase.HTableDescriptor;
45  import org.apache.hadoop.hbase.RemoteExceptionHandler;
46  import org.apache.hadoop.hbase.Server;
47  import org.apache.hadoop.hbase.ServerName;
48  import org.apache.hadoop.hbase.backup.HFileArchiver;
49  import org.apache.hadoop.hbase.exceptions.DeserializationException;
50  import org.apache.hadoop.hbase.fs.HFileSystem;
51  import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos.SplitLogTask.RecoveryMode;
52  import org.apache.hadoop.hbase.regionserver.HRegion;
53  import org.apache.hadoop.hbase.wal.DefaultWALProvider;
54  import org.apache.hadoop.hbase.wal.WALSplitter;
55  import org.apache.hadoop.hbase.util.Bytes;
56  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
57  import org.apache.hadoop.hbase.util.FSTableDescriptors;
58  import org.apache.hadoop.hbase.util.FSUtils;
59  
60  import com.google.common.annotations.VisibleForTesting;
61  
62  /**
63   * This class abstracts a bunch of operations the HMaster needs to interact with
64   * the underlying file system, including splitting log files, checking file
65   * system status, etc.
66   */
67  @InterfaceAudience.Private
68  public class MasterFileSystem {
69    private static final Log LOG = LogFactory.getLog(MasterFileSystem.class);
70  
71    /** Parameter name for HBase instance root directory permission*/
72    public static final String HBASE_DIR_PERMS = "hbase.rootdir.perms";
73  
74    /** Parameter name for HBase WAL directory permission*/
75    public static final String HBASE_WAL_DIR_PERMS = "hbase.wal.dir.perms";
76  
77    // HBase configuration
78    Configuration conf;
79    // master status
80    Server master;
81    // metrics for master
82    private final MetricsMasterFileSystem metricsMasterFilesystem = new MetricsMasterFileSystem();
83    // Persisted unique cluster ID
84    private ClusterId clusterId;
85    // Keep around for convenience.
86    private final FileSystem fs;
87    private final FileSystem walFs;
88    // root WAL directory
89    private final Path walRootDir;
90    // Is the fileystem ok?
91    private volatile boolean walFsOk = true;
92    // The Path to the old logs dir
93    private final Path oldLogDir;
94    // root hbase directory on the FS
95    private final Path rootdir;
96    // hbase temp directory used for table construction and deletion
97    private final Path tempdir;
98    // create the split log lock
99    final Lock splitLogLock = new ReentrantLock();
100   final boolean distributedLogReplay;
101   final SplitLogManager splitLogManager;
102   private final MasterServices services;
103 
104   final static PathFilter META_FILTER = new PathFilter() {
105     @Override
106     public boolean accept(Path p) {
107       return DefaultWALProvider.isMetaFile(p);
108     }
109   };
110 
111   final static PathFilter NON_META_FILTER = new PathFilter() {
112     @Override
113     public boolean accept(Path p) {
114       return !DefaultWALProvider.isMetaFile(p);
115     }
116   };
117 
118   public MasterFileSystem(Server master, MasterServices services)
119   throws IOException {
120     this.conf = master.getConfiguration();
121     this.master = master;
122     this.services = services;
123     // Set filesystem to be that of this.rootdir else we get complaints about
124     // mismatched filesystems if hbase.rootdir is hdfs and fs.defaultFS is
125     // default localfs.  Presumption is that rootdir is fully-qualified before
126     // we get to here with appropriate fs scheme.
127     this.rootdir = FSUtils.getRootDir(conf);
128     this.tempdir = new Path(this.rootdir, HConstants.HBASE_TEMP_DIRECTORY);
129     // Cover both bases, the old way of setting default fs and the new.
130     // We're supposed to run on 0.20 and 0.21 anyways.
131     this.fs = this.rootdir.getFileSystem(conf);
132     this.walRootDir = FSUtils.getWALRootDir(conf);
133     this.walFs = FSUtils.getWALFileSystem(conf);
134     FSUtils.setFsDefault(conf, new Path(this.walFs.getUri()));
135     walFs.setConf(conf);
136     FSUtils.setFsDefault(conf, new Path(this.fs.getUri()));
137     // make sure the fs has the same conf
138     fs.setConf(conf);
139     // setup the filesystem variable
140     // set up the archived logs path
141     this.oldLogDir = createInitialFileSystemLayout();
142     HFileSystem.addLocationsOrderInterceptor(conf);
143     this.splitLogManager =
144         new SplitLogManager(master, master.getConfiguration(), master, services,
145             master.getServerName());
146     this.distributedLogReplay = this.splitLogManager.isLogReplaying();
147   }
148 
149   @VisibleForTesting
150   SplitLogManager getSplitLogManager() {
151     return this.splitLogManager;
152   }
153 
154   /**
155    * Create initial layout in filesystem.
156    * <ol>
157    * <li>Check if the meta region exists and is readable, if not create it.
158    * Create hbase.version and the hbase:meta directory if not one.
159    * </li>
160    * <li>Create a log archive directory for RS to put archived logs</li>
161    * </ol>
162    * Idempotent.
163    */
164   private Path createInitialFileSystemLayout() throws IOException {
165 
166     checkRootDir(this.rootdir, conf, this.fs, HConstants.HBASE_DIR, HBASE_DIR_PERMS);
167     // if the log directory is different from root, check if it exists
168     if (!this.walRootDir.equals(this.rootdir)) {
169       checkRootDir(this.walRootDir, conf, this.walFs, HFileSystem.HBASE_WAL_DIR, HBASE_WAL_DIR_PERMS);
170     }
171 
172     // check if temp directory exists and clean it
173     checkTempDir(this.tempdir, conf, this.fs);
174 
175     Path oldLogDir = new Path(this.walRootDir, HConstants.HREGION_OLDLOGDIR_NAME);
176 
177     // Make sure the region servers can archive their old logs
178     if(!this.walFs.exists(oldLogDir)) {
179       this.walFs.mkdirs(oldLogDir);
180     }
181 
182     return oldLogDir;
183   }
184 
185   public FileSystem getFileSystem() {
186     return this.fs;
187   }
188 
189   /**
190    * Get the directory where old logs go
191    * @return the dir
192    */
193   public Path getOldLogDir() {
194     return this.oldLogDir;
195   }
196 
197   /**
198    * Checks to see if the file system is still accessible.
199    * If not, sets closed
200    * @return false if file system is not available
201    */
202   public boolean checkFileSystem() {
203     if (this.walFsOk) {
204       try {
205         FSUtils.checkFileSystemAvailable(this.walFs);
206         FSUtils.checkDfsSafeMode(this.conf);
207       } catch (IOException e) {
208         master.abort("Shutting down HBase cluster: file system not available", e);
209         this.walFsOk = false;
210       }
211     }
212     return this.walFsOk;
213   }
214 
215   protected FileSystem getWALFileSystem() {
216     return this.walFs;
217   }
218 
219   public Configuration getConfiguration() {
220     return this.conf;
221   }
222 
223   /**
224    * @return HBase root dir.
225    */
226   public Path getRootDir() {
227     return this.rootdir;
228   }
229 
230   /**
231    * @return HBase root log dir.
232    */
233   public Path getWALRootDir() { return this.walRootDir; }
234 
235   /**
236    * @return HBase temp dir.
237    */
238   public Path getTempDir() {
239     return this.tempdir;
240   }
241 
242   /**
243    * @return The unique identifier generated for this cluster
244    */
245   public ClusterId getClusterId() {
246     return clusterId;
247   }
248 
249   /**
250    * Inspect the log directory to find dead servers which need recovery work
251    * @return A set of ServerNames which aren't running but still have WAL files left in file system
252    */
253   Set<ServerName> getFailedServersFromLogFolders() {
254     boolean retrySplitting = !conf.getBoolean("hbase.hlog.split.skip.errors",
255         WALSplitter.SPLIT_SKIP_ERRORS_DEFAULT);
256 
257     Set<ServerName> serverNames = new HashSet<ServerName>();
258     Path logsDirPath = new Path(this.walRootDir, HConstants.HREGION_LOGDIR_NAME);
259 
260     do {
261       if (master.isStopped()) {
262         LOG.warn("Master stopped while trying to get failed servers.");
263         break;
264       }
265       try {
266         if (!this.walFs.exists(logsDirPath)) return serverNames;
267         FileStatus[] logFolders = FSUtils.listStatus(this.walFs, logsDirPath, null);
268         // Get online servers after getting log folders to avoid log folder deletion of newly
269         // checked in region servers . see HBASE-5916
270         Set<ServerName> onlineServers = ((HMaster) master).getServerManager().getOnlineServers()
271             .keySet();
272 
273         if (logFolders == null || logFolders.length == 0) {
274           LOG.debug("No log files to split, proceeding...");
275           return serverNames;
276         }
277         for (FileStatus status : logFolders) {
278           FileStatus[] curLogFiles = FSUtils.listStatus(this.walFs, status.getPath(), null);
279           if (curLogFiles == null || curLogFiles.length == 0) {
280             // Empty log folder. No recovery needed
281             continue;
282           }
283           final ServerName serverName = DefaultWALProvider.getServerNameFromWALDirectoryName(
284               status.getPath());
285           if (null == serverName) {
286             LOG.warn("Log folder " + status.getPath() + " doesn't look like its name includes a " +
287                 "region server name; leaving in place. If you see later errors about missing " +
288                 "write ahead logs they may be saved in this location.");
289           } else if (!onlineServers.contains(serverName)) {
290             LOG.info("Log folder " + status.getPath() + " doesn't belong "
291                 + "to a known region server, splitting");
292             serverNames.add(serverName);
293           } else {
294             LOG.info("Log folder " + status.getPath() + " belongs to an existing region server");
295           }
296         }
297         retrySplitting = false;
298       } catch (IOException ioe) {
299         LOG.warn("Failed getting failed servers to be recovered.", ioe);
300         if (!checkFileSystem()) {
301           LOG.warn("Bad Filesystem, exiting");
302           Runtime.getRuntime().halt(1);
303         }
304         try {
305           if (retrySplitting) {
306             Thread.sleep(conf.getInt("hbase.hlog.split.failure.retry.interval", 30 * 1000));
307           }
308         } catch (InterruptedException e) {
309           LOG.warn("Interrupted, aborting since cannot return w/o splitting");
310           Thread.currentThread().interrupt();
311           retrySplitting = false;
312           Runtime.getRuntime().halt(1);
313         }
314       }
315     } while (retrySplitting);
316 
317     return serverNames;
318   }
319 
320   public void splitLog(final ServerName serverName) throws IOException {
321     Set<ServerName> serverNames = new HashSet<ServerName>();
322     serverNames.add(serverName);
323     splitLog(serverNames);
324   }
325 
326   /**
327    * Specialized method to handle the splitting for meta WAL
328    * @param serverName
329    * @throws IOException
330    */
331   public void splitMetaLog(final ServerName serverName) throws IOException {
332     Set<ServerName> serverNames = new HashSet<ServerName>();
333     serverNames.add(serverName);
334     splitMetaLog(serverNames);
335   }
336 
337   /**
338    * Specialized method to handle the splitting for meta WAL
339    * @param serverNames
340    * @throws IOException
341    */
342   public void splitMetaLog(final Set<ServerName> serverNames) throws IOException {
343     splitLog(serverNames, META_FILTER);
344   }
345 
346   @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="UL_UNRELEASED_LOCK", justification=
347       "We only release this lock when we set it. Updates to code that uses it should verify use " +
348       "of the guard boolean.")
349   private List<Path> getLogDirs(final Set<ServerName> serverNames) throws IOException {
350     List<Path> logDirs = new ArrayList<Path>();
351     boolean needReleaseLock = false;
352     if (!this.services.isInitialized()) {
353       // during master initialization, we could have multiple places splitting a same wal
354       this.splitLogLock.lock();
355       needReleaseLock = true;
356     }
357     try {
358       for (ServerName serverName : serverNames) {
359         Path logDir = new Path(this.walRootDir,
360             DefaultWALProvider.getWALDirectoryName(serverName.toString()));
361         Path splitDir = logDir.suffix(DefaultWALProvider.SPLITTING_EXT);
362         // Rename the directory so a rogue RS doesn't create more WALs
363         if (walFs.exists(logDir)) {
364           if (!this.walFs.rename(logDir, splitDir)) {
365             throw new IOException("Failed fs.rename for log split: " + logDir);
366           }
367           logDir = splitDir;
368           LOG.debug("Renamed region directory: " + splitDir);
369         } else if (!walFs.exists(splitDir)) {
370           LOG.info("Log dir for server " + serverName + " does not exist");
371           continue;
372         }
373         logDirs.add(splitDir);
374       }
375     } catch (IOException ioe) {
376       if (!checkFileSystem()) {
377         this.services.abort("Aborting due to filesystem unavailable", ioe);
378         throw ioe;
379       }
380     } finally {
381       if (needReleaseLock) {
382         this.splitLogLock.unlock();
383       }
384     }
385     return logDirs;
386   }
387 
388   /**
389    * Mark regions in recovering state when distributedLogReplay are set true
390    * @param serverName Failed region server whose wals to be replayed
391    * @param regions Set of regions to be recovered
392    * @throws IOException
393    */
394   public void prepareLogReplay(ServerName serverName, Set<HRegionInfo> regions) throws IOException {
395     if (!this.distributedLogReplay) {
396       return;
397     }
398     // mark regions in recovering state
399     if (regions == null || regions.isEmpty()) {
400       return;
401     }
402     this.splitLogManager.markRegionsRecovering(serverName, regions);
403   }
404 
405   public void splitLog(final Set<ServerName> serverNames) throws IOException {
406     splitLog(serverNames, NON_META_FILTER);
407   }
408 
409   /**
410    * Wrapper function on {@link SplitLogManager#removeStaleRecoveringRegions(Set)}
411    * @param failedServers
412    * @throws IOException
413    */
414   void removeStaleRecoveringRegionsFromZK(final Set<ServerName> failedServers)
415       throws IOException, InterruptedIOException {
416     this.splitLogManager.removeStaleRecoveringRegions(failedServers);
417   }
418 
419   /**
420    * This method is the base split method that splits WAL files matching a filter. Callers should
421    * pass the appropriate filter for meta and non-meta WALs.
422    * @param serverNames logs belonging to these servers will be split; this will rename the log
423    *                    directory out from under a soft-failed server
424    * @param filter
425    * @throws IOException
426    */
427   public void splitLog(final Set<ServerName> serverNames, PathFilter filter) throws IOException {
428     long splitTime = 0, splitLogSize = 0;
429     List<Path> logDirs = getLogDirs(serverNames);
430 
431     splitLogManager.handleDeadWorkers(serverNames);
432     splitTime = EnvironmentEdgeManager.currentTime();
433     splitLogSize = splitLogManager.splitLogDistributed(serverNames, logDirs, filter);
434     splitTime = EnvironmentEdgeManager.currentTime() - splitTime;
435 
436     if (this.metricsMasterFilesystem != null) {
437       if (filter == META_FILTER) {
438         this.metricsMasterFilesystem.addMetaWALSplit(splitTime, splitLogSize);
439       } else {
440         this.metricsMasterFilesystem.addSplit(splitTime, splitLogSize);
441       }
442     }
443   }
444 
445   /**
446    * Get the rootdir.  Make sure its wholesome and exists before returning.
447    * @param rd
448    * @param c
449    * @param fs
450    * @return hbase.rootdir (after checks for existence and bootstrapping if
451    * needed populating the directory with necessary bootup files).
452    * @throws IOException
453    */
454   @SuppressWarnings("deprecation")
455   private Path checkRootDir(final Path rd, final Configuration c,
456     final FileSystem fs, final String dirConfKey, final String dirPermsConfName)
457   throws IOException {
458     // If FS is in safe mode wait till out of it.
459     FSUtils.waitOnSafeMode(c, c.getInt(HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000));
460 
461     boolean isSecurityEnabled = "kerberos".equalsIgnoreCase(c.get("hbase.security.authentication"));
462     FsPermission dirPerms = new FsPermission(c.get(dirPermsConfName, "700"));
463 
464     // Filesystem is good. Go ahead and check for rootdir.
465     try {
466       if (!fs.exists(rd)) {
467         if (isSecurityEnabled) {
468           fs.mkdirs(rd, dirPerms);
469         } else {
470           fs.mkdirs(rd);
471         }
472 
473         // HBASE-17437 updates createInitialFileSystemLayout() to re-use checkRootDir()
474         // to check hbase.wal.dir after checking hbase.rootdir.
475         // But FSUtils.setVersion() is supposed to be called only when checking hbase.rootdir,
476         // while it is supposed to be bypassed when checking hbase.wal.dir.
477         if (dirConfKey.equals(HConstants.HBASE_DIR)) {
478           // DFS leaves safe mode with 0 DNs when there are 0 blocks.
479           // We used to handle this by checking the current DN count and waiting until
480           // it is nonzero. With security, the check for datanode count doesn't work --
481           // it is a privileged op. So instead we adopt the strategy of the jobtracker
482           // and simply retry file creation during bootstrap indefinitely. As soon as
483           // there is one datanode it will succeed. Permission problems should have
484           // already been caught by mkdirs above.
485           FSUtils.setVersion(fs, rd,
486             c.getInt(HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000),
487             c.getInt(HConstants.VERSION_FILE_WRITE_ATTEMPTS, HConstants.DEFAULT_VERSION_FILE_WRITE_ATTEMPTS));
488         }
489       } else {
490         if (!fs.isDirectory(rd)) {
491           throw new IllegalArgumentException(rd.toString() + " is not a directory");
492         }
493         if (isSecurityEnabled && !dirPerms.equals(fs.getFileStatus(rd).getPermission())) {
494           // check whether the permission match
495           LOG.warn("Found rootdir permissions NOT matching expected \"" + dirPermsConfName + "\" for "
496               + "rootdir=" + rd.toString() + " permissions=" + fs.getFileStatus(rd).getPermission()
497               + " and  \"" + dirPermsConfName + "\" configured as "
498               + c.get(dirPermsConfName, "700") + ". Automatically setting the permissions. You"
499               + " can change the permissions by setting \"" + dirPermsConfName + "\" in hbase-site.xml "
500               + "and restarting the master");
501           fs.setPermission(rd, dirPerms);
502         }
503 
504         // HBASE-17437 updates createInitialFileSystemLayout() to re-use checkRootDir()
505         // to check hbase.wal.dir after checking hbase.rootdir.
506         // But FSUtils.checkVersion() is supposed to be called only when checking hbase.rootdir,
507         // while it is supposed to be bypassed when checking hbase.wal.dir.
508         if (dirConfKey.equals(HConstants.HBASE_DIR)) {
509           FSUtils.checkVersion(fs, rd, true,
510             c.getInt(HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000),
511             c.getInt(HConstants.VERSION_FILE_WRITE_ATTEMPTS, HConstants.DEFAULT_VERSION_FILE_WRITE_ATTEMPTS));
512         }
513       }
514     } catch (DeserializationException de) {
515       LOG.fatal("Please fix invalid configuration for " + dirConfKey, de);
516       IOException ioe = new IOException();
517       ioe.initCause(de);
518       throw ioe;
519     } catch (IllegalArgumentException iae) {
520       LOG.fatal("Please fix invalid configuration for "
521         + dirConfKey + " " + rd.toString(), iae);
522       throw iae;
523     }
524 
525     if (dirConfKey.equals(HConstants.HBASE_DIR)) {
526       // Make sure cluster ID exists
527       if (!FSUtils.checkClusterIdExists(fs, rd, c.getInt(
528           HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000))) {
529         FSUtils.setClusterId(fs, rd, new ClusterId(), c.getInt(HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000));
530       }
531       clusterId = FSUtils.getClusterId(fs, rd);
532 
533       // Make sure the meta region directory exists!
534       if (!FSUtils.metaRegionExists(fs, rd)) {
535         bootstrap(rd, c);
536       } else {
537         // Migrate table descriptor files if necessary
538         org.apache.hadoop.hbase.util.FSTableDescriptorMigrationToSubdir
539             .migrateFSTableDescriptorsIfNecessary(fs, rd);
540       }
541 
542       // Create tableinfo-s for hbase:meta if not already there.
543 
544       // meta table is a system table, so descriptors are predefined,
545       // we should get them from registry.
546       FSTableDescriptors fsd = new FSTableDescriptors(c, fs, rd);
547       fsd.createTableDescriptor(
548           new HTableDescriptor(fsd.get(TableName.META_TABLE_NAME)));
549     }
550 
551     return rd;
552   }
553 
554   /**
555    * Make sure the hbase temp directory exists and is empty.
556    * NOTE that this method is only executed once just after the master becomes the active one.
557    */
558   private void checkTempDir(final Path tmpdir, final Configuration c, final FileSystem fs)
559       throws IOException {
560     // If the temp directory exists, clear the content (left over, from the previous run)
561     if (fs.exists(tmpdir)) {
562       // Archive table in temp, maybe left over from failed deletion,
563       // if not the cleaner will take care of them.
564       for (Path tabledir: FSUtils.getTableDirs(fs, tmpdir)) {
565         for (Path regiondir: FSUtils.getRegionDirs(fs, tabledir)) {
566           HFileArchiver.archiveRegion(fs, this.rootdir, tabledir, regiondir);
567         }
568       }
569       if (!fs.delete(tmpdir, true)) {
570         throw new IOException("Unable to clean the temp directory: " + tmpdir);
571       }
572     }
573 
574     // Create the temp directory
575     if (!fs.mkdirs(tmpdir)) {
576       throw new IOException("HBase temp directory '" + tmpdir + "' creation failure.");
577     }
578   }
579 
580   private static void bootstrap(final Path rd, final Configuration c)
581   throws IOException {
582     LOG.info("BOOTSTRAP: creating hbase:meta region");
583     try {
584       // Bootstrapping, make sure blockcache is off.  Else, one will be
585       // created here in bootstrap and it'll need to be cleaned up.  Better to
586       // not make it in first place.  Turn off block caching for bootstrap.
587       // Enable after.
588       HRegionInfo metaHRI = new HRegionInfo(HRegionInfo.FIRST_META_REGIONINFO);
589       HTableDescriptor metaDescriptor = new FSTableDescriptors(c).get(TableName.META_TABLE_NAME);
590       setInfoFamilyCachingForMeta(metaDescriptor, false);
591       HRegion meta = HRegion.createHRegion(metaHRI, rd, c, metaDescriptor, null, true, true);
592       setInfoFamilyCachingForMeta(metaDescriptor, true);
593       HRegion.closeHRegion(meta);
594     } catch (IOException e) {
595       e = RemoteExceptionHandler.checkIOException(e);
596       LOG.error("bootstrap", e);
597       throw e;
598     }
599   }
600 
601   /**
602    * Enable in memory caching for hbase:meta
603    */
604   public static void setInfoFamilyCachingForMeta(final HTableDescriptor metaDescriptor,
605       final boolean b) {
606     for (HColumnDescriptor hcd: metaDescriptor.getColumnFamilies()) {
607       if (Bytes.equals(hcd.getName(), HConstants.CATALOG_FAMILY)) {
608         hcd.setBlockCacheEnabled(b);
609         hcd.setInMemory(b);
610       }
611     }
612   }
613 
614   public void deleteFamilyFromFS(HRegionInfo region, byte[] familyName)
615       throws IOException {
616     // archive family store files
617     Path tableDir = FSUtils.getTableDir(rootdir, region.getTable());
618     HFileArchiver.archiveFamily(fs, conf, region, tableDir, familyName);
619 
620     // delete the family folder
621     Path familyDir = new Path(tableDir,
622       new Path(region.getEncodedName(), Bytes.toString(familyName)));
623     if (fs.delete(familyDir, true) == false) {
624       if (fs.exists(familyDir)) {
625         throw new IOException("Could not delete family "
626             + Bytes.toString(familyName) + " from FileSystem for region "
627             + region.getRegionNameAsString() + "(" + region.getEncodedName()
628             + ")");
629       }
630     }
631   }
632 
633   public void stop() {
634     if (splitLogManager != null) {
635       this.splitLogManager.stop();
636     }
637   }
638 
639   /**
640    * The function is used in SSH to set recovery mode based on configuration after all outstanding
641    * log split tasks drained.
642    * @throws IOException
643    */
644   public void setLogRecoveryMode() throws IOException {
645       this.splitLogManager.setRecoveryMode(false);
646   }
647 
648   public RecoveryMode getLogRecoveryMode() {
649     return this.splitLogManager.getRecoveryMode();
650   }
651 
652   public void logFileSystemState(Log log) throws IOException {
653     FSUtils.logFileSystemState(fs, rootdir, log);
654   }
655 }