001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license 003 * agreements. See the NOTICE file distributed with this work for additional information regarding 004 * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the 005 * "License"); you may not use this file except in compliance with the License. You may obtain a 006 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable 007 * law or agreed to in writing, software distributed under the License is distributed on an "AS IS" 008 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License 009 * for the specific language governing permissions and limitations under the License. 010 */ 011 012package org.apache.hadoop.hbase.coprocessor; 013 014import static org.junit.Assert.assertEquals; 015import static org.junit.Assert.assertNotNull; 016 017import java.io.IOException; 018import java.util.ArrayList; 019import java.util.HashSet; 020import java.util.Hashtable; 021import java.util.Iterator; 022import java.util.List; 023import java.util.Random; 024import java.util.Set; 025 026import javax.management.MBeanAttributeInfo; 027import javax.management.MBeanInfo; 028import javax.management.MBeanServerConnection; 029import javax.management.ObjectInstance; 030import javax.management.ObjectName; 031import javax.management.remote.JMXConnector; 032import javax.management.remote.JMXConnectorFactory; 033 034import org.apache.hadoop.conf.Configuration; 035import org.apache.hadoop.hbase.HBaseClassTestRule; 036import org.apache.hadoop.hbase.HBaseTestingUtility; 037import org.apache.hadoop.hbase.JMXListener; 038import org.apache.hadoop.hbase.TableName; 039import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; 040import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder; 041import org.apache.hadoop.hbase.client.Put; 042import org.apache.hadoop.hbase.client.Table; 043import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 044import org.apache.hadoop.hbase.testclassification.CoprocessorTests; 045import org.apache.hadoop.hbase.testclassification.MediumTests; 046import org.apache.hadoop.hbase.util.Bytes; 047import org.apache.hadoop.hbase.util.Threads; 048import org.junit.AfterClass; 049import org.junit.BeforeClass; 050import org.junit.ClassRule; 051import org.junit.Test; 052import org.junit.experimental.categories.Category; 053import org.slf4j.Logger; 054import org.slf4j.LoggerFactory; 055 056 057@Category({ CoprocessorTests.class, MediumTests.class }) 058public class TestMetaTableMetrics { 059 060 @ClassRule 061 public static final HBaseClassTestRule CLASS_RULE = 062 HBaseClassTestRule.forClass(TestMetaTableMetrics.class); 063 private static final Logger LOG = LoggerFactory.getLogger(TestMetaTableMetrics.class); 064 065 private static final HBaseTestingUtility UTIL = new HBaseTestingUtility(); 066 private static final TableName NAME1 = TableName.valueOf("TestExampleMetaTableMetricsOne"); 067 private static final byte[] FAMILY = Bytes.toBytes("f"); 068 private static final byte[] QUALIFIER = Bytes.toBytes("q"); 069 private static final ColumnFamilyDescriptor CFD = 070 ColumnFamilyDescriptorBuilder.newBuilder(FAMILY).build(); 071 private static final int NUM_ROWS = 5; 072 private static final String value = "foo"; 073 private static Configuration conf = null; 074 private static int connectorPort = 61120; 075 076 @BeforeClass 077 public static void setupBeforeClass() throws Exception { 078 079 conf = UTIL.getConfiguration(); 080 // Set system coprocessor so it can be applied to meta regions 081 UTIL.getConfiguration().set("hbase.coprocessor.region.classes", 082 MetaTableMetrics.class.getName()); 083 conf.set(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY, JMXListener.class.getName()); 084 Random rand = new Random(); 085 for (int i = 0; i < 10; i++) { 086 do { 087 int sign = i % 2 == 0 ? 1 : -1; 088 connectorPort += sign * rand.nextInt(100); 089 } while (!HBaseTestingUtility.available(connectorPort)); 090 try { 091 conf.setInt("regionserver.rmi.registry.port", connectorPort); 092 UTIL.startMiniCluster(1); 093 break; 094 } catch (Exception e) { 095 LOG.debug("Encountered exception when starting cluster. Trying port " + connectorPort, e); 096 try { 097 // this is to avoid "IllegalStateException: A mini-cluster is already running" 098 UTIL.shutdownMiniCluster(); 099 } catch (Exception ex) { 100 LOG.debug("Encountered exception shutting down cluster", ex); 101 } 102 } 103 } 104 UTIL.getAdmin() 105 .createTable(TableDescriptorBuilder.newBuilder(NAME1) 106 .setColumnFamily(CFD) 107 .build()); 108 } 109 110 @AfterClass 111 public static void tearDown() throws Exception { 112 UTIL.shutdownMiniCluster(); 113 } 114 115 private void writeData(Table t) throws IOException { 116 List<Put> puts = new ArrayList<>(NUM_ROWS); 117 for (int i = 0; i < NUM_ROWS; i++) { 118 Put p = new Put(Bytes.toBytes(i + 1)); 119 p.addColumn(FAMILY, QUALIFIER, Bytes.toBytes(value)); 120 puts.add(p); 121 } 122 t.put(puts); 123 } 124 125 private Set<String> readJmxMetricsWithRetry() throws IOException { 126 final int count = 0; 127 for (int i = 0; i < 10; i++) { 128 Set<String> metrics = readJmxMetrics(); 129 if (metrics != null) { 130 return metrics; 131 } 132 LOG.warn("Failed to get jmxmetrics... sleeping, retrying; " + i + " of " + count + " times"); 133 Threads.sleep(1000); 134 } 135 return null; 136 } 137 138 /** 139 * Read the attributes from Hadoop->HBase->RegionServer->MetaTableMetrics in JMX 140 * @throws IOException when fails to retrieve jmx metrics. 141 */ 142 // this method comes from this class: TestStochasticBalancerJmxMetrics with minor modifications. 143 private Set<String> readJmxMetrics() throws IOException { 144 JMXConnector connector = null; 145 ObjectName target = null; 146 MBeanServerConnection mb = null; 147 try { 148 connector = 149 JMXConnectorFactory.connect(JMXListener.buildJMXServiceURL(connectorPort, connectorPort)); 150 mb = connector.getMBeanServerConnection(); 151 152 @SuppressWarnings("JdkObsolete") 153 Hashtable<String, String> pairs = new Hashtable<>(); 154 pairs.put("service", "HBase"); 155 pairs.put("name", "RegionServer"); 156 pairs.put("sub", 157 "Coprocessor.Region.CP_org.apache.hadoop.hbase.coprocessor" 158 + ".MetaTableMetrics"); 159 target = new ObjectName("Hadoop", pairs); 160 MBeanInfo beanInfo = mb.getMBeanInfo(target); 161 162 Set<String> existingAttrs = new HashSet<>(); 163 for (MBeanAttributeInfo attrInfo : beanInfo.getAttributes()) { 164 existingAttrs.add(attrInfo.getName()); 165 } 166 return existingAttrs; 167 } catch (Exception e) { 168 LOG.warn("Failed to get bean." + target, e); 169 if (mb != null) { 170 Set<ObjectInstance> instances = mb.queryMBeans(null, null); 171 Iterator<ObjectInstance> iterator = instances.iterator(); 172 LOG.warn("MBean Found:"); 173 while (iterator.hasNext()) { 174 ObjectInstance instance = iterator.next(); 175 LOG.warn("Class Name: " + instance.getClassName()); 176 LOG.warn("Object Name: " + instance.getObjectName()); 177 } 178 } 179 } finally { 180 if (connector != null) { 181 try { 182 connector.close(); 183 } catch (Exception e) { 184 e.printStackTrace(); 185 } 186 } 187 } 188 return null; 189 } 190 191 // verifies meta table metrics exist from jmx 192 // for one table, there should be 5 MetaTable_table_<TableName> metrics. 193 // such as: 194 // [Time-limited test] example.TestMetaTableMetrics(204): == 195 // MetaTable_table_TestExampleMetaTableMetricsOne_request_count 196 // [Time-limited test] example.TestMetaTableMetrics(204): == 197 // MetaTable_table_TestExampleMetaTableMetricsOne_request_mean_rate 198 // [Time-limited test] example.TestMetaTableMetrics(204): == 199 // MetaTable_table_TestExampleMetaTableMetricsOne_request_1min_rate 200 // [Time-limited test] example.TestMetaTableMetrics(204): == 201 // MetaTable_table_TestExampleMetaTableMetricsOne_request_5min_rate 202 // [Time-limited test] example.TestMetaTableMetrics(204): == 203 // MetaTable_table_TestExampleMetaTableMetricsOne_request_15min_rate 204 @Test 205 public void test() throws IOException, InterruptedException { 206 try (Table t = UTIL.getConnection().getTable(NAME1)) { 207 writeData(t); 208 // Flush the data 209 UTIL.flush(NAME1); 210 // Issue a compaction 211 UTIL.compact(NAME1, true); 212 Thread.sleep(2000); 213 } 214 Set<String> jmxMetrics = readJmxMetricsWithRetry(); 215 assertNotNull(jmxMetrics); 216 long name1TableMetricsCount = 217 jmxMetrics.stream().filter(metric -> metric.contains("MetaTable_table_" + NAME1)).count(); 218 assertEquals(5L, name1TableMetricsCount); 219 220 String putWithClientMetricNameRegex = "MetaTable_client_.+_put_request.*"; 221 long putWithClientMetricsCount = 222 jmxMetrics.stream().filter(metric -> metric.matches(putWithClientMetricNameRegex)) 223 .count(); 224 assertEquals(5L, putWithClientMetricsCount); 225 226 227 228 229 } 230 231}