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; 019 020import static org.junit.Assert.assertEquals; 021import static org.junit.Assert.assertFalse; 022import static org.junit.Assert.assertNotNull; 023import static org.junit.Assert.assertTrue; 024import static org.junit.Assert.fail; 025 026import com.codahale.metrics.Histogram; 027import com.codahale.metrics.Snapshot; 028import com.codahale.metrics.UniformReservoir; 029import java.io.BufferedReader; 030import java.io.ByteArrayInputStream; 031import java.io.File; 032import java.io.FileWriter; 033import java.io.IOException; 034import java.io.InputStreamReader; 035import java.lang.reflect.Constructor; 036import java.lang.reflect.InvocationTargetException; 037import java.nio.charset.StandardCharsets; 038import java.util.LinkedList; 039import java.util.NoSuchElementException; 040import java.util.Properties; 041import java.util.Queue; 042import org.apache.hadoop.fs.FSDataInputStream; 043import org.apache.hadoop.fs.FileSystem; 044import org.apache.hadoop.fs.Path; 045import org.apache.hadoop.hbase.PerformanceEvaluation.RandomReadTest; 046import org.apache.hadoop.hbase.PerformanceEvaluation.Status; 047import org.apache.hadoop.hbase.PerformanceEvaluation.TestOptions; 048import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; 049import org.apache.hadoop.hbase.client.Connection; 050import org.apache.hadoop.hbase.client.TableDescriptor; 051import org.apache.hadoop.hbase.regionserver.CompactingMemStore; 052import org.apache.hadoop.hbase.testclassification.MiscTests; 053import org.apache.hadoop.hbase.testclassification.SmallTests; 054import org.apache.hadoop.hbase.util.GsonUtil; 055import org.junit.ClassRule; 056import org.junit.Test; 057import org.junit.experimental.categories.Category; 058 059import org.apache.hbase.thirdparty.com.google.gson.Gson; 060 061@Category({ MiscTests.class, SmallTests.class }) 062public class TestPerformanceEvaluation { 063 @ClassRule 064 public static final HBaseClassTestRule CLASS_RULE = 065 HBaseClassTestRule.forClass(TestPerformanceEvaluation.class); 066 067 private static final HBaseTestingUtil HTU = new HBaseTestingUtil(); 068 069 @Test 070 public void testDefaultInMemoryCompaction() { 071 PerformanceEvaluation.TestOptions defaultOpts = new PerformanceEvaluation.TestOptions(); 072 assertEquals(CompactingMemStore.COMPACTING_MEMSTORE_TYPE_DEFAULT, 073 defaultOpts.getInMemoryCompaction().toString()); 074 TableDescriptor tableDescriptor = PerformanceEvaluation.getTableDescriptor(defaultOpts); 075 for (ColumnFamilyDescriptor familyDescriptor : tableDescriptor.getColumnFamilies()) { 076 assertEquals(CompactingMemStore.COMPACTING_MEMSTORE_TYPE_DEFAULT, 077 familyDescriptor.getInMemoryCompaction().toString()); 078 } 079 } 080 081 @Test 082 public void testSerialization() { 083 PerformanceEvaluation.TestOptions options = new PerformanceEvaluation.TestOptions(); 084 assertFalse(options.isAutoFlush()); 085 options.setAutoFlush(true); 086 Gson gson = GsonUtil.createGson().create(); 087 String optionsString = gson.toJson(options); 088 PerformanceEvaluation.TestOptions optionsDeserialized = 089 gson.fromJson(optionsString, PerformanceEvaluation.TestOptions.class); 090 assertTrue(optionsDeserialized.isAutoFlush()); 091 } 092 093 /** 094 * Exercise the mr spec writing. Simple assertions to make sure it is basically working. 095 */ 096 @Test 097 public void testWriteInputFile() throws IOException { 098 TestOptions opts = new PerformanceEvaluation.TestOptions(); 099 final int clients = 10; 100 opts.setNumClientThreads(clients); 101 opts.setPerClientRunRows(10); 102 Path dir = 103 PerformanceEvaluation.writeInputFile(HTU.getConfiguration(), opts, HTU.getDataTestDir()); 104 FileSystem fs = FileSystem.get(HTU.getConfiguration()); 105 Path p = new Path(dir, PerformanceEvaluation.JOB_INPUT_FILENAME); 106 long len = fs.getFileStatus(p).getLen(); 107 assertTrue(len > 0); 108 byte[] content = new byte[(int) len]; 109 try (FSDataInputStream dis = fs.open(p)) { 110 dis.readFully(content); 111 BufferedReader br = new BufferedReader( 112 new InputStreamReader(new ByteArrayInputStream(content), StandardCharsets.UTF_8)); 113 int count = 0; 114 while (br.readLine() != null) { 115 count++; 116 } 117 assertEquals(clients, count); 118 } 119 } 120 121 @Test 122 public void testSizeCalculation() { 123 TestOptions opts = new PerformanceEvaluation.TestOptions(); 124 opts = PerformanceEvaluation.calculateRowsAndSize(opts); 125 long rows = opts.getPerClientRunRows(); 126 // Default row count 127 final int defaultPerClientRunRows = 1024 * 1024; 128 assertEquals(defaultPerClientRunRows, rows); 129 // If size is 2G, then twice the row count. 130 opts.setSize(2.0f); 131 opts = PerformanceEvaluation.calculateRowsAndSize(opts); 132 assertEquals(defaultPerClientRunRows * 2, opts.getPerClientRunRows()); 133 // If two clients, then they get half the rows each. 134 opts.setNumClientThreads(2); 135 opts = PerformanceEvaluation.calculateRowsAndSize(opts); 136 assertEquals(defaultPerClientRunRows, opts.getPerClientRunRows()); 137 // What if valueSize is 'random'? Then half of the valueSize so twice the rows. 138 opts.valueRandom = true; 139 opts = PerformanceEvaluation.calculateRowsAndSize(opts); 140 assertEquals(defaultPerClientRunRows * 2, opts.getPerClientRunRows()); 141 } 142 143 @Test 144 public void testRandomReadCalculation() { 145 TestOptions opts = new PerformanceEvaluation.TestOptions(); 146 opts = PerformanceEvaluation.calculateRowsAndSize(opts); 147 long rows = opts.getPerClientRunRows(); 148 // Default row count 149 final int defaultPerClientRunRows = 1024 * 1024; 150 assertEquals(defaultPerClientRunRows, rows); 151 // If size is 2G, then twice the row count. 152 opts.setSize(2.0f); 153 opts.setPerClientRunRows(1000); 154 opts.setCmdName(PerformanceEvaluation.RANDOM_READ); 155 opts = PerformanceEvaluation.calculateRowsAndSize(opts); 156 assertEquals(1000, opts.getPerClientRunRows()); 157 // If two clients, then they get half the rows each. 158 opts.setNumClientThreads(2); 159 opts = PerformanceEvaluation.calculateRowsAndSize(opts); 160 assertEquals(1000, opts.getPerClientRunRows()); 161 // assuming we will get one before this loop expires 162 boolean foundValue = false; 163 for (int i = 0; i < 10000000; i++) { 164 long randomRow = PerformanceEvaluation.generateRandomRow(opts.totalRows); 165 if (randomRow > 1000) { 166 foundValue = true; 167 break; 168 } 169 } 170 assertTrue("We need to get a value more than 1000", foundValue); 171 } 172 173 @Test 174 public void testZipfian() throws NoSuchMethodException, SecurityException, InstantiationException, 175 IllegalAccessException, IllegalArgumentException, InvocationTargetException { 176 TestOptions opts = new PerformanceEvaluation.TestOptions(); 177 opts.setValueZipf(true); 178 final int valueSize = 1024; 179 opts.setValueSize(valueSize); 180 RandomReadTest rrt = new RandomReadTest(null, opts, null); 181 Constructor<?> ctor = 182 Histogram.class.getDeclaredConstructor(com.codahale.metrics.Reservoir.class); 183 ctor.setAccessible(true); 184 Histogram histogram = (Histogram) ctor.newInstance(new UniformReservoir(1024 * 500)); 185 for (int i = 0; i < 100; i++) { 186 histogram.update(rrt.getValueLength()); 187 } 188 Snapshot snapshot = histogram.getSnapshot(); 189 double stddev = snapshot.getStdDev(); 190 assertTrue(stddev != 0 && stddev != 1.0); 191 assertTrue(snapshot.getStdDev() != 0); 192 double median = snapshot.getMedian(); 193 assertTrue(median != 0 && median != 1 && median != valueSize); 194 } 195 196 @Test 197 public void testSetBufferSizeOption() { 198 TestOptions opts = new PerformanceEvaluation.TestOptions(); 199 long bufferSize = opts.getBufferSize(); 200 assertEquals(bufferSize, 2L * 1024L * 1024L); 201 opts.setBufferSize(64L * 1024L); 202 bufferSize = opts.getBufferSize(); 203 assertEquals(bufferSize, 64L * 1024L); 204 } 205 206 @Test 207 public void testParseOptsWithThreads() { 208 Queue<String> opts = new LinkedList<>(); 209 String cmdName = "sequentialWrite"; 210 int threads = 1; 211 opts.offer(cmdName); 212 opts.offer(String.valueOf(threads)); 213 PerformanceEvaluation.TestOptions options = PerformanceEvaluation.parseOpts(opts); 214 assertNotNull(options); 215 assertNotNull(options.getCmdName()); 216 assertEquals(cmdName, options.getCmdName()); 217 assertEquals(threads, options.getNumClientThreads()); 218 } 219 220 @Test 221 public void testParseOptsWrongThreads() { 222 Queue<String> opts = new LinkedList<>(); 223 String cmdName = "sequentialWrite"; 224 opts.offer(cmdName); 225 opts.offer("qq"); 226 try { 227 PerformanceEvaluation.parseOpts(opts); 228 } catch (IllegalArgumentException e) { 229 System.out.println(e.getMessage()); 230 assertEquals("Command " + cmdName + " does not have threads number", e.getMessage()); 231 assertTrue(e.getCause() instanceof NumberFormatException); 232 } 233 } 234 235 @Test 236 public void testParseOptsNoThreads() { 237 Queue<String> opts = new LinkedList<>(); 238 String cmdName = "sequentialWrite"; 239 try { 240 PerformanceEvaluation.parseOpts(opts); 241 } catch (IllegalArgumentException e) { 242 System.out.println(e.getMessage()); 243 assertEquals("Command " + cmdName + " does not have threads number", e.getMessage()); 244 assertTrue(e.getCause() instanceof NoSuchElementException); 245 } 246 } 247 248 @Test 249 public void testParseOptsMultiPuts() { 250 Queue<String> opts = new LinkedList<>(); 251 String cmdName = "sequentialWrite"; 252 opts.offer("--multiPut=10"); 253 opts.offer(cmdName); 254 opts.offer("64"); 255 PerformanceEvaluation.TestOptions options = null; 256 try { 257 options = PerformanceEvaluation.parseOpts(opts); 258 fail("should fail"); 259 } catch (IllegalArgumentException e) { 260 System.out.println(e.getMessage()); 261 } 262 263 // Re-create options 264 opts = new LinkedList<>(); 265 opts.offer("--autoFlush=true"); 266 opts.offer("--multiPut=10"); 267 opts.offer(cmdName); 268 opts.offer("64"); 269 270 options = PerformanceEvaluation.parseOpts(opts); 271 assertNotNull(options); 272 assertNotNull(options.getCmdName()); 273 assertEquals(cmdName, options.getCmdName()); 274 assertEquals(10, options.getMultiPut()); 275 } 276 277 @Test 278 public void testParseOptsMultiPutsAndAutoFlushOrder() { 279 Queue<String> opts = new LinkedList<>(); 280 String cmdName = "sequentialWrite"; 281 String cmdMultiPut = "--multiPut=10"; 282 String cmdAutoFlush = "--autoFlush=true"; 283 opts.offer(cmdAutoFlush); 284 opts.offer(cmdMultiPut); 285 opts.offer(cmdName); 286 opts.offer("64"); 287 PerformanceEvaluation.TestOptions options = null; 288 options = PerformanceEvaluation.parseOpts(opts); 289 assertNotNull(options); 290 assertEquals(true, options.autoFlush); 291 assertEquals(10, options.getMultiPut()); 292 293 // Change the order of AutoFlush and Multiput 294 opts = new LinkedList<>(); 295 opts.offer(cmdMultiPut); 296 opts.offer(cmdAutoFlush); 297 opts.offer(cmdName); 298 opts.offer("64"); 299 300 options = null; 301 options = PerformanceEvaluation.parseOpts(opts); 302 assertNotNull(options); 303 assertEquals(10, options.getMultiPut()); 304 assertEquals(true, options.autoFlush); 305 } 306 307 @Test 308 public void testParseOptsConnCount() { 309 Queue<String> opts = new LinkedList<>(); 310 String cmdName = "sequentialWrite"; 311 opts.offer("--oneCon=true"); 312 opts.offer("--connCount=10"); 313 opts.offer(cmdName); 314 opts.offer("64"); 315 PerformanceEvaluation.TestOptions options = null; 316 try { 317 options = PerformanceEvaluation.parseOpts(opts); 318 fail("should fail"); 319 } catch (IllegalArgumentException e) { 320 System.out.println(e.getMessage()); 321 } 322 323 opts = new LinkedList<>(); 324 opts.offer("--connCount=10"); 325 opts.offer(cmdName); 326 opts.offer("64"); 327 328 options = PerformanceEvaluation.parseOpts(opts); 329 assertNotNull(options); 330 assertNotNull(options.getCmdName()); 331 assertEquals(cmdName, options.getCmdName()); 332 assertEquals(10, options.getConnCount()); 333 } 334 335 @Test 336 public void testParseOptsValueRandom() { 337 Queue<String> opts = new LinkedList<>(); 338 String cmdName = "sequentialWrite"; 339 opts.offer("--valueRandom"); 340 opts.offer("--valueZipf"); 341 opts.offer(cmdName); 342 opts.offer("64"); 343 PerformanceEvaluation.TestOptions options = null; 344 try { 345 options = PerformanceEvaluation.parseOpts(opts); 346 fail("should fail"); 347 } catch (IllegalStateException e) { 348 System.out.println(e.getMessage()); 349 } 350 351 opts = new LinkedList<>(); 352 opts.offer("--valueRandom"); 353 opts.offer(cmdName); 354 opts.offer("64"); 355 356 options = PerformanceEvaluation.parseOpts(opts); 357 358 assertNotNull(options); 359 assertNotNull(options.getCmdName()); 360 assertEquals(cmdName, options.getCmdName()); 361 assertEquals(true, options.valueRandom); 362 } 363 364 @Test 365 public void testCustomTestClassOptions() throws IOException { 366 Queue<String> opts = new LinkedList<>(); 367 // create custom properties that can be used for a custom test class 368 Properties commandProps = new Properties(); 369 commandProps.put("prop1", "val1"); 370 String cmdPropsFilePath = 371 this.getClass().getClassLoader().getResource("").getPath() + "cmd_properties.txt"; 372 FileWriter writer = new FileWriter(new File(cmdPropsFilePath)); 373 commandProps.store(writer, null); 374 // create opts for the custom test class - commandPropertiesFile, testClassName 375 opts.offer("--commandPropertiesFile=" + "cmd_properties.txt"); 376 String testClassName = "org.apache.hadoop.hbase.TestPerformanceEvaluation$PESampleTestImpl"; 377 opts.offer(testClassName); 378 opts.offer("1"); 379 PerformanceEvaluation.TestOptions options = PerformanceEvaluation.parseOpts(opts); 380 assertNotNull(options); 381 assertNotNull(options.getCmdName()); 382 assertEquals(testClassName, options.getCmdName()); 383 assertNotNull(options.getCommandProperties()); 384 assertEquals("val1", options.getCommandProperties().get("prop1")); 385 } 386 387 class PESampleTestImpl extends PerformanceEvaluation.Test { 388 389 PESampleTestImpl(Connection con, TestOptions options, Status status) { 390 super(con, options, status); 391 } 392 393 @Override 394 void onStartup() throws IOException { 395 } 396 397 @Override 398 void onTakedown() throws IOException { 399 } 400 401 @Override 402 boolean testRow(long i, long startTime) throws IOException, InterruptedException { 403 return false; 404 } 405 } 406}