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         // DFS leaves safe mode with 0 DNs when there are 0 blocks.
473         // We used to handle this by checking the current DN count and waiting until
474         // it is nonzero. With security, the check for datanode count doesn't work --
475         // it is a privileged op. So instead we adopt the strategy of the jobtracker
476         // and simply retry file creation during bootstrap indefinitely. As soon as
477         // there is one datanode it will succeed. Permission problems should have
478         // already been caught by mkdirs above.
479         FSUtils.setVersion(fs, rd, c.getInt(HConstants.THREAD_WAKE_FREQUENCY,
480           10 * 1000), c.getInt(HConstants.VERSION_FILE_WRITE_ATTEMPTS,
481             HConstants.DEFAULT_VERSION_FILE_WRITE_ATTEMPTS));
482       } else {
483         if (!fs.isDirectory(rd)) {
484           throw new IllegalArgumentException(rd.toString() + " is not a directory");
485         }
486         if (isSecurityEnabled && !dirPerms.equals(fs.getFileStatus(rd).getPermission())) {
487           // check whether the permission match
488           LOG.warn("Found rootdir permissions NOT matching expected \"" + dirPermsConfName + "\" for "
489               + "rootdir=" + rd.toString() + " permissions=" + fs.getFileStatus(rd).getPermission()
490               + " and  \"" + dirPermsConfName + "\" configured as "
491               + c.get(dirPermsConfName, "700") + ". Automatically setting the permissions. You"
492               + " can change the permissions by setting \"" + dirPermsConfName + "\" in hbase-site.xml "
493               + "and restarting the master");
494           fs.setPermission(rd, dirPerms);
495         }
496         // as above
497         FSUtils.checkVersion(fs, rd, true, c.getInt(HConstants.THREAD_WAKE_FREQUENCY,
498           10 * 1000), c.getInt(HConstants.VERSION_FILE_WRITE_ATTEMPTS,
499             HConstants.DEFAULT_VERSION_FILE_WRITE_ATTEMPTS));
500       }
501     } catch (DeserializationException de) {
502       LOG.fatal("Please fix invalid configuration for " + dirConfKey, de);
503       IOException ioe = new IOException();
504       ioe.initCause(de);
505       throw ioe;
506     } catch (IllegalArgumentException iae) {
507       LOG.fatal("Please fix invalid configuration for "
508         + dirConfKey + " " + rd.toString(), iae);
509       throw iae;
510     }
511 
512     if (dirConfKey.equals(HConstants.HBASE_DIR)) {
513       // Make sure cluster ID exists
514       if (!FSUtils.checkClusterIdExists(fs, rd, c.getInt(
515           HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000))) {
516         FSUtils.setClusterId(fs, rd, new ClusterId(), c.getInt(HConstants.THREAD_WAKE_FREQUENCY, 10 * 1000));
517       }
518       clusterId = FSUtils.getClusterId(fs, rd);
519 
520       // Make sure the meta region directory exists!
521       if (!FSUtils.metaRegionExists(fs, rd)) {
522         bootstrap(rd, c);
523       } else {
524         // Migrate table descriptor files if necessary
525         org.apache.hadoop.hbase.util.FSTableDescriptorMigrationToSubdir
526             .migrateFSTableDescriptorsIfNecessary(fs, rd);
527       }
528 
529       // Create tableinfo-s for hbase:meta if not already there.
530 
531       // meta table is a system table, so descriptors are predefined,
532       // we should get them from registry.
533       FSTableDescriptors fsd = new FSTableDescriptors(c, fs, rd);
534       fsd.createTableDescriptor(
535           new HTableDescriptor(fsd.get(TableName.META_TABLE_NAME)));
536     }
537 
538     return rd;
539   }
540 
541   /**
542    * Make sure the hbase temp directory exists and is empty.
543    * NOTE that this method is only executed once just after the master becomes the active one.
544    */
545   private void checkTempDir(final Path tmpdir, final Configuration c, final FileSystem fs)
546       throws IOException {
547     // If the temp directory exists, clear the content (left over, from the previous run)
548     if (fs.exists(tmpdir)) {
549       // Archive table in temp, maybe left over from failed deletion,
550       // if not the cleaner will take care of them.
551       for (Path tabledir: FSUtils.getTableDirs(fs, tmpdir)) {
552         for (Path regiondir: FSUtils.getRegionDirs(fs, tabledir)) {
553           HFileArchiver.archiveRegion(fs, this.rootdir, tabledir, regiondir);
554         }
555       }
556       if (!fs.delete(tmpdir, true)) {
557         throw new IOException("Unable to clean the temp directory: " + tmpdir);
558       }
559     }
560 
561     // Create the temp directory
562     if (!fs.mkdirs(tmpdir)) {
563       throw new IOException("HBase temp directory '" + tmpdir + "' creation failure.");
564     }
565   }
566 
567   private static void bootstrap(final Path rd, final Configuration c)
568   throws IOException {
569     LOG.info("BOOTSTRAP: creating hbase:meta region");
570     try {
571       // Bootstrapping, make sure blockcache is off.  Else, one will be
572       // created here in bootstrap and it'll need to be cleaned up.  Better to
573       // not make it in first place.  Turn off block caching for bootstrap.
574       // Enable after.
575       HRegionInfo metaHRI = new HRegionInfo(HRegionInfo.FIRST_META_REGIONINFO);
576       HTableDescriptor metaDescriptor = new FSTableDescriptors(c).get(TableName.META_TABLE_NAME);
577       setInfoFamilyCachingForMeta(metaDescriptor, false);
578       HRegion meta = HRegion.createHRegion(metaHRI, rd, c, metaDescriptor, null, true, true);
579       setInfoFamilyCachingForMeta(metaDescriptor, true);
580       HRegion.closeHRegion(meta);
581     } catch (IOException e) {
582       e = RemoteExceptionHandler.checkIOException(e);
583       LOG.error("bootstrap", e);
584       throw e;
585     }
586   }
587 
588   /**
589    * Enable in memory caching for hbase:meta
590    */
591   public static void setInfoFamilyCachingForMeta(final HTableDescriptor metaDescriptor,
592       final boolean b) {
593     for (HColumnDescriptor hcd: metaDescriptor.getColumnFamilies()) {
594       if (Bytes.equals(hcd.getName(), HConstants.CATALOG_FAMILY)) {
595         hcd.setBlockCacheEnabled(b);
596         hcd.setInMemory(b);
597       }
598     }
599   }
600 
601   public void deleteFamilyFromFS(HRegionInfo region, byte[] familyName)
602       throws IOException {
603     // archive family store files
604     Path tableDir = FSUtils.getTableDir(rootdir, region.getTable());
605     HFileArchiver.archiveFamily(fs, conf, region, tableDir, familyName);
606 
607     // delete the family folder
608     Path familyDir = new Path(tableDir,
609       new Path(region.getEncodedName(), Bytes.toString(familyName)));
610     if (fs.delete(familyDir, true) == false) {
611       if (fs.exists(familyDir)) {
612         throw new IOException("Could not delete family "
613             + Bytes.toString(familyName) + " from FileSystem for region "
614             + region.getRegionNameAsString() + "(" + region.getEncodedName()
615             + ")");
616       }
617     }
618   }
619 
620   public void stop() {
621     if (splitLogManager != null) {
622       this.splitLogManager.stop();
623     }
624   }
625 
626   /**
627    * The function is used in SSH to set recovery mode based on configuration after all outstanding
628    * log split tasks drained.
629    * @throws IOException
630    */
631   public void setLogRecoveryMode() throws IOException {
632       this.splitLogManager.setRecoveryMode(false);
633   }
634 
635   public RecoveryMode getLogRecoveryMode() {
636     return this.splitLogManager.getRecoveryMode();
637   }
638 
639   public void logFileSystemState(Log log) throws IOException {
640     FSUtils.logFileSystemState(fs, rootdir, log);
641   }
642 
643   /**
644    * For meta region open and closed normally on a server, it may leave some meta
645    * WAL in the server's wal dir. Since meta region is no long on this server,
646    * The SCP won't split those meta wals, just leaving them there. So deleting
647    * the wal dir will fail since the dir is not empty. Actually We can safely achive those
648    * meta log and Archiving the meta log and delete the dir.
649    * @param serverName the server to archive meta log
650    */
651   public void archiveMetaLog(final ServerName serverName) {
652     try {
653       Path logDir = new Path(this.rootdir,
654           DefaultWALProvider.getWALDirectoryName(serverName.toString()));
655       Path splitDir = logDir.suffix(DefaultWALProvider.SPLITTING_EXT);
656       if (fs.exists(splitDir)) {
657         FileStatus[] logfiles = FSUtils.listStatus(fs, splitDir, META_FILTER);
658         if (logfiles != null) {
659           for (FileStatus status : logfiles) {
660             if (!status.isDir()) {
661               Path newPath = DefaultWALProvider.getWALArchivePath(this.oldLogDir,
662                   status.getPath());
663               if (!FSUtils.renameAndSetModifyTime(fs, status.getPath(), newPath)) {
664                 LOG.warn("Unable to move  " + status.getPath() + " to " + newPath);
665               } else {
666                 LOG.debug("Archived meta log " + status.getPath() + " to " + newPath);
667               }
668             }
669           }
670         }
671         if (!fs.delete(splitDir, false)) {
672           LOG.warn("Unable to delete log dir. Ignoring. " + splitDir);
673         }
674       }
675     } catch (IOException ie) {
676       LOG.warn("Failed archiving meta log for server " + serverName, ie);
677     }
678   }
679 
680 
681 }