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.HBaseTestingUtil; 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.StartTestingClusterOption; 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 HBaseTestingUtil 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 HBaseTestingUtil 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 HBaseTestingUtil.setReplicas(admin, TableName.META_TABLE_NAME, 3); 109 testUtil.waitUntilNoRegionsInTransition(); 110 registry = ConnectionRegistryFactory.create(testUtil.getConfiguration(), User.getCurrent()); 111 RegionReplicaTestHelper.waitUntilAllMetaReplicasAreReady(testUtil, registry); 112 admin.balancerSwitch(false).get(); 113 locator = new AsyncMetaRegionLocator(registry); 114 initialized = true; 115 } 116 117 @Override 118 protected void after() { 119 registry.close(); 120 } 121 } 122 123 public static abstract class AbstractBase { 124 private final OpenTelemetryClassRule otelClassRule = OpenTelemetryClassRule.create(); 125 private final MiniClusterRule miniClusterRule; 126 private final Setup setup; 127 128 protected Matcher<SpanData> parentSpanMatcher; 129 protected List<SpanData> spans; 130 protected Matcher<SpanData> registryGetMetaRegionLocationsMatcher; 131 132 @Rule 133 public final TestRule classRule; 134 135 @Rule 136 public final OpenTelemetryTestRule otelTestRule = new OpenTelemetryTestRule(otelClassRule); 137 138 @Rule 139 public TestName testName = new TestName(); 140 141 public AbstractBase() { 142 miniClusterRule = MiniClusterRule.newBuilder() 143 .setMiniClusterOption(StartTestingClusterOption.builder().numWorkers(3).build()) 144 .setConfiguration(() -> { 145 final Configuration conf = HBaseConfiguration.create(); 146 conf.setClass(HConstants.CLIENT_CONNECTION_REGISTRY_IMPL_CONF_KEY, 147 getConnectionRegistryClass(), ConnectionRegistry.class); 148 return conf; 149 }).build(); 150 final ConnectionRule connectionRule = 151 ConnectionRule.createAsyncConnectionRule(miniClusterRule::createAsyncConnection); 152 setup = new Setup(connectionRule, miniClusterRule); 153 classRule = RuleChain.outerRule(otelClassRule).around(miniClusterRule).around(connectionRule) 154 .around(setup); 155 } 156 157 protected abstract Class<? extends ConnectionRegistry> getConnectionRegistryClass(); 158 159 @Test 160 public void test() throws Exception { 161 final AsyncMetaRegionLocator locator = setup.getLocator(); 162 final HBaseTestingUtil testUtil = setup.getTestingUtility(); 163 164 TraceUtil.trace(() -> { 165 try { 166 testLocator(miniClusterRule.getTestingUtility(), TableName.META_TABLE_NAME, 167 new Locator() { 168 @Override 169 public void updateCachedLocationOnError(HRegionLocation loc, Throwable error) { 170 locator.updateCachedLocationOnError(loc, error); 171 } 172 173 @Override 174 public RegionLocations getRegionLocations(TableName tableName, int replicaId, 175 boolean reload) throws Exception { 176 return locator.getRegionLocations(replicaId, reload).get(); 177 } 178 }); 179 } catch (Exception e) { 180 throw new RuntimeException(e); 181 } 182 }, testName.getMethodName()); 183 184 final Configuration conf = testUtil.getConfiguration(); 185 parentSpanMatcher = allOf(hasName(testName.getMethodName()), hasEnded()); 186 Waiter.waitFor(conf, TimeUnit.SECONDS.toMillis(5), 187 new MatcherPredicate<>(otelClassRule::getSpans, hasItem(parentSpanMatcher))); 188 spans = otelClassRule.getSpans(); 189 if (logger.isDebugEnabled()) { 190 StringTraceRenderer renderer = new StringTraceRenderer(spans); 191 renderer.render(logger::debug); 192 } 193 assertThat(spans, hasItem(parentSpanMatcher)); 194 final SpanData parentSpan = spans.stream().filter(parentSpanMatcher::matches).findAny() 195 .orElseThrow(AssertionError::new); 196 197 registryGetMetaRegionLocationsMatcher = 198 allOf(hasName(endsWith("ConnectionRegistry.getMetaRegionLocations")), 199 hasParentSpanId(parentSpan), hasKind(SpanKind.INTERNAL), hasEnded()); 200 assertThat(spans, hasItem(registryGetMetaRegionLocationsMatcher)); 201 } 202 } 203 204 /** 205 * Test covers when client is configured with {@link ZKConnectionRegistry}. 206 */ 207 @Category({ MediumTests.class, ClientTests.class }) 208 public static class TestZKConnectionRegistry extends AbstractBase { 209 @ClassRule 210 public static final HBaseClassTestRule CLASS_RULE = 211 HBaseClassTestRule.forClass(TestZKConnectionRegistry.class); 212 213 @Override 214 protected Class<? extends ConnectionRegistry> getConnectionRegistryClass() { 215 return ZKConnectionRegistry.class; 216 } 217 } 218 219 /** 220 * Test covers when client is configured with {@link RpcConnectionRegistry}. 221 */ 222 @Category({ MediumTests.class, ClientTests.class }) 223 public static class TestRpcConnectionRegistry extends AbstractBase { 224 @ClassRule 225 public static final HBaseClassTestRule CLASS_RULE = 226 HBaseClassTestRule.forClass(TestRpcConnectionRegistry.class); 227 228 @Override 229 protected Class<? extends ConnectionRegistry> getConnectionRegistryClass() { 230 return RpcConnectionRegistry.class; 231 } 232 233 @Test 234 @Override 235 public void test() throws Exception { 236 super.test(); 237 final SpanData registry_getMetaRegionLocationsSpan = 238 spans.stream().filter(registryGetMetaRegionLocationsMatcher::matches).findAny() 239 .orElseThrow(AssertionError::new); 240 final Matcher<SpanData> clientGetMetaRegionLocationsMatcher = allOf( 241 hasName(endsWith("ClientMetaService/GetMetaRegionLocations")), 242 hasParentSpanId(registry_getMetaRegionLocationsSpan), hasKind(SpanKind.CLIENT), hasEnded()); 243 assertThat(spans, hasItem(clientGetMetaRegionLocationsMatcher)); 244 } 245 } 246}