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;
025
026import java.util.concurrent.CompletableFuture;
027import java.util.concurrent.atomic.AtomicReference;
028import org.apache.hadoop.hbase.HRegionLocation;
029import org.apache.hadoop.hbase.RegionLocations;
030import org.apache.yetus.audience.InterfaceAudience;
031
032/**
033 * The asynchronous locator for meta region.
034 */
035@InterfaceAudience.Private
036class AsyncMetaRegionLocator {
037
038  private final AsyncRegistry registry;
039
040  private final AtomicReference<RegionLocations> metaRegionLocations = new AtomicReference<>();
041
042  private final AtomicReference<CompletableFuture<RegionLocations>> metaRelocateFuture =
043    new AtomicReference<>();
044
045  AsyncMetaRegionLocator(AsyncRegistry registry) {
046    this.registry = registry;
047  }
048
049  /**
050   * Get the region locations for meta region. If the location for the given replica is not
051   * available in the cached locations, then fetch from the HBase cluster.
052   * <p/>
053   * The <code>replicaId</code> parameter is important. If the region replication config for meta
054   * region is changed, then the cached region locations may not have the locations for new
055   * replicas. If we do not check the location for the given replica, we will always return the
056   * cached region locations and cause an infinite loop.
057   */
058  CompletableFuture<RegionLocations> getRegionLocations(int replicaId, boolean reload) {
059    return ConnectionUtils.getOrFetch(metaRegionLocations, metaRelocateFuture, reload,
060      registry::getMetaRegionLocation, locs -> isGood(locs, replicaId), "meta region location");
061  }
062
063  private HRegionLocation getCacheLocation(HRegionLocation loc) {
064    RegionLocations locs = metaRegionLocations.get();
065    return locs != null ? locs.getRegionLocation(loc.getRegion().getReplicaId()) : null;
066  }
067
068  private void addLocationToCache(HRegionLocation loc) {
069    for (;;) {
070      int replicaId = loc.getRegion().getReplicaId();
071      RegionLocations oldLocs = metaRegionLocations.get();
072      if (oldLocs == null) {
073        RegionLocations newLocs = createRegionLocations(loc);
074        if (metaRegionLocations.compareAndSet(null, newLocs)) {
075          return;
076        }
077      }
078      HRegionLocation oldLoc = oldLocs.getRegionLocation(replicaId);
079      if (oldLoc != null && (oldLoc.getSeqNum() > loc.getSeqNum() ||
080        oldLoc.getServerName().equals(loc.getServerName()))) {
081        return;
082      }
083      RegionLocations newLocs = replaceRegionLocation(oldLocs, loc);
084      if (metaRegionLocations.compareAndSet(oldLocs, newLocs)) {
085        return;
086      }
087    }
088  }
089
090  private void removeLocationFromCache(HRegionLocation loc) {
091    for (;;) {
092      RegionLocations oldLocs = metaRegionLocations.get();
093      if (oldLocs == null) {
094        return;
095      }
096      HRegionLocation oldLoc = oldLocs.getRegionLocation(loc.getRegion().getReplicaId());
097      if (!canUpdateOnError(loc, oldLoc)) {
098        return;
099      }
100      RegionLocations newLocs = removeRegionLocation(oldLocs, loc.getRegion().getReplicaId());
101      if (metaRegionLocations.compareAndSet(oldLocs, newLocs)) {
102        return;
103      }
104    }
105  }
106
107  void updateCachedLocationOnError(HRegionLocation loc, Throwable exception) {
108    AsyncRegionLocatorHelper.updateCachedLocationOnError(loc, exception, this::getCacheLocation,
109      this::addLocationToCache, this::removeLocationFromCache);
110  }
111
112  void clearCache() {
113    metaRegionLocations.set(null);
114  }
115}