From 99682e0cc27bc09b8cbc49a77574b1481460c7a0 Mon Sep 17 00:00:00 2001 From: Michael Stack Date: Tue, 22 May 2018 13:01:04 -0700 Subject: [PATCH] Perpetual flush, memstore fill, etc., tools --- hbase-server/pom.xml | 10 ++ .../apache/hadoop/hbase/regionserver/HRegion.java | 3 +- .../apache/hadoop/hbase/regionserver/HStore.java | 4 +- .../regionserver/ThreadSafeMemStoreSizing.java | 7 +- .../regionserver/TestComparatorPerformance.java | 126 +++++++++++++++ .../hbase/regionserver/TestFlushPerformance.java | 169 +++++++++++++++++++++ .../regionserver/TestMemstorePerformance.java | 98 ++++++++++++ 7 files changed, 413 insertions(+), 4 deletions(-) create mode 100644 hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestComparatorPerformance.java create mode 100644 hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestFlushPerformance.java create mode 100644 hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestMemstorePerformance.java diff --git a/hbase-server/pom.xml b/hbase-server/pom.xml index 031ec4f13c..22f4efcec3 100644 --- a/hbase-server/pom.xml +++ b/hbase-server/pom.xml @@ -577,6 +577,16 @@ mockito-core test + + org.mockito + mockito-core + compile + + + org.mockito + mockito-core + compile + diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java index a1fcaf2493..13e7c9e8d7 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java @@ -287,7 +287,8 @@ public class HRegion implements HeapSize, PropagatingConfigurationObserver, Regi private Map coprocessorServiceHandlers = Maps.newHashMap(); // Track data size in all memstores - private final MemStoreSizing memStoreSizing = new ThreadSafeMemStoreSizing(); + @VisibleForTesting + final MemStoreSizing memStoreSizing = new ThreadSafeMemStoreSizing(); private final RegionServicesForStores regionServicesForStores = new RegionServicesForStores(this); // Debug possible data loss due to WAL off diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HStore.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HStore.java index 4232f83b1d..ac4b14ee8c 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HStore.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HStore.java @@ -139,7 +139,9 @@ public class HStore implements Store, HeapSize, StoreConfigInformation, Propagat private static final Logger LOG = LoggerFactory.getLogger(HStore.class); - protected final MemStore memstore; + @VisibleForTesting + final MemStore memstore; + // This stores directory in the filesystem. protected final HRegion region; private final ColumnFamilyDescriptor family; diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/ThreadSafeMemStoreSizing.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/ThreadSafeMemStoreSizing.java index de0549386e..ad0a5032c7 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/ThreadSafeMemStoreSizing.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/ThreadSafeMemStoreSizing.java @@ -19,6 +19,7 @@ package org.apache.hadoop.hbase.regionserver; import java.util.concurrent.atomic.AtomicLong; +import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting; import org.apache.yetus.audience.InterfaceAudience; /** @@ -32,8 +33,10 @@ class ThreadSafeMemStoreSizing implements MemStoreSizing { // We used to tie the update of these thread counters so // they all changed together under one lock. This was // undone. Doesn't seem necessary. - private final AtomicLong dataSize = new AtomicLong(); - private final AtomicLong heapSize = new AtomicLong(); + @VisibleForTesting + final AtomicLong dataSize = new AtomicLong(); + @VisibleForTesting + final AtomicLong heapSize = new AtomicLong(); private final AtomicLong offHeapSize = new AtomicLong(); ThreadSafeMemStoreSizing() { diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestComparatorPerformance.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestComparatorPerformance.java new file mode 100644 index 0000000000..09693488b9 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestComparatorPerformance.java @@ -0,0 +1,126 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.hadoop.hbase.regionserver; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.ByteBufferKeyValue; +import org.apache.hadoop.hbase.Cell; +import org.apache.hadoop.hbase.CellComparator; +import org.apache.hadoop.hbase.CellComparatorImpl; +import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.KeyValue; +import org.apache.hadoop.hbase.testclassification.LargeTests; +import org.apache.hadoop.hbase.util.ByteBufferUtils; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hbase.wal.WALFactory; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.TestName; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Random; +import java.util.concurrent.ConcurrentSkipListMap; + +import static org.junit.Assert.assertTrue; + + +/** + * Test Comparator performance. + * @see TestMemstorePerformance + */ +@Category(LargeTests.class) +public class TestComparatorPerformance { + @Rule public TestName name = new TestName(); + + private static final Logger LOG = LoggerFactory.getLogger(TestComparatorPerformance.class); + private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); + private static WALFactory WALFACTORY; + private static final Random RANDOM = new Random(); + private static final String FAMILY_NAME = "cf"; + private static final byte [] FAMILY_NAME_BYTES = Bytes.toBytes(FAMILY_NAME); + private static final int TOTAL_ROWS = 400000; + private static final int COLUMNS = 10; + private static final byte [] VALUE = Bytes.toBytes("Some old value"); + + @BeforeClass + public static void beforeClass() throws IOException { + // Make it so we use the DisableWALProvider; i.e. no WAL + // TODO: Doc. how to get a disabled WAL. Too hard to figure currently. + Configuration conf = TEST_UTIL.getConfiguration(); + conf.setBoolean("hbase.regionserver.hlog.enabled", false); + assertTrue(!conf.getBoolean("hbase.regionserver.hlog.enabled", true)); + WALFACTORY = new WALFactory(conf, TestComparatorPerformance.class.getSimpleName()); + ChunkCreator.initialize(MemStoreLABImpl.CHUNK_SIZE_DEFAULT, false, 0, + 0, 0, null); + } + + @Test + public void testComparator() throws IOException { + // BBKVComparator bbkvComparator = new BBKVComparator(); + CellComparator cc = CellComparatorImpl.COMPARATOR; + while(true) { + LOG.info("Start"); + ConcurrentSkipListMap map = new ConcurrentSkipListMap<>(cc); + for (int i = 0; i < TOTAL_ROWS; i++) { + /* if ((i % 2) == 0) { + createKeyValue(map, TOTAL_ROWS); + } else { + */ + createByteBufferKeyValue(map, TOTAL_ROWS); + /* + } + */ + } + } + } + + void createKeyValue(ConcurrentSkipListMap map, int index) { + byte [] row = TestFlushPerformance.getRandomRow(index); + for (int column = 0; column < COLUMNS; column++) { + byte[] qualifier = Bytes.toBytes("" + column + 1); + KeyValue kv = new KeyValue(row, FAMILY_NAME_BYTES, qualifier); + map.put(kv, kv); + } + } + + // void createByteBufferKeyValue(ConcurrentSkipListMap map, + void createByteBufferKeyValue(ConcurrentSkipListMap map, int index) { + byte [] row = TestFlushPerformance.getRandomRow(index); + for (int column = 0; column < COLUMNS; column++) { + byte[] qualifier = Bytes.toBytes("" + column + 1); + ByteBufferKeyValue bbkv = getOnHeapCell(row, FAMILY_NAME_BYTES, qualifier); + map.put(bbkv, bbkv); + } + } + + private static ByteBufferKeyValue getOnHeapCell(byte [] row, byte [] family, byte [] qualifier) { + KeyValue kvCell = new KeyValue(row, family, qualifier, 0L, KeyValue.Type.Put, row); + ByteBuffer buf = ByteBuffer.allocate(kvCell.getBuffer().length); + ByteBufferUtils.copyFromArrayToBuffer(buf, kvCell.getBuffer(), 0, kvCell.getBuffer().length); + return new ByteBufferKeyValue(buf, 0, buf.capacity(), 0L); + } + + public static void main(String args[]) { + org.junit.runner.JUnitCore.main(TestComparatorPerformance.class.getName()); + } +} \ No newline at end of file diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestFlushPerformance.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestFlushPerformance.java new file mode 100644 index 0000000000..14f5841c68 --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestFlushPerformance.java @@ -0,0 +1,169 @@ +package org.apache.hadoop.hbase.regionserver; + +import com.sun.org.apache.xpath.internal.axes.WalkerFactory; +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.fs.FileSystem; +import org.apache.hadoop.fs.Path; +import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.ServerName; +import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.client.Put; +import org.apache.hadoop.hbase.HRegionInfo; +import org.apache.hadoop.hbase.HColumnDescriptor; +import org.apache.hadoop.hbase.HTableDescriptor; +import org.apache.hadoop.hbase.testclassification.LargeTests; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hbase.util.CancelableProgressable; +import org.apache.hadoop.hbase.wal.WAL; +import org.apache.hadoop.hbase.wal.WALFactory; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.TestName; +import org.mockito.Mockito; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Random; + +import static org.junit.Assert.assertTrue; + + +/** + * Test with a main that is about loading a region and then re-using the load just to profile + * region flush rate. Needed because hbase2 is slower than hbase1 flushing. + */ +@Category(LargeTests.class) +public class TestFlushPerformance { + @Rule public TestName name = new TestName(); + + private static final Logger LOG = LoggerFactory.getLogger(TestFlushPerformance.class); + private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); + private static WALFactory WALFACTORY; + private static final Random RANDOM = new Random(); + private static final String FAMILY_NAME = "cf"; + private static final byte [] FAMILY_NAME_BYTES = Bytes.toBytes(FAMILY_NAME); + private static final int TOTAL_ROWS = 400000; + private static final int COLUMNS = 10; + private static final byte [] VALUE = Bytes.toBytes("Some old value"); + + @BeforeClass + public static void beforeClass() throws IOException { + // Make it so we use the DisableWALProvider; i.e. no WAL + // TODO: Doc. how to get a disabled WAL. Too hard to figure currently. + Configuration conf = TEST_UTIL.getConfiguration(); + conf.setBoolean("hbase.regionserver.hlog.enabled", false); + assertTrue(!conf.getBoolean("hbase.regionserver.hlog.enabled", true)); + WALFACTORY = new WALFactory(conf, TestFlushPerformance.class.getSimpleName()); + ChunkCreator.initialize(MemStoreLABImpl.CHUNK_SIZE_DEFAULT, false, 0, + 0, 0, null); + } + + @Test + public void testFlush() throws IOException { + HRegion region = getRegion(WALFACTORY, + TEST_UTIL.getConfiguration(), this.name.getMethodName()); + load(region, TOTAL_ROWS); + long dataSize = region.memStoreSizing.getDataSize(); + long heapSize = region.memStoreSizing.getHeapSize(); + try { + for (;;) { + MutableSegment activeReference = + ((AbstractMemStore) region.getStore(FAMILY_NAME_BYTES).memstore).active; + HRegion.FlushResult flushResult = region.flush(true); + assertTrue(flushResult.isFlushSucceeded()); + // Put the reference back in place to see if we can flush again. Restore datasize too. + ((AbstractMemStore)region.getStore(FAMILY_NAME_BYTES).memstore).active = activeReference; + ((ThreadSafeMemStoreSizing)region.memStoreSizing).dataSize.set(dataSize); + ((ThreadSafeMemStoreSizing)region.memStoreSizing).heapSize.set(heapSize); + } + } finally { + region.close(); + } + } + + static HRegion getRegion(WALFactory walFactory, + Configuration conf, String tableName) throws IOException { + HColumnDescriptor hcd = new HColumnDescriptor(FAMILY_NAME); + HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(tableName)); + htd.setMemStoreFlushSize(Long.MAX_VALUE/1024/*Big number to stop auto-flushing!*/); + htd.addFamily(hcd); + RegionServerServices rss = Mockito.mock(RegionServerServices.class); + Mockito.when(rss.getServerName()).thenReturn(ServerName.valueOf("serverName", 0, 0)); + FlushRequester flushRequester = Mockito.mock(FlushRequester.class); + Mockito.when(rss.getFlushRequester()).thenReturn(flushRequester); + HRegionInfo ri = new HRegionInfo(htd.getTableName()); + Path tableDir = TEST_UTIL.getDataTestDir(); + HRegionFileSystem rfs = new HRegionFileSystem(conf, FileSystem.get(conf), tableDir, ri); + WAL wal = walFactory.getWAL(ri); + HRegion region = new HRegion(rfs, wal, conf, htd, rss); + CancelableProgressable cancelableProgressable = Mockito.mock(CancelableProgressable.class); + return region.openHRegion(cancelableProgressable); + } + + static void load(HRegion region, int totalRows) throws IOException { + for (int i = 0; i < totalRows; i++) { + Put put = new Put(getRandomRow(totalRows)); + for (int column = 0; column < COLUMNS; column++) { + byte[] qualifier = Bytes.toBytes("" + column + 1); + put.addColumn(FAMILY_NAME_BYTES, qualifier, VALUE); + } + region.put(put); + } + } + + // Below methods are stolen from PerformanceEvaluation. TODO: Move into hbase-common. + + static byte [] getRandomRow(final int totalRows) { + return format(generateRandomRow(totalRows)); + } + + private static byte [] format(final int number) { + byte [] b = new byte[26]; + int d = Math.abs(number); + for (int i = b.length - 1; i >= 0; i--) { + b[i] = (byte)((d % 10) + '0'); + d /= 10; + } + return b; + } + + private static int generateRandomRow(int totalRows) { + return RANDOM.nextInt(Integer.MAX_VALUE) % totalRows; + } + + /* + * This method takes some time and is done inline uploading data. For + * example, doing the mapfile test, generation of the key and value + * consumes about 30% of CPU time. + * @return Generated random value to insert into a table cell. + */ + private static byte[] generateData(int length) { + byte [] b = new byte [length]; + int i; + + for(i = 0; i < (length-8); i += 8) { + b[i] = (byte) (65 + RANDOM.nextInt(26)); + b[i+1] = b[i]; + b[i+2] = b[i]; + b[i+3] = b[i]; + b[i+4] = b[i]; + b[i+5] = b[i]; + b[i+6] = b[i]; + b[i+7] = b[i]; + } + + byte a = (byte) (65 + RANDOM.nextInt(26)); + for(; i < length; i++) { + b[i] = a; + } + return b; + } + + public static void main(String args[]) { + org.junit.runner.JUnitCore.main(TestFlushPerformance.class.getName()); + } +} diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestMemstorePerformance.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestMemstorePerformance.java new file mode 100644 index 0000000000..65508b6e2e --- /dev/null +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestMemstorePerformance.java @@ -0,0 +1,98 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.hadoop.hbase.regionserver; + +import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.HBaseTestingUtility; +import org.apache.hadoop.hbase.testclassification.LargeTests; +import org.apache.hadoop.hbase.util.Bytes; +import org.apache.hadoop.hbase.wal.WALFactory; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.rules.TestName; +import org.mockito.Mockito; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.Random; + +import static org.junit.Assert.assertTrue; + + +/** + * Test with a main that is about loading a region memstore over and over again. + * Needed because hbase2 is slower than hbase1 flushing. + * @see TestFlushPerformance + */ +@Category(LargeTests.class) +public class TestMemstorePerformance { + @Rule public TestName name = new TestName(); + + private static final Logger LOG = LoggerFactory.getLogger(TestMemstorePerformance.class); + private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); + private static WALFactory WALFACTORY; + private static final Random RANDOM = new Random(); + private static final String FAMILY_NAME = "cf"; + private static final byte [] FAMILY_NAME_BYTES = Bytes.toBytes(FAMILY_NAME); + private static final int TOTAL_ROWS = 400000; + private static final int COLUMNS = 10; + private static final byte [] VALUE = Bytes.toBytes("Some old value"); + + @BeforeClass + public static void beforeClass() throws IOException { + // Make it so we use the DisableWALProvider; i.e. no WAL + // TODO: Doc. how to get a disabled WAL. Too hard to figure currently. + Configuration conf = TEST_UTIL.getConfiguration(); + conf.setBoolean("hbase.regionserver.hlog.enabled", false); + assertTrue(!conf.getBoolean("hbase.regionserver.hlog.enabled", true)); + WALFACTORY = new WALFactory(conf, TestMemstorePerformance.class.getSimpleName()); + ChunkCreator.initialize(MemStoreLABImpl.CHUNK_SIZE_DEFAULT, false, 0, + 0, 0, null); + } + + @Test + public void testMemstore() throws IOException { + HRegion region = TestFlushPerformance.getRegion(WALFACTORY, TEST_UTIL.getConfiguration(), + this.name.getMethodName()); + try { + // Cycle filling the memstore, snapshotting, dropping the snapshot and so on. + for (;;) { + TestFlushPerformance.load(region, TOTAL_ROWS); + long dataSize = region.getMemStoreDataSize(); + MemStoreSnapshot mss = + ((DefaultMemStore)((HStore)region.getStore(FAMILY_NAME_BYTES)).memstore).snapshot(); + MemStoreSize size = ((HStore)region.getStore(FAMILY_NAME_BYTES)).getSnapshotSize(); + ((DefaultMemStore)((HStore)region.getStore(FAMILY_NAME_BYTES)).memstore). + clearSnapshot(mss.getId()); + region.decrMemStoreSize(size); + LOG.info("dataSize={}, snapshotSize={}, currentSize={}", dataSize, size, + region.getMemStoreDataSize()); + } + } finally { + region.close(); + } + } + + public static void main(String args[]) { + org.junit.runner.JUnitCore.main(TestMemstorePerformance.class.getName()); + } +} \ No newline at end of file -- 2.16.3