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