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.trace.HBaseSemanticAttributes.REGION_NAMES_KEY; 021 022import io.opentelemetry.api.trace.Span; 023import io.opentelemetry.api.trace.StatusCode; 024import io.opentelemetry.context.Scope; 025import java.io.IOException; 026import java.util.ArrayList; 027import java.util.Arrays; 028import java.util.Collections; 029import java.util.List; 030import java.util.Objects; 031import java.util.function.Function; 032import java.util.function.Supplier; 033import java.util.stream.Collectors; 034import org.apache.hadoop.hbase.CatalogReplicaMode; 035import org.apache.hadoop.hbase.HConstants; 036import org.apache.hadoop.hbase.HRegionLocation; 037import org.apache.hadoop.hbase.MetaTableAccessor; 038import org.apache.hadoop.hbase.RegionLocations; 039import org.apache.hadoop.hbase.TableName; 040import org.apache.hadoop.hbase.client.trace.TableSpanBuilder; 041import org.apache.hadoop.hbase.trace.TraceUtil; 042import org.apache.yetus.audience.InterfaceAudience; 043 044import org.apache.hbase.thirdparty.org.apache.commons.collections4.CollectionUtils; 045 046/** 047 * An implementation of {@link RegionLocator}. Used to view region location information for a single 048 * HBase table. Lightweight. Get as needed and just close when done. Instances of this class SHOULD 049 * NOT be constructed directly. Obtain an instance via {@link Connection}. See 050 * {@link ConnectionFactory} class comment for an example of how. 051 * <p/> 052 * This class is thread safe 053 */ 054@InterfaceAudience.Private 055public class HRegionLocator implements RegionLocator { 056 057 private final TableName tableName; 058 private final ConnectionImplementation connection; 059 060 public HRegionLocator(TableName tableName, ConnectionImplementation connection) { 061 this.connection = connection; 062 this.tableName = tableName; 063 } 064 065 /** 066 * {@inheritDoc} 067 */ 068 @Override 069 public void close() throws IOException { 070 // This method is required by the RegionLocator interface. This implementation does not have any 071 // persistent state, so there is no need to do anything here. 072 } 073 074 @Override 075 public HRegionLocation getRegionLocation(byte[] row, int replicaId, boolean reload) 076 throws IOException { 077 final Supplier<Span> supplier = new TableSpanBuilder(connection) 078 .setName("HRegionLocator.getRegionLocation").setTableName(tableName); 079 return tracedLocationFuture(() -> connection 080 .locateRegion(tableName, row, !reload, true, replicaId).getRegionLocation(replicaId), 081 AsyncRegionLocator::getRegionNames, supplier); 082 } 083 084 @Override 085 public List<HRegionLocation> getRegionLocations(byte[] row, boolean reload) throws IOException { 086 final Supplier<Span> supplier = new TableSpanBuilder(connection) 087 .setName("HRegionLocator.getRegionLocations").setTableName(tableName); 088 final RegionLocations locs = tracedLocationFuture( 089 () -> connection.locateRegion(tableName, row, !reload, true, RegionInfo.DEFAULT_REPLICA_ID), 090 AsyncRegionLocator::getRegionNames, supplier); 091 return Arrays.asList(locs.getRegionLocations()); 092 } 093 094 @Override 095 public List<HRegionLocation> getAllRegionLocations() throws IOException { 096 final Supplier<Span> supplier = new TableSpanBuilder(connection) 097 .setName("HRegionLocator.getAllRegionLocations").setTableName(tableName); 098 return tracedLocationFuture(() -> { 099 ArrayList<HRegionLocation> regions = new ArrayList<>(); 100 for (RegionLocations locations : listRegionLocations()) { 101 for (HRegionLocation location : locations.getRegionLocations()) { 102 regions.add(location); 103 } 104 RegionLocations cleaned = locations.removeElementsWithNullLocation(); 105 // above can return null if all locations had null location 106 if (cleaned != null) { 107 connection.cacheLocation(tableName, cleaned); 108 } 109 } 110 return regions; 111 }, HRegionLocator::getRegionNames, supplier); 112 } 113 114 private static List<String> getRegionNames(List<HRegionLocation> locations) { 115 if (CollectionUtils.isEmpty(locations)) { 116 return Collections.emptyList(); 117 } 118 return locations.stream().filter(Objects::nonNull).map(AsyncRegionLocator::getRegionNames) 119 .filter(Objects::nonNull).flatMap(List::stream).collect(Collectors.toList()); 120 } 121 122 @Override 123 public void clearRegionLocationCache() { 124 final Supplier<Span> supplier = new TableSpanBuilder(connection) 125 .setName("HRegionLocator.clearRegionLocationCache").setTableName(tableName); 126 TraceUtil.trace(() -> connection.clearRegionCache(tableName), supplier); 127 } 128 129 @Override 130 public TableName getName() { 131 return this.tableName; 132 } 133 134 @SuppressWarnings("MixedMutabilityReturnType") 135 private List<RegionLocations> listRegionLocations() throws IOException { 136 if (TableName.isMetaTableName(tableName)) { 137 return Collections 138 .singletonList(connection.locateRegion(tableName, HConstants.EMPTY_START_ROW, false, true)); 139 } 140 final List<RegionLocations> regions = new ArrayList<>(); 141 MetaTableAccessor.Visitor visitor = new MetaTableAccessor.TableVisitorBase(tableName) { 142 @Override 143 public boolean visitInternal(Result result) throws IOException { 144 RegionLocations locations = MetaTableAccessor.getRegionLocations(result); 145 if (locations == null) { 146 return true; 147 } 148 regions.add(locations); 149 return true; 150 } 151 }; 152 CatalogReplicaMode metaReplicaMode = CatalogReplicaMode.fromString(connection.getConfiguration() 153 .get(LOCATOR_META_REPLICAS_MODE, CatalogReplicaMode.NONE.toString())); 154 MetaTableAccessor.scanMetaForTableRegions(connection, visitor, tableName, metaReplicaMode); 155 return regions; 156 } 157 158 private <R, T extends Throwable> R tracedLocationFuture(TraceUtil.ThrowingCallable<R, T> action, 159 Function<R, List<String>> getRegionNames, Supplier<Span> spanSupplier) throws T { 160 final Span span = spanSupplier.get(); 161 try (Scope ignored = span.makeCurrent()) { 162 final R result = action.call(); 163 final List<String> regionNames = getRegionNames.apply(result); 164 if (!CollectionUtils.isEmpty(regionNames)) { 165 span.setAttribute(REGION_NAMES_KEY, regionNames); 166 } 167 span.setStatus(StatusCode.OK); 168 span.end(); 169 return result; 170 } catch (Throwable e) { 171 TraceUtil.setError(span, e); 172 span.end(); 173 throw e; 174 } 175 } 176}