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