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