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.client;
019
020import static org.apache.hadoop.hbase.client.ConnectionUtils.isEmptyStopRow;
021
022import java.util.Map;
023import java.util.concurrent.ConcurrentNavigableMap;
024import org.apache.hadoop.hbase.RegionLocations;
025import org.apache.hadoop.hbase.util.Bytes;
026import org.apache.yetus.audience.InterfaceAudience;
027import org.slf4j.Logger;
028import org.slf4j.LoggerFactory;
029
030/**
031 * Util class to DRY common logic between AsyncRegionLocationCache and MetaCache
032 */
033@InterfaceAudience.Private
034final class MetaCacheUtil {
035
036  private static final Logger LOG = LoggerFactory.getLogger(MetaCacheUtil.class);
037
038  private MetaCacheUtil() {
039  }
040
041  /**
042   * When caching a location, the region may have been the result of a merge. Check to see if the
043   * region's boundaries overlap any other cached locations in a problematic way. Those would have
044   * been merge parents which no longer exist. We need to proactively clear them out to avoid a case
045   * where a merged region which receives no requests never gets cleared. This causes requests to
046   * other merged regions after it to see the wrong cached location.
047   * <p>
048   * For example, if we have Start_New < Start_Old < End_Old < End_New, then if we only access
049   * within range [End_Old, End_New], then it will always return the old region but it will then
050   * find out the row is not in the range, and try to get the new region, and then we get
051   * [Start_New, End_New), still fall into the same situation.
052   * <p>
053   * If Start_Old is less than Start_New, even if we have overlap, it is not a problem, as when the
054   * row is greater than Start_New, we will locate to the new region, and if the row is less than
055   * Start_New, it will fall into the old region's range and we will try to access the region and
056   * get a NotServing exception, and then we will clean the cache.
057   * <p>
058   * See HBASE-27650
059   * @param locations the new location that was just cached
060   */
061  static void cleanProblematicOverlappedRegions(RegionLocations locations,
062    ConcurrentNavigableMap<byte[], RegionLocations> cache) {
063    RegionInfo region = locations.getRegionLocation().getRegion();
064
065    boolean isLast = isEmptyStopRow(region.getEndKey());
066
067    while (true) {
068      Map.Entry<byte[], RegionLocations> overlap =
069        isLast ? cache.lastEntry() : cache.lowerEntry(region.getEndKey());
070      if (
071        overlap == null || overlap.getValue() == locations
072          || Bytes.equals(overlap.getKey(), region.getStartKey())
073      ) {
074        break;
075      }
076
077      if (LOG.isDebugEnabled()) {
078        LOG.debug(
079          "Removing cached location {} (endKey={}) because it overlaps with "
080            + "new location {} (endKey={})",
081          overlap.getValue(),
082          Bytes.toStringBinary(overlap.getValue().getRegionLocation().getRegion().getEndKey()),
083          locations, Bytes.toStringBinary(locations.getRegionLocation().getRegion().getEndKey()));
084      }
085
086      cache.remove(overlap.getKey());
087    }
088  }
089}