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}