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