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.exceptions.ClientExceptionsUtil.findException;
021import static org.apache.hadoop.hbase.exceptions.ClientExceptionsUtil.isMetaClearingException;
022import java.util.Arrays;
023import java.util.function.Consumer;
024import java.util.function.Function;
025import org.apache.commons.lang3.ObjectUtils;
026import org.apache.hadoop.hbase.HRegionLocation;
027import org.apache.hadoop.hbase.RegionLocations;
028import org.apache.hadoop.hbase.exceptions.RegionMovedException;
029import org.apache.yetus.audience.InterfaceAudience;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033/**
034 * Helper class for asynchronous region locator.
035 */
036@InterfaceAudience.Private
037final class AsyncRegionLocatorHelper {
038
039  private static final Logger LOG = LoggerFactory.getLogger(AsyncRegionLocatorHelper.class);
040
041  private AsyncRegionLocatorHelper() {
042  }
043
044  static boolean canUpdateOnError(HRegionLocation loc, HRegionLocation oldLoc) {
045    // Do not need to update if no such location, or the location is newer, or the location is not
046    // the same with us
047    if (loc == null || loc.getServerName() == null) {
048      return false;
049    }
050    if (oldLoc == null || oldLoc.getServerName() == null) {
051      return false;
052    }
053    return oldLoc.getSeqNum() <= loc.getSeqNum() &&
054      oldLoc.getServerName().equals(loc.getServerName());
055  }
056
057  static void updateCachedLocationOnError(HRegionLocation loc, Throwable exception,
058      Function<HRegionLocation, HRegionLocation> cachedLocationSupplier,
059      Consumer<HRegionLocation> addToCache, Consumer<HRegionLocation> removeFromCache,
060      MetricsConnection metrics) {
061    HRegionLocation oldLoc = cachedLocationSupplier.apply(loc);
062    if (LOG.isDebugEnabled()) {
063      LOG.debug("Try updating {} , the old value is {}, error={}", loc, oldLoc,
064        exception != null ? exception.toString() : "none");
065    }
066    if (!canUpdateOnError(loc, oldLoc)) {
067      return;
068    }
069    Throwable cause = findException(exception);
070    if (LOG.isDebugEnabled()) {
071      LOG.debug("The actual exception when updating {} is {}", loc,
072        cause != null ? cause.toString() : "none");
073    }
074    if (cause == null || !isMetaClearingException(cause)) {
075      LOG.debug("Will not update {} because the exception is null or not the one we care about",
076        loc);
077      return;
078    }
079    if (cause instanceof RegionMovedException) {
080      RegionMovedException rme = (RegionMovedException) cause;
081      HRegionLocation newLoc =
082        new HRegionLocation(loc.getRegion(), rme.getServerName(), rme.getLocationSeqNum());
083      LOG.debug("Try updating {} with the new location {} constructed by {}", loc, newLoc,
084        rme.toString());
085      addToCache.accept(newLoc);
086    } else {
087      LOG.debug("Try removing {} from cache", loc);
088      if (metrics != null) {
089        metrics.incrCacheDroppingExceptions(exception);
090      }
091      removeFromCache.accept(loc);
092    }
093  }
094
095  static RegionLocations createRegionLocations(HRegionLocation loc) {
096    int replicaId = loc.getRegion().getReplicaId();
097    HRegionLocation[] locs = new HRegionLocation[replicaId + 1];
098    locs[replicaId] = loc;
099    return new RegionLocations(locs);
100  }
101
102  /**
103   * Create a new {@link RegionLocations} based on the given {@code oldLocs}, and replace the
104   * location for the given {@code replicaId} with the given {@code loc}.
105   * <p/>
106   * All the {@link RegionLocations} in async locator related class are immutable because we want to
107   * access them concurrently, so here we need to create a new one, instead of calling
108   * {@link RegionLocations#updateLocation(HRegionLocation, boolean, boolean)}.
109   */
110  static RegionLocations replaceRegionLocation(RegionLocations oldLocs, HRegionLocation loc) {
111    int replicaId = loc.getRegion().getReplicaId();
112    HRegionLocation[] locs = oldLocs.getRegionLocations();
113    locs = Arrays.copyOf(locs, Math.max(replicaId + 1, locs.length));
114    locs[replicaId] = loc;
115    return new RegionLocations(locs);
116  }
117
118  /**
119   * Create a new {@link RegionLocations} based on the given {@code oldLocs}, and remove the
120   * location for the given {@code replicaId}.
121   * <p/>
122   * All the {@link RegionLocations} in async locator related class are immutable because we want to
123   * access them concurrently, so here we need to create a new one, instead of calling
124   * {@link RegionLocations#remove(int)}.
125   */
126  static RegionLocations removeRegionLocation(RegionLocations oldLocs, int replicaId) {
127    HRegionLocation[] locs = oldLocs.getRegionLocations();
128    if (locs.length < replicaId + 1) {
129      // Here we do not modify the oldLocs so it is safe to return it.
130      return oldLocs;
131    }
132    locs = Arrays.copyOf(locs, locs.length);
133    locs[replicaId] = null;
134    if (ObjectUtils.firstNonNull(locs) != null) {
135      return new RegionLocations(locs);
136    } else {
137      // if all the locations are null, just return null
138      return null;
139    }
140  }
141
142  static boolean isGood(RegionLocations locs, int replicaId) {
143    if (locs == null) {
144      return false;
145    }
146    HRegionLocation loc = locs.getRegionLocation(replicaId);
147    return loc != null && loc.getServerName() != null;
148  }
149}