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 org.apache.hadoop.fs.FSDataInputStream; 041import org.apache.hadoop.fs.FileSystem; 042import org.apache.hadoop.fs.Path; 043import org.apache.hadoop.hbase.PerformanceEvaluation.RandomReadTest; 044import org.apache.hadoop.hbase.PerformanceEvaluation.TestOptions; 045import org.apache.hadoop.hbase.regionserver.CompactingMemStore; 046import org.apache.hadoop.hbase.testclassification.MiscTests; 047import org.apache.hadoop.hbase.testclassification.SmallTests; 048import org.apache.hadoop.hbase.util.GsonUtil; 049import org.junit.ClassRule; 050import org.junit.Test; 051import org.junit.experimental.categories.Category; 052 053import org.apache.hbase.thirdparty.com.google.gson.Gson; 054 055@Category({MiscTests.class, SmallTests.class}) 056public class TestPerformanceEvaluation { 057 @ClassRule 058 public static final HBaseClassTestRule CLASS_RULE = 059 HBaseClassTestRule.forClass(TestPerformanceEvaluation.class); 060 061 private static final HBaseTestingUtility HTU = new HBaseTestingUtility(); 062 063 @Test 064 public void testDefaultInMemoryCompaction() { 065 PerformanceEvaluation.TestOptions defaultOpts = 066 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 Random random = new Random(); 157 // assuming we will get one before this loop expires 158 boolean foundValue = false; 159 for (int i = 0; i < 10000000; i++) { 160 int randomRow = PerformanceEvaluation.generateRandomRow(random, 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 ((LinkedList<String>) opts).offerFirst("--multiPut=10"); 259 ((LinkedList<String>) opts).offerFirst("--autoFlush=true"); 260 options = PerformanceEvaluation.parseOpts(opts); 261 assertNotNull(options); 262 assertNotNull(options.getCmdName()); 263 assertEquals(cmdName, options.getCmdName()); 264 assertEquals(10, options.getMultiPut()); 265 } 266 267 @Test 268 public void testParseOptsConnCount() { 269 Queue<String> opts = new LinkedList<>(); 270 String cmdName = "sequentialWrite"; 271 opts.offer("--oneCon=true"); 272 opts.offer("--connCount=10"); 273 opts.offer(cmdName); 274 opts.offer("64"); 275 PerformanceEvaluation.TestOptions options = null; 276 try { 277 options = PerformanceEvaluation.parseOpts(opts); 278 fail("should fail"); 279 } catch (IllegalArgumentException e) { 280 System.out.println(e.getMessage()); 281 } 282 ((LinkedList<String>) opts).offerFirst("--connCount=10"); 283 options = PerformanceEvaluation.parseOpts(opts); 284 assertNotNull(options); 285 assertNotNull(options.getCmdName()); 286 assertEquals(cmdName, options.getCmdName()); 287 assertEquals(10, options.getConnCount()); 288 } 289}