1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.hadoop.hbase.coprocessor;
21
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assert.fail;
24
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.Hashtable;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Random;
35 import java.util.Set;
36
37 import javax.management.MBeanAttributeInfo;
38 import javax.management.MBeanInfo;
39 import javax.management.MBeanServerConnection;
40 import javax.management.ObjectInstance;
41 import javax.management.ObjectName;
42 import javax.management.remote.JMXConnector;
43 import javax.management.remote.JMXConnectorFactory;
44
45 import org.apache.hadoop.conf.Configuration;
46 import org.apache.hadoop.hbase.HBaseTestingUtility;
47 import org.apache.hadoop.hbase.JMXListener;
48 import org.apache.hadoop.hbase.TableName;
49 import org.apache.hadoop.hbase.Waiter.Predicate;
50 import org.apache.hadoop.hbase.client.Get;
51 import org.apache.hadoop.hbase.client.Put;
52 import org.apache.hadoop.hbase.client.Table;
53 import org.apache.hadoop.hbase.testclassification.CoprocessorTests;
54 import org.apache.hadoop.hbase.testclassification.MediumTests;
55 import org.apache.hadoop.hbase.util.Bytes;
56 import org.hamcrest.CustomTypeSafeMatcher;
57 import org.hamcrest.Matcher;
58 import org.hamcrest.core.AllOf;
59 import org.junit.AfterClass;
60 import org.junit.BeforeClass;
61 import org.junit.Test;
62 import org.junit.experimental.categories.Category;
63 import org.slf4j.Logger;
64 import org.slf4j.LoggerFactory;
65
66
67 @Category({ CoprocessorTests.class, MediumTests.class })
68 public class TestMetaTableMetrics {
69
70 private static final Logger LOG = LoggerFactory.getLogger(TestMetaTableMetrics.class);
71
72 private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
73 private static final TableName NAME1 = TableName.valueOf("TestExampleMetaTableMetricsOne");
74 private static final byte[] FAMILY = Bytes.toBytes("f");
75 private static final byte[] QUALIFIER = Bytes.toBytes("q");
76 private static final int NUM_ROWS = 5;
77 private static final String value = "foo";
78 private static final String METRICS_ATTRIBUTE_NAME_PREFIX = "MetaTable_";
79 private static final List<String> METRICS_ATTRIBUTE_NAME_POSTFIXES =
80 Arrays.asList("_count", "_mean_rate", "_1min_rate", "_5min_rate", "_15min_rate");
81 private static int connectorPort = 61120;
82
83 private final byte[] cf = Bytes.toBytes("info");
84 private final byte[] col = Bytes.toBytes("any");
85 private byte[] tablename;
86 private final int nthreads = 20;
87
88 @BeforeClass
89 public static void setupBeforeClass() throws Exception {
90
91 Configuration conf = UTIL.getConfiguration();
92
93 UTIL.getConfiguration().set("hbase.coprocessor.region.classes",
94 MetaTableMetrics.class.getName());
95 conf.set(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY, JMXListener.class.getName());
96 Random rand = new Random();
97 for (int i = 0; i < 10; i++) {
98 do {
99 int sign = i % 2 == 0 ? 1 : -1;
100 connectorPort += sign * rand.nextInt(100);
101 } while (!HBaseTestingUtility.available(connectorPort));
102 try {
103 conf.setInt("regionserver.rmi.registry.port", connectorPort);
104 UTIL.startMiniCluster(1);
105 break;
106 } catch (Exception e) {
107 LOG.debug("Encountered exception when starting cluster. Trying port {}", connectorPort, e);
108 try {
109
110 UTIL.shutdownMiniCluster();
111 } catch (Exception ex) {
112 LOG.debug("Encountered exception shutting down cluster", ex);
113 }
114 }
115 }
116 }
117
118 @AfterClass
119 public static void tearDown() throws Exception {
120 UTIL.shutdownMiniCluster();
121 }
122
123
124
125
126
127
128
129
130
131
132
133 @Test
134 public void testMetaTableMetricsInJmx() throws Exception {
135 UTIL.createTable(NAME1, new byte[][]{FAMILY});
136 writeData(NAME1);
137 readingSingleRowFromTheMetaTable();
138 UTIL.deleteTable(NAME1);
139
140 UTIL.waitFor(30000, 2000, true, new Predicate<IOException>() {
141
142 @Override
143 public boolean evaluate() throws IOException {
144 Map<String, Double> jmxMetrics = readMetaTableJmxMetrics();
145 boolean allMetricsFound = AllOf.allOf(
146 containsPositiveJmxAttributesFor("MetaTable_get_request"),
147 containsPositiveJmxAttributesFor("MetaTable_put_request"),
148 containsPositiveJmxAttributesFor("MetaTable_delete_request"),
149 containsPositiveJmxAttributesFor("MetaTable_region_.+_lossy_request"),
150 containsPositiveJmxAttributesFor("MetaTable_table_" + NAME1 + "_request"),
151 containsPositiveJmxAttributesFor("MetaTable_client_.+_put_request"),
152 containsPositiveJmxAttributesFor("MetaTable_client_.+_get_request"),
153 containsPositiveJmxAttributesFor("MetaTable_client_.+_delete_request"),
154 containsPositiveJmxAttributesFor("MetaTable_client_.+_lossy_request")
155 ).matches(jmxMetrics);
156
157 if (allMetricsFound) {
158 LOG.info("all the meta table metrics found with positive values: {}", jmxMetrics);
159 } else {
160 LOG.warn("couldn't find all the meta table metrics with positive values: {}", jmxMetrics);
161 }
162 return allMetricsFound;
163 }
164 });
165
166 }
167
168 @Test
169 public void testConcurrentAccess() {
170 try {
171 tablename = Bytes.toBytes("hbase:meta");
172 int numRows = 3000;
173 int numRowsInTableBefore = UTIL.countRows(TableName.valueOf(tablename));
174 putData(numRows);
175 Thread.sleep(2000);
176 int numRowsInTableAfter = UTIL.countRows(TableName.valueOf(tablename));
177 assertTrue(numRowsInTableAfter >= numRowsInTableBefore + numRows);
178 getData(numRows);
179 } catch (InterruptedException e) {
180 LOG.info("Caught InterruptedException while testConcurrentAccess: {}", e.getMessage());
181 fail();
182 } catch (IOException e) {
183 LOG.info("Caught IOException while testConcurrentAccess: {}", e.getMessage());
184 fail();
185 }
186 }
187
188 private void writeData(TableName tableName) throws IOException {
189 try (Table t = UTIL.getConnection().getTable(tableName)) {
190 List<Put> puts = new ArrayList<>(NUM_ROWS);
191 for (int i = 0; i < NUM_ROWS; i++) {
192 Put p = new Put(Bytes.toBytes(i + 1));
193 p.addColumn(FAMILY, QUALIFIER, Bytes.toBytes(value));
194 puts.add(p);
195 }
196 t.put(puts);
197 }
198 }
199
200 private void readingSingleRowFromTheMetaTable() throws IOException {
201 TableName metaTableName = TableName.valueOf(Bytes.toBytes("hbase:meta"));
202 try (Table metaTable = UTIL.getConnection().getTable(metaTableName)) {
203 Get get = new Get(Bytes.toBytes(1));
204 metaTable.get(get);
205 }
206 }
207
208 private Matcher<Map<String, Double>> containsPositiveJmxAttributesFor(final String regexp) {
209 return new CustomTypeSafeMatcher<Map<String, Double>>(
210 "failed to find all the 5 positive JMX attributes for: " + regexp) {
211
212 @Override
213 protected boolean matchesSafely(final Map<String, Double> values) {
214 for (String key : values.keySet()) {
215 for (String metricsNamePostfix : METRICS_ATTRIBUTE_NAME_POSTFIXES) {
216 if (key.matches(regexp + metricsNamePostfix) && values.get(key) > 0) {
217 return true;
218 }
219 }
220 }
221 return false;
222 }
223 };
224 }
225
226
227
228
229
230 private Map<String, Double> readMetaTableJmxMetrics() throws IOException {
231 JMXConnector connector = null;
232 ObjectName target = null;
233 MBeanServerConnection mb = null;
234 try {
235 connector =
236 JMXConnectorFactory.connect(JMXListener.buildJMXServiceURL(connectorPort, connectorPort));
237 mb = connector.getMBeanServerConnection();
238
239 @SuppressWarnings("JdkObsolete")
240 Hashtable<String, String> pairs = new Hashtable<>();
241 pairs.put("service", "HBase");
242 pairs.put("name", "RegionServer");
243 pairs.put("sub",
244 "Coprocessor.Region.CP_org.apache.hadoop.hbase.coprocessor.MetaTableMetrics");
245 target = new ObjectName("Hadoop", pairs);
246 MBeanInfo beanInfo = mb.getMBeanInfo(target);
247
248 Map<String, Double> existingAttrs = new HashMap<>();
249 for (MBeanAttributeInfo attrInfo : beanInfo.getAttributes()) {
250 Object value = mb.getAttribute(target, attrInfo.getName());
251 if (attrInfo.getName().startsWith(METRICS_ATTRIBUTE_NAME_PREFIX)
252 && value instanceof Number) {
253 existingAttrs.put(attrInfo.getName(), Double.parseDouble(value.toString()));
254 }
255 }
256 LOG.info("MBean Found: {}", target);
257 return existingAttrs;
258 } catch (Exception e) {
259 LOG.warn("Failed to get Meta Table Metrics bean (will retry later): {}", target, e);
260 if (mb != null) {
261 Set<ObjectInstance> instances = mb.queryMBeans(null, null);
262 Iterator<ObjectInstance> iterator = instances.iterator();
263 LOG.debug("All the MBeans we found:");
264 while (iterator.hasNext()) {
265 ObjectInstance instance = iterator.next();
266 LOG.debug("Class and object name: {} [{}]", instance.getClassName(),
267 instance.getObjectName());
268 }
269 }
270 } finally {
271 if (connector != null) {
272 try {
273 connector.close();
274 } catch (Exception e) {
275 e.printStackTrace();
276 }
277 }
278 }
279 return Collections.emptyMap();
280 }
281
282 private void putData(int nrows) throws InterruptedException {
283 LOG.info("Putting {} rows in hbase:meta", nrows);
284 Thread[] threads = new Thread[nthreads];
285 for (int i = 1; i <= nthreads; i++) {
286 threads[i - 1] = new PutThread(1, nrows);
287 }
288 startThreadsAndWaitToJoin(threads);
289 }
290
291 private void getData(int nrows) throws InterruptedException {
292 LOG.info("Getting {} rows from hbase:meta", nrows);
293 Thread[] threads = new Thread[nthreads];
294 for (int i = 1; i <= nthreads; i++) {
295 threads[i - 1] = new GetThread(1, nrows);
296 }
297 startThreadsAndWaitToJoin(threads);
298 }
299
300 private void startThreadsAndWaitToJoin(Thread[] threads) throws InterruptedException {
301 for (int i = 1; i <= nthreads; i++) {
302 threads[i - 1].start();
303 }
304 for (int i = 1; i <= nthreads; i++) {
305 threads[i - 1].join();
306 }
307 }
308
309 private class PutThread extends Thread {
310 int start;
311 int end;
312
313 PutThread(int start, int end) {
314 this.start = start;
315 this.end = end;
316 }
317
318 @Override
319 public void run() {
320 try (Table table = UTIL.getConnection().getTable(TableName.valueOf(tablename))) {
321 for (int i = start; i <= end; i++) {
322 Put p = new Put(Bytes.toBytes(String.format("tableName,rowKey%d,region%d", i, i)));
323 p.addColumn(cf, col, Bytes.toBytes("Value" + i));
324 table.put(p);
325 }
326 } catch (IOException e) {
327 LOG.warn("Caught IOException while PutThread operation", e);
328 }
329 }
330 }
331
332 private class GetThread extends Thread {
333 int start;
334 int end;
335
336 GetThread(int start, int end) {
337 this.start = start;
338 this.end = end;
339 }
340
341 @Override
342 public void run() {
343 try (Table table = UTIL.getConnection().getTable(TableName.valueOf(tablename))) {
344 for (int i = start; i <= end; i++) {
345 Get get = new Get(Bytes.toBytes(String.format("tableName,rowKey%d,region%d", i, i)));
346 table.get(get);
347 }
348 } catch (IOException e) {
349 LOG.warn("Caught IOException while GetThread operation", e);
350 }
351 }
352 }
353
354 }