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.junit.jupiter.api.Assertions.assertEquals; 021import static org.junit.jupiter.api.Assertions.assertNotNull; 022import static org.junit.jupiter.api.Assertions.assertNull; 023 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.List; 027import java.util.concurrent.CompletableFuture; 028import org.apache.hadoop.hbase.HBaseTestingUtil; 029import org.apache.hadoop.hbase.TableName; 030import org.apache.hadoop.hbase.regionserver.MetricsRegionServer; 031import org.apache.hadoop.hbase.regionserver.MetricsRegionServerSource; 032import org.apache.hadoop.hbase.regionserver.MetricsRegionServerSourceImpl; 033import org.apache.hadoop.hbase.testclassification.ClientTests; 034import org.apache.hadoop.hbase.testclassification.MediumTests; 035import org.apache.hadoop.hbase.util.Bytes; 036import org.apache.hadoop.hbase.util.JVMClusterUtil; 037import org.junit.jupiter.api.AfterAll; 038import org.junit.jupiter.api.BeforeAll; 039import org.junit.jupiter.api.Tag; 040import org.junit.jupiter.api.Test; 041 042import org.apache.hbase.thirdparty.com.google.common.io.Closeables; 043 044@Tag(MediumTests.TAG) 045@Tag(ClientTests.TAG) 046public class TestAsyncTableQueryMetrics { 047 048 private static final HBaseTestingUtil UTIL = new HBaseTestingUtil(); 049 050 private static final TableName TABLE_NAME = TableName.valueOf("ResultMetrics"); 051 052 private static final byte[] CF = Bytes.toBytes("cf"); 053 054 private static final byte[] CQ = Bytes.toBytes("cq"); 055 056 private static final byte[] VALUE = Bytes.toBytes("value"); 057 058 private static final byte[] ROW_1 = Bytes.toBytes("zzz1"); 059 private static final byte[] ROW_2 = Bytes.toBytes("zzz2"); 060 private static final byte[] ROW_3 = Bytes.toBytes("zzz3"); 061 062 private static AsyncConnection CONN; 063 064 @BeforeAll 065 public static void setUp() throws Exception { 066 UTIL.startMiniCluster(3); 067 // Create 3 rows in the table, with rowkeys starting with "zzz*" so that 068 // scan are forced to hit all the regions. 069 try (Table table = UTIL.createMultiRegionTable(TABLE_NAME, CF)) { 070 table.put(Arrays.asList(new Put(ROW_1).addColumn(CF, CQ, VALUE), 071 new Put(ROW_2).addColumn(CF, CQ, VALUE), new Put(ROW_3).addColumn(CF, CQ, VALUE))); 072 } 073 CONN = ConnectionFactory.createAsyncConnection(UTIL.getConfiguration()).get(); 074 CONN.getAdmin().flush(TABLE_NAME).join(); 075 } 076 077 @AfterAll 078 public static void tearDown() throws Exception { 079 Closeables.close(CONN, true); 080 UTIL.shutdownMiniCluster(); 081 } 082 083 @Test 084 public void itTestsGets() throws Exception { 085 // Test a single Get 086 Get g1 = new Get(ROW_1); 087 g1.setQueryMetricsEnabled(true); 088 089 long bbs = getClusterBlockBytesScanned(); 090 Result result = CONN.getTable(TABLE_NAME).get(g1).get(); 091 bbs += result.getMetrics().getBlockBytesScanned(); 092 assertNotNull(result.getMetrics()); 093 assertEquals(getClusterBlockBytesScanned(), bbs); 094 095 // Test multigets 096 Get g2 = new Get(ROW_2); 097 g2.setQueryMetricsEnabled(true); 098 099 Get g3 = new Get(ROW_3); 100 g3.setQueryMetricsEnabled(true); 101 102 List<CompletableFuture<Result>> futures = CONN.getTable(TABLE_NAME).get(List.of(g1, g2, g3)); 103 104 for (CompletableFuture<Result> future : futures) { 105 result = future.join(); 106 assertNotNull(result.getMetrics()); 107 bbs += result.getMetrics().getBlockBytesScanned(); 108 } 109 110 assertEquals(getClusterBlockBytesScanned(), bbs); 111 112 g1.setCheckExistenceOnly(true); 113 g2.setCheckExistenceOnly(true); 114 g3.setCheckExistenceOnly(true); 115 116 result = CONN.getTable(TABLE_NAME).get(g1).get(); 117 assertEquals(Boolean.TRUE, result.getExists()); 118 assertNotNull(result.getMetrics()); 119 bbs += result.getMetrics().getBlockBytesScanned(); 120 assertEquals(getClusterBlockBytesScanned(), bbs); 121 122 futures = CONN.getTable(TABLE_NAME).get(List.of(g1, g2, g3)); 123 for (CompletableFuture<Result> future : futures) { 124 result = future.join(); 125 assertEquals(Boolean.TRUE, result.getExists()); 126 assertNotNull(result.getMetrics()); 127 bbs += result.getMetrics().getBlockBytesScanned(); 128 } 129 assertEquals(getClusterBlockBytesScanned(), bbs); 130 } 131 132 @Test 133 public void itTestsDefaultGetNoMetrics() throws Exception { 134 // Test a single Get 135 Get g1 = new Get(ROW_1); 136 137 Result result = CONN.getTable(TABLE_NAME).get(g1).get(); 138 assertNull(result.getMetrics()); 139 140 // Test multigets 141 Get g2 = new Get(ROW_2); 142 Get g3 = new Get(ROW_3); 143 List<CompletableFuture<Result>> futures = CONN.getTable(TABLE_NAME).get(List.of(g1, g2, g3)); 144 futures.forEach(f -> assertNull(f.join().getMetrics())); 145 146 } 147 148 @Test 149 public void itTestsScans() { 150 Scan scan = new Scan(); 151 scan.setQueryMetricsEnabled(true); 152 153 long bbs = getClusterBlockBytesScanned(); 154 try (ResultScanner scanner = CONN.getTable(TABLE_NAME).getScanner(scan)) { 155 for (Result result : scanner) { 156 assertNotNull(result.getMetrics()); 157 bbs += result.getMetrics().getBlockBytesScanned(); 158 assertEquals(getClusterBlockBytesScanned(), bbs); 159 } 160 } 161 } 162 163 @Test 164 public void itTestsDefaultScanNoMetrics() { 165 Scan scan = new Scan(); 166 167 try (ResultScanner scanner = CONN.getTable(TABLE_NAME).getScanner(scan)) { 168 for (Result result : scanner) { 169 assertNull(result.getMetrics()); 170 } 171 } 172 } 173 174 @Test 175 public void itTestsAtomicOperations() { 176 CheckAndMutate cam = CheckAndMutate.newBuilder(ROW_1).ifEquals(CF, CQ, VALUE) 177 .queryMetricsEnabled(true).build(new Put(ROW_1).addColumn(CF, CQ, VALUE)); 178 179 long bbs = getClusterBlockBytesScanned(); 180 CheckAndMutateResult result = CONN.getTable(TABLE_NAME).checkAndMutate(cam).join(); 181 QueryMetrics metrics = result.getMetrics(); 182 183 assertNotNull(metrics); 184 assertEquals(getClusterBlockBytesScanned(), bbs + metrics.getBlockBytesScanned()); 185 186 bbs = getClusterBlockBytesScanned(); 187 List<CheckAndMutate> batch = new ArrayList<>(); 188 batch.add(cam); 189 batch.add(CheckAndMutate.newBuilder(ROW_2).queryMetricsEnabled(true).ifEquals(CF, CQ, VALUE) 190 .build(new Put(ROW_2).addColumn(CF, CQ, VALUE))); 191 batch.add(CheckAndMutate.newBuilder(ROW_3).queryMetricsEnabled(true).ifEquals(CF, CQ, VALUE) 192 .build(new Put(ROW_3).addColumn(CF, CQ, VALUE))); 193 194 List<Object> res = CONN.getTable(TABLE_NAME).batchAll(batch).join(); 195 long totalBbs = res.stream() 196 .mapToLong(r -> ((CheckAndMutateResult) r).getMetrics().getBlockBytesScanned()).sum(); 197 assertEquals(getClusterBlockBytesScanned(), bbs + totalBbs); 198 199 bbs = getClusterBlockBytesScanned(); 200 201 // flush to force fetch from disk 202 CONN.getAdmin().flush(TABLE_NAME).join(); 203 List<CompletableFuture<Object>> futures = CONN.getTable(TABLE_NAME).batch(batch); 204 205 totalBbs = futures.stream().map(CompletableFuture::join) 206 .mapToLong(r -> ((CheckAndMutateResult) r).getMetrics().getBlockBytesScanned()).sum(); 207 assertEquals(getClusterBlockBytesScanned(), bbs + totalBbs); 208 } 209 210 @Test 211 public void itTestsDefaultAtomicOperations() { 212 CheckAndMutate cam = CheckAndMutate.newBuilder(ROW_1).ifEquals(CF, CQ, VALUE) 213 .build(new Put(ROW_1).addColumn(CF, CQ, VALUE)); 214 215 CheckAndMutateResult result = CONN.getTable(TABLE_NAME).checkAndMutate(cam).join(); 216 QueryMetrics metrics = result.getMetrics(); 217 218 assertNull(metrics); 219 220 List<CheckAndMutate> batch = new ArrayList<>(); 221 batch.add(cam); 222 batch.add(CheckAndMutate.newBuilder(ROW_2).ifEquals(CF, CQ, VALUE) 223 .build(new Put(ROW_2).addColumn(CF, CQ, VALUE))); 224 batch.add(CheckAndMutate.newBuilder(ROW_3).ifEquals(CF, CQ, VALUE) 225 .build(new Put(ROW_3).addColumn(CF, CQ, VALUE))); 226 227 List<Object> res = CONN.getTable(TABLE_NAME).batchAll(batch).join(); 228 for (Object r : res) { 229 assertNull(((CheckAndMutateResult) r).getMetrics()); 230 } 231 232 // flush to force fetch from disk 233 CONN.getAdmin().flush(TABLE_NAME).join(); 234 List<CompletableFuture<Object>> futures = CONN.getTable(TABLE_NAME).batch(batch); 235 236 for (CompletableFuture<Object> future : futures) { 237 Object r = future.join(); 238 assertNull(((CheckAndMutateResult) r).getMetrics()); 239 } 240 } 241 242 private static long getClusterBlockBytesScanned() { 243 long bbs = 0L; 244 245 for (JVMClusterUtil.RegionServerThread rs : UTIL.getHBaseCluster().getRegionServerThreads()) { 246 MetricsRegionServer metrics = rs.getRegionServer().getMetrics(); 247 MetricsRegionServerSourceImpl source = 248 (MetricsRegionServerSourceImpl) metrics.getMetricsSource(); 249 250 bbs += source.getMetricsRegistry() 251 .getCounter(MetricsRegionServerSource.BLOCK_BYTES_SCANNED_KEY, 0L).value(); 252 } 253 254 return bbs; 255 } 256}