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.AsyncRegionLocatorHelper.canUpdateOnError; 021import static org.apache.hadoop.hbase.client.AsyncRegionLocatorHelper.createRegionLocations; 022import static org.apache.hadoop.hbase.client.AsyncRegionLocatorHelper.isGood; 023import static org.apache.hadoop.hbase.client.AsyncRegionLocatorHelper.removeRegionLocation; 024import static org.apache.hadoop.hbase.client.AsyncRegionLocatorHelper.replaceRegionLocation; 025import static org.apache.hadoop.hbase.util.FutureUtils.addListener; 026 027import java.util.concurrent.CompletableFuture; 028import java.util.concurrent.atomic.AtomicReference; 029import org.apache.hadoop.hbase.HRegionLocation; 030import org.apache.hadoop.hbase.RegionLocations; 031import org.apache.yetus.audience.InterfaceAudience; 032import org.slf4j.Logger; 033import org.slf4j.LoggerFactory; 034 035/** 036 * The asynchronous locator for meta region. 037 */ 038@InterfaceAudience.Private 039class AsyncMetaRegionLocator { 040 041 private static final Logger LOG = LoggerFactory.getLogger(AsyncMetaRegionLocator.class); 042 043 private final AsyncRegistry registry; 044 045 private final AtomicReference<RegionLocations> metaRegionLocations = new AtomicReference<>(); 046 047 private final AtomicReference<CompletableFuture<RegionLocations>> metaRelocateFuture = 048 new AtomicReference<>(); 049 050 AsyncMetaRegionLocator(AsyncRegistry registry) { 051 this.registry = registry; 052 } 053 054 /** 055 * Get the region locations for meta region. If the location for the given replica is not 056 * available in the cached locations, then fetch from the HBase cluster. 057 * <p/> 058 * The <code>replicaId</code> parameter is important. If the region replication config for meta 059 * region is changed, then the cached region locations may not have the locations for new 060 * replicas. If we do not check the location for the given replica, we will always return the 061 * cached region locations and cause an infinite loop. 062 */ 063 CompletableFuture<RegionLocations> getRegionLocations(int replicaId, boolean reload) { 064 for (;;) { 065 if (!reload) { 066 RegionLocations locs = this.metaRegionLocations.get(); 067 if (isGood(locs, replicaId)) { 068 return CompletableFuture.completedFuture(locs); 069 } 070 } 071 LOG.trace("Meta region location cache is null, try fetching from registry."); 072 if (metaRelocateFuture.compareAndSet(null, new CompletableFuture<>())) { 073 LOG.debug("Start fetching meta region location from registry."); 074 CompletableFuture<RegionLocations> future = metaRelocateFuture.get(); 075 addListener(registry.getMetaRegionLocation(), (locs, error) -> { 076 if (error != null) { 077 LOG.debug("Failed to fetch meta region location from registry", error); 078 metaRelocateFuture.getAndSet(null).completeExceptionally(error); 079 return; 080 } 081 LOG.debug("The fetched meta region location is {}", locs); 082 // Here we update cache before reset future, so it is possible that someone can get a 083 // stale value. Consider this: 084 // 1. update cache 085 // 2. someone clear the cache and relocate again 086 // 3. the metaRelocateFuture is not null so the old future is used. 087 // 4. we clear metaRelocateFuture and complete the future in it with the value being 088 // cleared in step 2. 089 // But we do not think it is a big deal as it rarely happens, and even if it happens, the 090 // caller will retry again later, no correctness problems. 091 this.metaRegionLocations.set(locs); 092 metaRelocateFuture.set(null); 093 future.complete(locs); 094 }); 095 return future; 096 } else { 097 CompletableFuture<RegionLocations> future = metaRelocateFuture.get(); 098 if (future != null) { 099 return future; 100 } 101 } 102 } 103 } 104 105 private HRegionLocation getCacheLocation(HRegionLocation loc) { 106 RegionLocations locs = metaRegionLocations.get(); 107 return locs != null ? locs.getRegionLocation(loc.getRegion().getReplicaId()) : null; 108 } 109 110 private void addLocationToCache(HRegionLocation loc) { 111 for (;;) { 112 int replicaId = loc.getRegion().getReplicaId(); 113 RegionLocations oldLocs = metaRegionLocations.get(); 114 if (oldLocs == null) { 115 RegionLocations newLocs = createRegionLocations(loc); 116 if (metaRegionLocations.compareAndSet(null, newLocs)) { 117 return; 118 } 119 } 120 HRegionLocation oldLoc = oldLocs.getRegionLocation(replicaId); 121 if (oldLoc != null && (oldLoc.getSeqNum() > loc.getSeqNum() || 122 oldLoc.getServerName().equals(loc.getServerName()))) { 123 return; 124 } 125 RegionLocations newLocs = replaceRegionLocation(oldLocs, loc); 126 if (metaRegionLocations.compareAndSet(oldLocs, newLocs)) { 127 return; 128 } 129 } 130 } 131 132 private void removeLocationFromCache(HRegionLocation loc) { 133 for (;;) { 134 RegionLocations oldLocs = metaRegionLocations.get(); 135 if (oldLocs == null) { 136 return; 137 } 138 HRegionLocation oldLoc = oldLocs.getRegionLocation(loc.getRegion().getReplicaId()); 139 if (!canUpdateOnError(loc, oldLoc)) { 140 return; 141 } 142 RegionLocations newLocs = removeRegionLocation(oldLocs, loc.getRegion().getReplicaId()); 143 if (metaRegionLocations.compareAndSet(oldLocs, newLocs)) { 144 return; 145 } 146 } 147 } 148 149 void updateCachedLocationOnError(HRegionLocation loc, Throwable exception) { 150 AsyncRegionLocatorHelper.updateCachedLocationOnError(loc, exception, this::getCacheLocation, 151 this::addLocationToCache, this::removeLocationFromCache); 152 } 153 154 void clearCache() { 155 metaRegionLocations.set(null); 156 } 157}