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