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