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;
021
022import org.apache.hadoop.conf.Configuration;
023import org.apache.hadoop.fs.FileStatus;
024import org.apache.hadoop.fs.FileSystem;
025import org.apache.hadoop.fs.Path;
026import org.apache.hadoop.hbase.HBaseInterfaceAudience;
027import org.apache.hadoop.hbase.HConstants;
028import org.apache.yetus.audience.InterfaceAudience;
029import org.slf4j.Logger;
030import org.slf4j.LoggerFactory;
031import org.apache.hadoop.hbase.io.HFileLink;
032import org.apache.hadoop.hbase.mob.MobUtils;
033import org.apache.hadoop.hbase.util.FSUtils;
034
035/**
036 * HFileLink cleaner that determines if a hfile should be deleted.
037 * HFiles can be deleted only if there're no links to them.
038 *
039 * When a HFileLink is created a back reference file is created in:
040 *      /hbase/archive/table/region/cf/.links-hfile/ref-region.ref-table
041 * To check if the hfile can be deleted the back references folder must be empty.
042 */
043@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG)
044public class HFileLinkCleaner extends BaseHFileCleanerDelegate {
045  private static final Logger LOG = LoggerFactory.getLogger(HFileLinkCleaner.class);
046
047  private FileSystem fs = null;
048
049  @Override
050  public synchronized boolean isFileDeletable(FileStatus fStat) {
051    if (this.fs == null) return false;
052    Path filePath = fStat.getPath();
053    // HFile Link is always deletable
054    if (HFileLink.isHFileLink(filePath)) return true;
055
056    // If the file is inside a link references directory, means that it is a back ref link.
057    // The back ref can be deleted only if the referenced file doesn't exists.
058    Path parentDir = filePath.getParent();
059    if (HFileLink.isBackReferencesDir(parentDir)) {
060      Path hfilePath = null;
061      try {
062        // Also check if the HFile is in the HBASE_TEMP_DIRECTORY; this is where the referenced
063        // file gets created when cloning a snapshot.
064        hfilePath = HFileLink.getHFileFromBackReference(
065            new Path(FSUtils.getRootDir(getConf()), HConstants.HBASE_TEMP_DIRECTORY), filePath);
066        if (fs.exists(hfilePath)) {
067          return false;
068        }
069        // check whether the HFileLink still exists in mob dir.
070        hfilePath = HFileLink.getHFileFromBackReference(MobUtils.getMobHome(getConf()), filePath);
071        if (fs.exists(hfilePath)) {
072          return false;
073        }
074        hfilePath = HFileLink.getHFileFromBackReference(FSUtils.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 FSUtils.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}