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.RegionReplicaTestHelper.testLocator; 021import static org.apache.hadoop.hbase.client.trace.hamcrest.SpanDataMatchers.hasEnded; 022import static org.apache.hadoop.hbase.client.trace.hamcrest.SpanDataMatchers.hasKind; 023import static org.apache.hadoop.hbase.client.trace.hamcrest.SpanDataMatchers.hasName; 024import static org.apache.hadoop.hbase.client.trace.hamcrest.SpanDataMatchers.hasParentSpanId; 025import static org.hamcrest.MatcherAssert.assertThat; 026import static org.hamcrest.Matchers.allOf; 027import static org.hamcrest.Matchers.endsWith; 028import static org.hamcrest.Matchers.hasItem; 029 030import io.opentelemetry.api.trace.SpanKind; 031import io.opentelemetry.sdk.testing.junit5.OpenTelemetryExtension; 032import io.opentelemetry.sdk.trace.data.SpanData; 033import java.io.IOException; 034import java.util.List; 035import java.util.concurrent.TimeUnit; 036import java.util.stream.Stream; 037import org.apache.hadoop.conf.Configuration; 038import org.apache.hadoop.hbase.HBaseParameterizedTestTemplate; 039import org.apache.hadoop.hbase.HBaseTestingUtil; 040import org.apache.hadoop.hbase.HConstants; 041import org.apache.hadoop.hbase.HRegionLocation; 042import org.apache.hadoop.hbase.MatcherPredicate; 043import org.apache.hadoop.hbase.RegionLocations; 044import org.apache.hadoop.hbase.TableName; 045import org.apache.hadoop.hbase.client.RegionReplicaTestHelper.Locator; 046import org.apache.hadoop.hbase.client.trace.StringTraceRenderer; 047import org.apache.hadoop.hbase.security.User; 048import org.apache.hadoop.hbase.testclassification.ClientTests; 049import org.apache.hadoop.hbase.testclassification.MediumTests; 050import org.apache.hadoop.hbase.trace.TraceUtil; 051import org.hamcrest.Matcher; 052import org.junit.jupiter.api.AfterAll; 053import org.junit.jupiter.api.AfterEach; 054import org.junit.jupiter.api.BeforeAll; 055import org.junit.jupiter.api.BeforeEach; 056import org.junit.jupiter.api.Tag; 057import org.junit.jupiter.api.TestInfo; 058import org.junit.jupiter.api.TestTemplate; 059import org.junit.jupiter.api.extension.RegisterExtension; 060import org.junit.jupiter.params.provider.Arguments; 061import org.slf4j.Logger; 062import org.slf4j.LoggerFactory; 063 064import org.apache.hbase.thirdparty.com.google.common.io.Closeables; 065 066@Tag(MediumTests.TAG) 067@Tag(ClientTests.TAG) 068@HBaseParameterizedTestTemplate(name = "[{index}]: registry = {0}") 069public class TestAsyncMetaRegionLocator { 070 071 private static final Logger LOG = LoggerFactory.getLogger(TestAsyncMetaRegionLocator.class); 072 073 private static final HBaseTestingUtil UTIL = new HBaseTestingUtil(); 074 075 @RegisterExtension 076 private static final OpenTelemetryExtension OTEL_EXT = OpenTelemetryExtension.create(); 077 078 private Class<? extends ConnectionRegistry> registryClass; 079 080 private ConnectionRegistry registry; 081 082 private AsyncMetaRegionLocator locator; 083 084 public TestAsyncMetaRegionLocator(Class<? extends ConnectionRegistry> registryClass) { 085 this.registryClass = registryClass; 086 } 087 088 @SuppressWarnings("deprecation") 089 public static Stream<Arguments> parameters() { 090 return Stream.of(Arguments.of(RpcConnectionRegistry.class), Arguments.of(MasterRegistry.class), 091 Arguments.of(ZKConnectionRegistry.class)); 092 } 093 094 @BeforeAll 095 public static void setUpBeforeAll() throws Exception { 096 UTIL.startMiniCluster(3); 097 HBaseTestingUtil.setReplicas(UTIL.getAdmin(), TableName.META_TABLE_NAME, 3); 098 UTIL.waitUntilNoRegionsInTransition(); 099 try (ConnectionRegistry registry = 100 ConnectionRegistryFactory.create(UTIL.getConfiguration(), User.getCurrent())) { 101 RegionReplicaTestHelper.waitUntilAllMetaReplicasAreReady(UTIL, registry); 102 } 103 UTIL.getAdmin().balancerSwitch(false, true); 104 } 105 106 @AfterAll 107 public static void tearDownAfterAll() throws IOException { 108 UTIL.shutdownMiniCluster(); 109 } 110 111 @BeforeEach 112 public void setUp() throws IOException { 113 Configuration conf = new Configuration(UTIL.getConfiguration()); 114 conf.setClass(HConstants.CLIENT_CONNECTION_REGISTRY_IMPL_CONF_KEY, registryClass, 115 ConnectionRegistry.class); 116 registry = ConnectionRegistryFactory.create(conf, User.getCurrent()); 117 locator = new AsyncMetaRegionLocator(registry); 118 } 119 120 @AfterEach 121 public void tearDown() throws IOException { 122 Closeables.close(registry, true); 123 } 124 125 @TestTemplate 126 public void test(TestInfo testInfo) { 127 String methodName = testInfo.getTestMethod().get().getName(); 128 TraceUtil.trace(() -> { 129 try { 130 testLocator(UTIL, TableName.META_TABLE_NAME, new Locator() { 131 @Override 132 public void updateCachedLocationOnError(HRegionLocation loc, Throwable error) { 133 locator.updateCachedLocationOnError(loc, error); 134 } 135 136 @Override 137 public RegionLocations getRegionLocations(TableName tableName, int replicaId, 138 boolean reload) throws Exception { 139 return locator.getRegionLocations(replicaId, reload).get(); 140 } 141 }); 142 } catch (Exception e) { 143 throw new RuntimeException(e); 144 } 145 }, methodName); 146 147 Matcher<SpanData> parentSpanMatcher = allOf(hasName(methodName), hasEnded()); 148 149 UTIL.waitFor(TimeUnit.SECONDS.toMillis(5), 150 new MatcherPredicate<>(OTEL_EXT::getSpans, hasItem(parentSpanMatcher))); 151 List<SpanData> spans = OTEL_EXT.getSpans(); 152 if (LOG.isDebugEnabled()) { 153 StringTraceRenderer renderer = new StringTraceRenderer(spans); 154 renderer.render(LOG::debug); 155 } 156 assertThat(spans, hasItem(parentSpanMatcher)); 157 final SpanData parentSpan = 158 spans.stream().filter(parentSpanMatcher::matches).findAny().orElseThrow(AssertionError::new); 159 160 Matcher<SpanData> registryGetMetaRegionLocationsMatcher = 161 allOf(hasName(endsWith(registryClass.getSimpleName() + ".getMetaRegionLocations")), 162 hasParentSpanId(parentSpan), hasKind(SpanKind.INTERNAL), hasEnded()); 163 assertThat(spans, hasItem(registryGetMetaRegionLocationsMatcher)); 164 165 // RpcConnectionRegistry specific tracing assertions 166 if (registry instanceof RpcConnectionRegistry) { 167 SpanData registryGetMetaRegionLocationsSpan = 168 spans.stream().filter(registryGetMetaRegionLocationsMatcher::matches).findFirst() 169 .orElseThrow(AssertionError::new); 170 // we need to fetch cluster id at the first time 171 Matcher<SpanData> fetchClusterIdMatcher = allOf( 172 hasName(endsWith("ConnectionRegistryService/GetConnectionRegistry")), 173 hasParentSpanId(registryGetMetaRegionLocationsSpan), hasKind(SpanKind.CLIENT), hasEnded()); 174 assertThat(spans, hasItem(fetchClusterIdMatcher)); 175 SpanData fetchClusterIdSpan = spans.stream().filter(fetchClusterIdMatcher::matches) 176 .findFirst().orElseThrow(AssertionError::new); 177 Matcher<SpanData> clientGetMetaRegionLocationsMatcher = 178 allOf(hasName(endsWith("ClientMetaService/GetMetaRegionLocations")), 179 hasParentSpanId(fetchClusterIdSpan), hasKind(SpanKind.CLIENT), hasEnded()); 180 assertThat(spans, hasItem(clientGetMetaRegionLocationsMatcher)); 181 } 182 } 183}