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.assertTrue;
022
023import com.codahale.metrics.Timer;
024import java.io.IOException;
025import java.util.Arrays;
026import org.apache.hadoop.conf.Configuration;
027import org.apache.hadoop.hbase.HBaseConfiguration;
028import org.apache.hadoop.hbase.HBaseTestingUtil;
029import org.apache.hadoop.hbase.TableName;
030import org.apache.hadoop.hbase.testclassification.ClientTests;
031import org.apache.hadoop.hbase.testclassification.MediumTests;
032import org.apache.hadoop.hbase.util.Bytes;
033import org.junit.jupiter.api.AfterAll;
034import org.junit.jupiter.api.BeforeAll;
035import org.junit.jupiter.api.Tag;
036import org.junit.jupiter.api.Test;
037
038import org.apache.hadoop.hbase.shaded.protobuf.generated.ClientProtos.ClientService;
039
040@Tag(MediumTests.TAG)
041@Tag(ClientTests.TAG)
042public class TestClientTableMetrics {
043
044  private static HBaseTestingUtil UTIL;
045  private static Connection CONN;
046  private static MetricsConnection METRICS;
047  private static final String tableName = "table_1";
048  private static final TableName TABLE_1 = TableName.valueOf(tableName);
049  private static final byte[] FAMILY = Bytes.toBytes("f");
050
051  @BeforeAll
052  public static void beforeClass() throws Exception {
053    Configuration conf = HBaseConfiguration.create();
054    conf.setBoolean(MetricsConnection.CLIENT_SIDE_METRICS_ENABLED_KEY, true);
055    conf.setBoolean(MetricsConnection.CLIENT_SIDE_TABLE_METRICS_ENABLED_KEY, true);
056    UTIL = new HBaseTestingUtil(conf);
057    UTIL.startMiniCluster(2);
058    UTIL.createTable(TABLE_1, FAMILY);
059    UTIL.waitTableAvailable(TABLE_1);
060    CONN = UTIL.getConnection();
061    METRICS = ((AsyncConnectionImpl) CONN.toAsyncConnection()).getConnectionMetrics().get();
062  }
063
064  @AfterAll
065  public static void afterClass() throws Exception {
066    UTIL.deleteTableIfAny(TABLE_1);
067    UTIL.shutdownMiniCluster();
068  }
069
070  @Test
071  public void testGetTableMetrics() throws IOException {
072    Table table = CONN.getTable(TABLE_1);
073    table.get(new Get(Bytes.toBytes("row1")));
074    table.get(new Get(Bytes.toBytes("row2")));
075    table.get(new Get(Bytes.toBytes("row3")));
076    table.close();
077
078    String metricKey =
079      "rpcCallDurationMs_" + ClientService.getDescriptor().getName() + "_Get_" + tableName;
080    verifyTableMetrics(metricKey, 3);
081  }
082
083  @Test
084  public void testMutateTableMetrics() throws IOException {
085    Table table = CONN.getTable(TABLE_1);
086    // PUT
087    Put put = new Put(Bytes.toBytes("row1"));
088    put.addColumn(FAMILY, Bytes.toBytes("name"), Bytes.toBytes("tom"));
089    table.put(put);
090    put = new Put(Bytes.toBytes("row2"));
091    put.addColumn(FAMILY, Bytes.toBytes("name"), Bytes.toBytes("jerry"));
092    table.put(put);
093    // DELETE
094    table.delete(new Delete(Bytes.toBytes("row1")));
095    table.close();
096
097    String metricKey =
098      "rpcCallDurationMs_" + ClientService.getDescriptor().getName() + "_Mutate(Put)_" + tableName;
099    verifyTableMetrics(metricKey, 2);
100
101    metricKey = "rpcCallDurationMs_" + ClientService.getDescriptor().getName() + "_Mutate(Delete)_"
102      + tableName;
103    verifyTableMetrics(metricKey, 1);
104  }
105
106  @Test
107  public void testScanTableMetrics() throws IOException {
108    Table table = CONN.getTable(TABLE_1);
109    table.getScanner(new Scan());
110    table.close();
111
112    String metricKey =
113      "rpcCallDurationMs_" + ClientService.getDescriptor().getName() + "_Scan_" + tableName;
114    verifyTableMetrics(metricKey, 1);
115  }
116
117  @Test
118  public void testMultiTableMetrics() throws IOException {
119    Table table = CONN.getTable(TABLE_1);
120    table.put(Arrays.asList(
121      new Put(Bytes.toBytes("row1")).addColumn(FAMILY, Bytes.toBytes("name"), Bytes.toBytes("tom")),
122      new Put(Bytes.toBytes("row2")).addColumn(FAMILY, Bytes.toBytes("name"),
123        Bytes.toBytes("jerry"))));
124    table.get(Arrays.asList(new Get(Bytes.toBytes("row1")), new Get(Bytes.toBytes("row2"))));
125    table.close();
126
127    String metricKey =
128      "rpcCallDurationMs_" + ClientService.getDescriptor().getName() + "_Multi_" + tableName;
129    verifyTableMetrics(metricKey, 2);
130  }
131
132  private static void verifyTableMetrics(String metricKey, int expectedVal) {
133    String numOpsSuffix = "_num_ops";
134    String p95Suffix = "_95th_percentile";
135    String p99Suffix = "_99th_percentile";
136    Timer timer = METRICS.getRpcTimers().get(metricKey);
137    long numOps = timer.getCount();
138    double p95 = timer.getSnapshot().get95thPercentile();
139    double p99 = timer.getSnapshot().get99thPercentile();
140    assertEquals(expectedVal, numOps, "metric: " + metricKey + numOpsSuffix + " val: " + numOps);
141    assertTrue(p95 >= 0, "metric: " + metricKey + p95Suffix + " val: " + p95);
142    assertTrue(p99 >= 0, "metric: " + metricKey + p99Suffix + " val: " + p99);
143  }
144}