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