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.master.procedure; 019 020import static io.opentelemetry.api.common.AttributeKey.longKey; 021import static org.apache.hadoop.hbase.client.trace.hamcrest.AttributesMatchers.containsEntry; 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.hasName; 025import static org.apache.hadoop.hbase.client.trace.hamcrest.SpanDataMatchers.hasStatusWithCode; 026import static org.hamcrest.MatcherAssert.assertThat; 027import static org.hamcrest.Matchers.allOf; 028import static org.hamcrest.Matchers.any; 029import static org.hamcrest.Matchers.anyOf; 030import static org.hamcrest.Matchers.containsString; 031import static org.hamcrest.Matchers.hasItem; 032 033import io.opentelemetry.api.trace.StatusCode; 034import io.opentelemetry.sdk.testing.junit5.OpenTelemetryExtension; 035import io.opentelemetry.sdk.trace.data.SpanData; 036import java.util.List; 037import java.util.concurrent.TimeUnit; 038import java.util.concurrent.atomic.AtomicReference; 039import java.util.stream.Collectors; 040import org.apache.hadoop.hbase.ConnectionExtension; 041import org.apache.hadoop.hbase.HRegionLocation; 042import org.apache.hadoop.hbase.MatcherPredicate; 043import org.apache.hadoop.hbase.MiniClusterExtension; 044import org.apache.hadoop.hbase.StartTestingClusterOption; 045import org.apache.hadoop.hbase.TableName; 046import org.apache.hadoop.hbase.Waiter; 047import org.apache.hadoop.hbase.client.AsyncAdmin; 048import org.apache.hadoop.hbase.client.AsyncConnection; 049import org.apache.hadoop.hbase.client.RegionInfo; 050import org.apache.hadoop.hbase.client.Table; 051import org.apache.hadoop.hbase.client.trace.StringTraceRenderer; 052import org.apache.hadoop.hbase.testclassification.MasterTests; 053import org.apache.hadoop.hbase.testclassification.MediumTests; 054import org.apache.hadoop.hbase.trace.TraceUtil; 055import org.apache.hadoop.hbase.util.Bytes; 056import org.hamcrest.Matcher; 057import org.hamcrest.Matchers; 058import org.junit.jupiter.api.BeforeEach; 059import org.junit.jupiter.api.Order; 060import org.junit.jupiter.api.Tag; 061import org.junit.jupiter.api.Test; 062import org.junit.jupiter.api.TestInfo; 063import org.junit.jupiter.api.extension.RegisterExtension; 064import org.slf4j.Logger; 065import org.slf4j.LoggerFactory; 066 067/** 068 * Test of master ProcedureV2 tracing. 069 */ 070@Tag(MasterTests.TAG) 071@Tag(MediumTests.TAG) 072public class TestProcedureTracing { 073 private static final Logger LOG = LoggerFactory.getLogger(TestProcedureTracing.class); 074 075 @RegisterExtension 076 @Order(1) 077 static final OpenTelemetryExtension otelExtension = OpenTelemetryExtension.create(); 078 079 @RegisterExtension 080 @Order(2) 081 static final MiniClusterExtension miniClusterExtension = MiniClusterExtension.newBuilder() 082 .setMiniClusterOption(StartTestingClusterOption.builder().numWorkers(3).build()).build(); 083 084 @RegisterExtension 085 @Order(3) 086 static final ConnectionExtension connectionExtension = 087 ConnectionExtension.createAsyncConnectionExtension(miniClusterExtension::createAsyncConnection); 088 private String testMethodName; 089 090 @BeforeEach 091 public void setTestMethod(TestInfo testInfo) { 092 testMethodName = testInfo.getTestMethod().get().getName(); 093 } 094 095 @Test 096 public void testCreateOpenDeleteTableSpans() throws Exception { 097 final TableName tableName = TableName.valueOf(testMethodName); 098 final AsyncConnection conn = connectionExtension.getAsyncConnection(); 099 final AsyncAdmin admin = conn.getAdmin(); 100 101 final AtomicReference<List<String>> tableRegionsRef = new AtomicReference<>(); 102 TraceUtil.trace(() -> { 103 try (final Table ignored = miniClusterExtension.getTestingUtility() 104 .createMultiRegionTable(tableName, Bytes.toBytes("fam"), 5)) { 105 final List<String> tableRegions = conn.getRegionLocator(tableName).getAllRegionLocations() 106 .get().stream().map(HRegionLocation::getRegion).map(RegionInfo::getEncodedName) 107 .collect(Collectors.toList()); 108 tableRegionsRef.set(tableRegions); 109 } 110 if (admin.tableExists(tableName).get()) { 111 if (admin.isTableEnabled(tableName).get()) { 112 admin.disableTable(tableName).get(); 113 } 114 admin.deleteTable(tableName).get(); 115 } 116 }, testMethodName); 117 118 final Matcher<SpanData> testSpanMatcher = allOf(hasName(testMethodName), hasEnded()); 119 Waiter.waitFor(conn.getConfiguration(), TimeUnit.MINUTES.toMillis(3), 120 new MatcherPredicate<>(otelExtension::getSpans, hasItem(testSpanMatcher))); 121 final List<SpanData> spans = otelExtension.getSpans(); 122 final StringTraceRenderer renderer = new StringTraceRenderer(spans); 123 renderer.render(LOG::debug); 124 125 // Expect to find a span for a CreateTableProcedure for the test table 126 final Matcher<SpanData> createTableProcedureSpanMatcher = allOf( 127 hasName( 128 allOf(containsString("CreateTableProcedure"), containsString("table=" + testMethodName))), 129 hasEnded(), hasStatusWithCode(StatusCode.OK), 130 hasAttributes(allOf(containsEntry(longKey("procId"), any(Long.class)), 131 containsEntry(longKey("parentProcId"), any(Long.class))))); 132 assertThat("Expected to find a span for a CreateTableProcedure for the test table", spans, 133 hasItem(createTableProcedureSpanMatcher)); 134 135 // Expect to find a span for a TransitRegionStateProcedure for the test table 136 final Matcher<SpanData> transitRegionStateProcedureSpanMatcher = allOf( 137 hasName(allOf(containsString("TransitRegionStateProcedure"), 138 containsString("table=" + testMethodName))), 139 hasEnded(), hasStatusWithCode(StatusCode.OK), 140 hasAttributes(allOf(containsEntry(longKey("procId"), any(Long.class)), 141 containsEntry(longKey("parentProcId"), any(Long.class))))); 142 assertThat("Expected to find a span for a TransitRegionStateProcedure for the test table", 143 spans, hasItem(transitRegionStateProcedureSpanMatcher)); 144 145 // Expect to find a span for an OpenRegionProcedure for a region of the test table 146 final List<Matcher<? super String>> tableRegionMatchers = 147 tableRegionsRef.get().stream().map(Matchers::containsString).collect(Collectors.toList()); 148 final Matcher<SpanData> openRegionProcedureSpanMatcher = 149 allOf(hasName(allOf(containsString("OpenRegionProcedure"), anyOf(tableRegionMatchers))), 150 hasEnded(), hasStatusWithCode(StatusCode.OK), 151 hasAttributes(allOf(containsEntry(longKey("procId"), any(Long.class)), 152 containsEntry(longKey("parentProcId"), any(Long.class))))); 153 assertThat("Expected to find a span for an OpenRegionProcedure for a region of the test table", 154 spans, hasItem(openRegionProcedureSpanMatcher)); 155 156 // Expect to find a span for a CloseRegionProcedure for a region of the test table 157 final Matcher<SpanData> closeRegionProcedureSpanMatcher = 158 allOf(hasName(allOf(containsString("CloseRegionProcedure"), anyOf(tableRegionMatchers))), 159 hasEnded(), hasStatusWithCode(StatusCode.OK), 160 hasAttributes(allOf(containsEntry(longKey("procId"), any(Long.class)), 161 containsEntry(longKey("parentProcId"), any(Long.class))))); 162 assertThat("Expected to find a span for a CloseRegionProcedure for a region of the test table", 163 spans, hasItem(closeRegionProcedureSpanMatcher)); 164 165 // Expect to find a span for a DisableTableProcedure for the test table 166 final Matcher<SpanData> disableTableProcedureSpanMatcher = allOf( 167 hasName( 168 allOf(containsString("DisableTableProcedure"), containsString("table=" + testMethodName))), 169 hasEnded(), hasStatusWithCode(StatusCode.OK), 170 hasAttributes(allOf(containsEntry(longKey("procId"), any(Long.class)), 171 containsEntry(longKey("parentProcId"), any(Long.class))))); 172 assertThat("Expected to find a span for a DisableTableProcedure for the test table", spans, 173 hasItem(disableTableProcedureSpanMatcher)); 174 175 // Expect to find a span for a DeleteTableProcedure for the test table 176 final Matcher<SpanData> deleteTableProcedureSpanMatcher = allOf( 177 hasName( 178 allOf(containsString("DeleteTableProcedure"), containsString("table=" + testMethodName))), 179 hasEnded(), hasStatusWithCode(StatusCode.OK), 180 hasAttributes(allOf(containsEntry(longKey("procId"), any(Long.class)), 181 containsEntry(longKey("parentProcId"), any(Long.class))))); 182 assertThat("Expected to find a span for a DeleteTableProcedure for the test table", spans, 183 hasItem(deleteTableProcedureSpanMatcher)); 184 } 185}