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