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