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.trace.hamcrest.AttributesMatchers.containsEntry;
021import static org.apache.hadoop.hbase.client.trace.hamcrest.AttributesMatchers.containsEntryWithStringValuesOf;
022import static org.apache.hadoop.hbase.client.trace.hamcrest.SpanDataMatchers.hasAttributes;
023import static org.apache.hadoop.hbase.client.trace.hamcrest.SpanDataMatchers.hasEnded;
024import static org.apache.hadoop.hbase.client.trace.hamcrest.SpanDataMatchers.hasKind;
025import static org.apache.hadoop.hbase.client.trace.hamcrest.SpanDataMatchers.hasName;
026import static org.apache.hadoop.hbase.client.trace.hamcrest.SpanDataMatchers.hasStatusWithCode;
027import static org.apache.hadoop.hbase.client.trace.hamcrest.TraceTestUtil.buildConnectionAttributesMatcher;
028import static org.apache.hadoop.hbase.client.trace.hamcrest.TraceTestUtil.buildTableAttributesMatcher;
029import static org.hamcrest.MatcherAssert.assertThat;
030import static org.hamcrest.Matchers.allOf;
031import static org.hamcrest.Matchers.containsInAnyOrder;
032import static org.hamcrest.Matchers.hasItem;
033
034import io.opentelemetry.api.trace.SpanKind;
035import io.opentelemetry.api.trace.StatusCode;
036import io.opentelemetry.sdk.testing.junit4.OpenTelemetryRule;
037import io.opentelemetry.sdk.trace.data.SpanData;
038import java.io.IOException;
039import java.util.Arrays;
040import java.util.concurrent.CompletableFuture;
041import java.util.concurrent.TimeUnit;
042import org.apache.hadoop.conf.Configuration;
043import org.apache.hadoop.hbase.HBaseClassTestRule;
044import org.apache.hadoop.hbase.HBaseConfiguration;
045import org.apache.hadoop.hbase.HConstants;
046import org.apache.hadoop.hbase.HRegionLocation;
047import org.apache.hadoop.hbase.MatcherPredicate;
048import org.apache.hadoop.hbase.RegionLocations;
049import org.apache.hadoop.hbase.ServerName;
050import org.apache.hadoop.hbase.TableName;
051import org.apache.hadoop.hbase.Waiter;
052import org.apache.hadoop.hbase.security.UserProvider;
053import org.apache.hadoop.hbase.testclassification.ClientTests;
054import org.apache.hadoop.hbase.testclassification.MediumTests;
055import org.hamcrest.Matcher;
056import org.junit.After;
057import org.junit.Before;
058import org.junit.ClassRule;
059import org.junit.Rule;
060import org.junit.Test;
061import org.junit.experimental.categories.Category;
062import org.slf4j.Logger;
063import org.slf4j.LoggerFactory;
064
065import org.apache.hbase.thirdparty.com.google.common.io.Closeables;
066
067@Category({ ClientTests.class, MediumTests.class })
068public class TestAsyncRegionLocatorTracing {
069  private static final Logger LOG = LoggerFactory.getLogger(TestAsyncRegionLocatorTracing.class);
070
071  @ClassRule
072  public static final HBaseClassTestRule CLASS_RULE =
073    HBaseClassTestRule.forClass(TestAsyncRegionLocatorTracing.class);
074
075  private static final Configuration CONF = HBaseConfiguration.create();
076
077  private AsyncConnectionImpl conn;
078
079  private RegionLocations locs;
080
081  @Rule
082  public OpenTelemetryRule traceRule = OpenTelemetryRule.create();
083
084  @Before
085  public void setUp() throws IOException {
086    RegionInfo metaRegionInfo = RegionInfoBuilder.newBuilder(TableName.META_TABLE_NAME).build();
087    locs = new RegionLocations(
088      new HRegionLocation(metaRegionInfo,
089        ServerName.valueOf("127.0.0.1", 12345, System.currentTimeMillis())),
090      new HRegionLocation(RegionReplicaUtil.getRegionInfoForReplica(metaRegionInfo, 1),
091        ServerName.valueOf("127.0.0.2", 12345, System.currentTimeMillis())),
092      new HRegionLocation(RegionReplicaUtil.getRegionInfoForReplica(metaRegionInfo, 2),
093        ServerName.valueOf("127.0.0.3", 12345, System.currentTimeMillis())));
094    conn = new AsyncConnectionImpl(CONF, new DoNothingConnectionRegistry(CONF) {
095
096      @Override
097      public CompletableFuture<RegionLocations> getMetaRegionLocations() {
098        return CompletableFuture.completedFuture(locs);
099      }
100    }, "test", UserProvider.instantiate(CONF).getCurrent());
101  }
102
103  @After
104  public void tearDown() throws IOException {
105    Closeables.close(conn, true);
106  }
107
108  private SpanData waitSpan(String name) {
109    return waitSpan(hasName(name));
110  }
111
112  private SpanData waitSpan(Matcher<SpanData> matcher) {
113    Matcher<SpanData> spanLocator = allOf(matcher, hasEnded());
114    try {
115      Waiter.waitFor(CONF, 1000, new MatcherPredicate<>("waiting for span",
116        () -> traceRule.getSpans(), hasItem(spanLocator)));
117    } catch (AssertionError e) {
118      LOG.error("AssertionError while waiting for matching span. Span reservoir contains: {}",
119        traceRule.getSpans());
120      throw e;
121    }
122    return traceRule.getSpans().stream().filter(spanLocator::matches).findFirst()
123      .orElseThrow(AssertionError::new);
124  }
125
126  @Test
127  public void testClearCache() {
128    conn.getLocator().clearCache();
129    SpanData span = waitSpan("AsyncRegionLocator.clearCache");
130    assertThat(span, allOf(hasStatusWithCode(StatusCode.OK), hasKind(SpanKind.INTERNAL),
131      buildConnectionAttributesMatcher(conn)));
132  }
133
134  @Test
135  public void testClearCacheServerName() {
136    ServerName sn = ServerName.valueOf("127.0.0.1", 12345, System.currentTimeMillis());
137    conn.getLocator().clearCache(sn);
138    SpanData span = waitSpan("AsyncRegionLocator.clearCache");
139    assertThat(span,
140      allOf(hasStatusWithCode(StatusCode.OK), hasKind(SpanKind.INTERNAL),
141        buildConnectionAttributesMatcher(conn),
142        hasAttributes(containsEntry("db.hbase.server.name", sn.getServerName()))));
143  }
144
145  @Test
146  public void testClearCacheTableName() {
147    conn.getLocator().clearCache(TableName.META_TABLE_NAME);
148    SpanData span = waitSpan("AsyncRegionLocator.clearCache");
149    assertThat(span,
150      allOf(hasStatusWithCode(StatusCode.OK), hasKind(SpanKind.INTERNAL),
151        buildConnectionAttributesMatcher(conn),
152        buildTableAttributesMatcher(TableName.META_TABLE_NAME)));
153  }
154
155  @Test
156  public void testGetRegionLocation() {
157    conn.getLocator().getRegionLocation(TableName.META_TABLE_NAME, HConstants.EMPTY_START_ROW,
158      RegionLocateType.CURRENT, TimeUnit.SECONDS.toNanos(1)).join();
159    SpanData span = waitSpan("AsyncRegionLocator.getRegionLocation");
160    assertThat(span,
161      allOf(hasStatusWithCode(StatusCode.OK), hasKind(SpanKind.INTERNAL),
162        buildConnectionAttributesMatcher(conn),
163        buildTableAttributesMatcher(TableName.META_TABLE_NAME),
164        hasAttributes(containsEntryWithStringValuesOf("db.hbase.regions",
165          locs.getDefaultRegionLocation().getRegion().getRegionNameAsString()))));
166  }
167
168  @Test
169  public void testGetRegionLocations() {
170    conn.getLocator().getRegionLocations(TableName.META_TABLE_NAME, HConstants.EMPTY_START_ROW,
171      RegionLocateType.CURRENT, false, TimeUnit.SECONDS.toNanos(1)).join();
172    SpanData span = waitSpan("AsyncRegionLocator.getRegionLocations");
173    String[] expectedRegions =
174      Arrays.stream(locs.getRegionLocations()).map(HRegionLocation::getRegion)
175        .map(RegionInfo::getRegionNameAsString).toArray(String[]::new);
176    assertThat(span, allOf(hasStatusWithCode(StatusCode.OK), hasKind(SpanKind.INTERNAL),
177      buildConnectionAttributesMatcher(conn),
178      buildTableAttributesMatcher(TableName.META_TABLE_NAME), hasAttributes(
179        containsEntryWithStringValuesOf("db.hbase.regions", containsInAnyOrder(expectedRegions)))));
180  }
181}