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.mob;
019
020import java.util.concurrent.ConcurrentHashMap;
021import java.util.concurrent.ConcurrentMap;
022import org.apache.hadoop.fs.FileStatus;
023import org.apache.hadoop.fs.Path;
024import org.apache.hadoop.hbase.HBaseInterfaceAudience;
025import org.apache.hadoop.hbase.TableName;
026import org.apache.hadoop.hbase.master.cleaner.BaseHFileCleanerDelegate;
027import org.apache.hadoop.hbase.util.CommonFSUtils;
028import org.apache.yetus.audience.InterfaceAudience;
029import org.slf4j.Logger;
030import org.slf4j.LoggerFactory;
031
032/**
033 * {@link BaseHFileCleanerDelegate} that prevents cleaning HFiles from a mob region
034 *
035 * keeps a map of table name strings to mob region name strings over the life of
036 * a JVM instance. if there's churn of unique table names we'll eat memory until
037 * Master restart.
038 */
039@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.CONFIG)
040public class ManualMobMaintHFileCleaner extends BaseHFileCleanerDelegate {
041
042  private static final Logger LOG = LoggerFactory.getLogger(ManualMobMaintHFileCleaner.class);
043
044  // We need to avoid making HRegionInfo objects for every table we check.
045  private static final ConcurrentMap<TableName, String> MOB_REGIONS = new ConcurrentHashMap<>();
046
047  @Override
048  public boolean isFileDeletable(FileStatus fStat) {
049    try {
050      // if its a directory, then it can be deleted
051      if (fStat.isDirectory()) {
052        return true;
053      }
054
055      Path file = fStat.getPath();
056
057      // we need the table and region to determine if this is from a mob region
058      // we don't need to worry about hfilelink back references, because the hfilelink cleaner will
059      // retain them.
060      Path family = file.getParent();
061      Path region = family.getParent();
062      Path table = region.getParent();
063
064      TableName tableName = CommonFSUtils.getTableName(table);
065
066      String mobRegion = MOB_REGIONS.get(tableName);
067      if (mobRegion == null) {
068        String tmp = MobUtils.getMobRegionInfo(tableName).getEncodedName();
069        if (tmp == null) {
070          LOG.error("couldn't determine mob region for table {} keeping files just in case.",
071              tableName);
072          return false;
073        }
074        mobRegion = MOB_REGIONS.putIfAbsent(tableName, tmp);
075        // a return of null means that tmp is now in the map for future lookups.
076        if (mobRegion == null) {
077          mobRegion = tmp;
078        }
079        LOG.debug("Had to calculate name of mob region for table {} and it is {}", tableName,
080            mobRegion);
081      }
082
083      boolean ret = !mobRegion.equals(region.getName());
084      if (LOG.isDebugEnabled() && !ret) {
085        LOG.debug("Keeping file '{}' because it is from mob dir", fStat.getPath());
086      }
087      return ret;
088    } catch (RuntimeException e) {
089      LOG.error("Failed to determine mob status of '{}', keeping it just in case.", fStat.getPath(),
090          e);
091      return false;
092    }
093  }
094
095}