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}