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.master.cleaner;
019
020import java.io.IOException;
021import org.apache.hadoop.conf.Configuration;
022import org.apache.hadoop.fs.FileStatus;
023import org.apache.hadoop.fs.FileSystem;
024import org.apache.hadoop.fs.Path;
025import org.apache.hadoop.hbase.HBaseInterfaceAudience;
026import org.apache.hadoop.hbase.HConstants;
027import org.apache.hadoop.hbase.io.HFileLink;
028import org.apache.hadoop.hbase.mob.MobUtils;
029import org.apache.hadoop.hbase.util.CommonFSUtils;
030import org.apache.yetus.audience.InterfaceAudience;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034/**
035 * HFileLink cleaner that determines if a hfile should be deleted.
036 * HFiles can be deleted only if there're no links to them.
037 *
038 * When a HFileLink is created a back reference file is created in:
039 *      /hbase/archive/table/region/cf/.links-hfile/ref-region.ref-table
040 * To check if the hfile can be deleted the back references folder must be empty.
041 */
042@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG)
043public class HFileLinkCleaner extends BaseHFileCleanerDelegate {
044  private static final Logger LOG = LoggerFactory.getLogger(HFileLinkCleaner.class);
045
046  private FileSystem fs = null;
047
048  @Override
049  public synchronized boolean isFileDeletable(FileStatus fStat) {
050    if (this.fs == null) return false;
051    Path filePath = fStat.getPath();
052    // HFile Link is always deletable
053    if (HFileLink.isHFileLink(filePath)) return true;
054
055    // If the file is inside a link references directory, means that it is a back ref link.
056    // The back ref can be deleted only if the referenced file doesn't exists.
057    Path parentDir = filePath.getParent();
058    if (HFileLink.isBackReferencesDir(parentDir)) {
059      Path hfilePath = null;
060      try {
061        // Also check if the HFile is in the HBASE_TEMP_DIRECTORY; this is where the referenced
062        // file gets created when cloning a snapshot.
063        hfilePath = HFileLink.getHFileFromBackReference(
064          new Path(CommonFSUtils.getRootDir(getConf()), HConstants.HBASE_TEMP_DIRECTORY), filePath);
065        if (fs.exists(hfilePath)) {
066          return false;
067        }
068        // check whether the HFileLink still exists in mob dir.
069        hfilePath = HFileLink.getHFileFromBackReference(MobUtils.getMobHome(getConf()), filePath);
070        if (fs.exists(hfilePath)) {
071          return false;
072        }
073        hfilePath =
074          HFileLink.getHFileFromBackReference(CommonFSUtils.getRootDir(getConf()), filePath);
075        return !fs.exists(hfilePath);
076      } catch (IOException e) {
077        if (LOG.isDebugEnabled()) {
078          LOG.debug("Couldn't verify if the referenced file still exists, keep it just in case: " +
079            hfilePath);
080        }
081        return false;
082      }
083    }
084
085    // HFile is deletable only if has no links
086    Path backRefDir = null;
087    try {
088      backRefDir = HFileLink.getBackReferencesDir(parentDir, filePath.getName());
089      return CommonFSUtils.listStatus(fs, backRefDir) == null;
090    } catch (IOException e) {
091      if (LOG.isDebugEnabled()) {
092        LOG.debug("Couldn't get the references, not deleting file, just in case. filePath="
093            + filePath + ", backRefDir=" + backRefDir);
094      }
095      return false;
096    }
097  }
098
099  @Override
100  public synchronized void setConf(Configuration conf) {
101    super.setConf(conf);
102
103    // setup filesystem
104    try {
105      this.fs = FileSystem.get(this.getConf());
106    } catch (IOException e) {
107      if (LOG.isDebugEnabled()) {
108        LOG.debug("Couldn't instantiate the file system, not deleting file, just in case. "
109            + FileSystem.FS_DEFAULT_NAME_KEY + "="
110            + getConf().get(FileSystem.FS_DEFAULT_NAME_KEY, FileSystem.DEFAULT_FS));
111      }
112    }
113  }
114}