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.trace.data.SpanData; 032import java.util.List; 033import java.util.concurrent.TimeUnit; 034import org.apache.hadoop.conf.Configuration; 035import org.apache.hadoop.hbase.ConnectionRule; 036import org.apache.hadoop.hbase.HBaseClassTestRule; 037import org.apache.hadoop.hbase.HBaseConfiguration; 038import org.apache.hadoop.hbase.HBaseTestingUtility; 039import org.apache.hadoop.hbase.HConstants; 040import org.apache.hadoop.hbase.HRegionLocation; 041import org.apache.hadoop.hbase.MatcherPredicate; 042import org.apache.hadoop.hbase.MiniClusterRule; 043import org.apache.hadoop.hbase.RegionLocations; 044import org.apache.hadoop.hbase.StartMiniClusterOption; 045import org.apache.hadoop.hbase.TableName; 046import org.apache.hadoop.hbase.Waiter; 047import org.apache.hadoop.hbase.client.RegionReplicaTestHelper.Locator; 048import org.apache.hadoop.hbase.client.trace.StringTraceRenderer; 049import org.apache.hadoop.hbase.testclassification.ClientTests; 050import org.apache.hadoop.hbase.testclassification.MediumTests; 051import org.apache.hadoop.hbase.trace.OpenTelemetryClassRule; 052import org.apache.hadoop.hbase.trace.OpenTelemetryTestRule; 053import org.apache.hadoop.hbase.trace.TraceUtil; 054import org.hamcrest.Matcher; 055import org.junit.ClassRule; 056import org.junit.Rule; 057import org.junit.Test; 058import org.junit.experimental.categories.Category; 059import org.junit.experimental.runners.Enclosed; 060import org.junit.rules.ExternalResource; 061import org.junit.rules.RuleChain; 062import org.junit.rules.TestName; 063import org.junit.rules.TestRule; 064import org.junit.runner.RunWith; 065import org.slf4j.Logger; 066import org.slf4j.LoggerFactory; 067 068@RunWith(Enclosed.class) 069public class TestAsyncMetaRegionLocator { 070 private static final Logger logger = LoggerFactory.getLogger(TestAsyncMetaRegionLocator.class); 071 072 private static final class Setup extends ExternalResource { 073 074 private final MiniClusterRule miniClusterRule; 075 private final ConnectionRule connectionRule; 076 077 private boolean initialized = false; 078 private HBaseTestingUtility testUtil; 079 private AsyncMetaRegionLocator locator; 080 private ConnectionRegistry registry; 081 082 public Setup(final ConnectionRule connectionRule, final MiniClusterRule miniClusterRule) { 083 this.connectionRule = connectionRule; 084 this.miniClusterRule = miniClusterRule; 085 } 086 087 public HBaseTestingUtility getTestingUtility() { 088 assertInitialized(); 089 return testUtil; 090 } 091 092 public AsyncMetaRegionLocator getLocator() { 093 assertInitialized(); 094 return locator; 095 } 096 097 private void assertInitialized() { 098 if (!initialized) { 099 throw new IllegalStateException("before method has not been called."); 100 } 101 } 102 103 @Override 104 protected void before() throws Throwable { 105 final AsyncAdmin admin = connectionRule.getAsyncConnection().getAdmin(); 106 testUtil = miniClusterRule.getTestingUtility(); 107 HBaseTestingUtility.setReplicas(admin, TableName.META_TABLE_NAME, 3); 108 testUtil.waitUntilNoRegionsInTransition(); 109 registry = ConnectionRegistryFactory.getRegistry(testUtil.getConfiguration()); 110 RegionReplicaTestHelper.waitUntilAllMetaReplicasAreReady(testUtil, registry); 111 admin.balancerSwitch(false).get(); 112 locator = new AsyncMetaRegionLocator(registry); 113 initialized = true; 114 } 115 116 @Override 117 protected void after() { 118 registry.close(); 119 } 120 } 121 122 public static abstract class AbstractBase { 123 private final OpenTelemetryClassRule otelClassRule = OpenTelemetryClassRule.create(); 124 private final MiniClusterRule miniClusterRule; 125 private final Setup setup; 126 127 protected Matcher<SpanData> parentSpanMatcher; 128 protected List<SpanData> spans; 129 protected Matcher<SpanData> registryGetMetaRegionLocationsMatcher; 130 131 @Rule 132 public final TestRule classRule; 133 134 @Rule 135 public final OpenTelemetryTestRule otelTestRule = new OpenTelemetryTestRule(otelClassRule); 136 137 @Rule 138 public TestName testName = new TestName(); 139 140 public AbstractBase() { 141 miniClusterRule = MiniClusterRule.newBuilder() 142 .setMiniClusterOption(StartMiniClusterOption.builder().numWorkers(3).build()) 143 .setConfiguration(() -> { 144 final Configuration conf = HBaseConfiguration.create(); 145 conf.setClass(HConstants.CLIENT_CONNECTION_REGISTRY_IMPL_CONF_KEY, 146 getConnectionRegistryClass(), ConnectionRegistry.class); 147 return conf; 148 }).build(); 149 final ConnectionRule connectionRule = 150 ConnectionRule.createAsyncConnectionRule(miniClusterRule::createAsyncConnection); 151 setup = new Setup(connectionRule, miniClusterRule); 152 classRule = RuleChain.outerRule(otelClassRule).around(miniClusterRule).around(connectionRule) 153 .around(setup); 154 } 155 156 protected abstract Class<? extends ConnectionRegistry> getConnectionRegistryClass(); 157 158 @Test 159 public void test() throws Exception { 160 final AsyncMetaRegionLocator locator = setup.getLocator(); 161 final HBaseTestingUtility testUtil = setup.getTestingUtility(); 162 163 TraceUtil.trace(() -> { 164 try { 165 testLocator(miniClusterRule.getTestingUtility(), TableName.META_TABLE_NAME, 166 new Locator() { 167 @Override 168 public void updateCachedLocationOnError(HRegionLocation loc, Throwable error) { 169 locator.updateCachedLocationOnError(loc, error); 170 } 171 172 @Override 173 public RegionLocations getRegionLocations(TableName tableName, int replicaId, 174 boolean reload) throws Exception { 175 return locator.getRegionLocations(replicaId, reload).get(); 176 } 177 }); 178 } catch (Exception e) { 179 throw new RuntimeException(e); 180 } 181 }, testName.getMethodName()); 182 183 final Configuration conf = testUtil.getConfiguration(); 184 parentSpanMatcher = allOf(hasName(testName.getMethodName()), hasEnded()); 185 Waiter.waitFor(conf, TimeUnit.SECONDS.toMillis(5), 186 new MatcherPredicate<>(otelClassRule::getSpans, hasItem(parentSpanMatcher))); 187 spans = otelClassRule.getSpans(); 188 if (logger.isDebugEnabled()) { 189 StringTraceRenderer renderer = new StringTraceRenderer(spans); 190 renderer.render(logger::debug); 191 } 192 assertThat(spans, hasItem(parentSpanMatcher)); 193 final SpanData parentSpan = spans.stream().filter(parentSpanMatcher::matches).findAny() 194 .orElseThrow(AssertionError::new); 195 196 registryGetMetaRegionLocationsMatcher = 197 allOf(hasName(endsWith("ConnectionRegistry.getMetaRegionLocations")), 198 hasParentSpanId(parentSpan), hasKind(SpanKind.INTERNAL), hasEnded()); 199 assertThat(spans, hasItem(registryGetMetaRegionLocationsMatcher)); 200 } 201 } 202 203 /** 204 * Test covers when client is configured with {@link ZKConnectionRegistry}. 205 */ 206 @Category({ MediumTests.class, ClientTests.class }) 207 public static class TestZKConnectionRegistry extends AbstractBase { 208 @ClassRule 209 public static final HBaseClassTestRule CLASS_RULE = 210 HBaseClassTestRule.forClass(TestZKConnectionRegistry.class); 211 212 @Override 213 protected Class<? extends ConnectionRegistry> getConnectionRegistryClass() { 214 return ZKConnectionRegistry.class; 215 } 216 } 217 218 /** 219 * Test covers when client is configured with {@link RpcConnectionRegistry}. 220 */ 221 @Category({ MediumTests.class, ClientTests.class }) 222 public static class TestRpcConnectionRegistry extends AbstractBase { 223 @ClassRule 224 public static final HBaseClassTestRule CLASS_RULE = 225 HBaseClassTestRule.forClass(TestRpcConnectionRegistry.class); 226 227 @Override 228 protected Class<? extends ConnectionRegistry> getConnectionRegistryClass() { 229 return RpcConnectionRegistry.class; 230 } 231 232 @Test 233 @Override 234 public void test() throws Exception { 235 super.test(); 236 final SpanData registry_getMetaRegionLocationsSpan = 237 spans.stream().filter(registryGetMetaRegionLocationsMatcher::matches).findAny() 238 .orElseThrow(AssertionError::new); 239 final Matcher<SpanData> clientGetMetaRegionLocationsMatcher = allOf( 240 hasName(endsWith("ClientMetaService/GetMetaRegionLocations")), 241 hasParentSpanId(registry_getMetaRegionLocationsSpan), hasKind(SpanKind.CLIENT), hasEnded()); 242 assertThat(spans, hasItem(clientGetMetaRegionLocationsMatcher)); 243 } 244 } 245}