Repository: hbase Updated Branches: refs/heads/HBASE-19397-branch-2 0eb1823a1 -> 84530255c (forced update)
HBASE-19139 Create Async Admin methods for Clear Block Cache Project: http://git-wip-us.apache.org/repos/asf/hbase/repo Commit: http://git-wip-us.apache.org/repos/asf/hbase/commit/895267d0 Tree: http://git-wip-us.apache.org/repos/asf/hbase/tree/895267d0 Diff: http://git-wip-us.apache.org/repos/asf/hbase/diff/895267d0 Branch: refs/heads/HBASE-19397-branch-2 Commit: 895267d09c15c001be5eff450b712d9d8b3cdb78 Parents: bc4e49f Author: Guanghao Zhang <[email protected]> Authored: Mon Jan 8 14:56:13 2018 +0800 Committer: Guanghao Zhang <[email protected]> Committed: Tue Jan 9 14:55:21 2018 +0800 ---------------------------------------------------------------------- .../hbase/CacheEvictionStatsAggregator.java | 42 +++++++++++++ .../apache/hadoop/hbase/client/AsyncAdmin.java | 13 ++++ .../hadoop/hbase/client/AsyncHBaseAdmin.java | 7 +++ .../hadoop/hbase/client/RawAsyncHBaseAdmin.java | 51 ++++++++++++++++ .../hadoop/hbase/client/TestInterfaceAlign.java | 2 - .../regionserver/TestClearRegionBlockCache.java | 64 ++++++++++++++++++-- 6 files changed, 172 insertions(+), 7 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/hbase/blob/895267d0/hbase-client/src/main/java/org/apache/hadoop/hbase/CacheEvictionStatsAggregator.java ---------------------------------------------------------------------- diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/CacheEvictionStatsAggregator.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/CacheEvictionStatsAggregator.java new file mode 100644 index 0000000..85d68dc --- /dev/null +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/CacheEvictionStatsAggregator.java @@ -0,0 +1,42 @@ +/** + * + * 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; + +import org.apache.yetus.audience.InterfaceAudience; + +/** + * Used to merge CacheEvictionStats. Thread safe for concurrent accessing. + */ [email protected] +public class CacheEvictionStatsAggregator { + + private final CacheEvictionStatsBuilder builder; + + public CacheEvictionStatsAggregator() { + this.builder = new CacheEvictionStatsBuilder(); + } + + public synchronized void append(CacheEvictionStats stats) { + this.builder.append(stats); + } + + public synchronized CacheEvictionStats sum() { + return this.builder.build(); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/hbase/blob/895267d0/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java ---------------------------------------------------------------------- diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java index af39f17..a375265 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncAdmin.java @@ -18,6 +18,7 @@ package org.apache.hadoop.hbase.client; import com.google.protobuf.RpcChannel; + import java.util.Collection; import java.util.EnumSet; import java.util.List; @@ -27,6 +28,8 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.function.Function; import java.util.regex.Pattern; + +import org.apache.hadoop.hbase.CacheEvictionStats; import org.apache.hadoop.hbase.ClusterMetrics; import org.apache.hadoop.hbase.ClusterMetrics.Option; import org.apache.hadoop.hbase.NamespaceDescriptor; @@ -1211,4 +1214,14 @@ public interface AsyncAdmin { * @return - returns a list of servers that not cleared wrapped by a {@link CompletableFuture}. */ CompletableFuture<List<ServerName>> clearDeadServers(final List<ServerName> servers); + + /** + * Clear all the blocks corresponding to this table from BlockCache. For expert-admins. Calling + * this API will drop all the cached blocks specific to a table from BlockCache. This can + * significantly impact the query performance as the subsequent queries will have to retrieve the + * blocks from underlying filesystem. + * @param tableName table to clear block cache + * @return CacheEvictionStats related to the eviction wrapped by a {@link CompletableFuture}. + */ + CompletableFuture<CacheEvictionStats> clearBlockCache(final TableName tableName); } http://git-wip-us.apache.org/repos/asf/hbase/blob/895267d0/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java ---------------------------------------------------------------------- diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java index 3ca5d69..d0d19c1 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/AsyncHBaseAdmin.java @@ -27,6 +27,8 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.function.Function; import java.util.regex.Pattern; + +import org.apache.hadoop.hbase.CacheEvictionStats; import org.apache.hadoop.hbase.ClusterMetrics; import org.apache.hadoop.hbase.ClusterMetrics.Option; import org.apache.hadoop.hbase.NamespaceDescriptor; @@ -734,4 +736,9 @@ class AsyncHBaseAdmin implements AsyncAdmin { public CompletableFuture<List<ServerName>> clearDeadServers(List<ServerName> servers) { return wrap(rawAdmin.clearDeadServers(servers)); } + + @Override + public CompletableFuture<CacheEvictionStats> clearBlockCache(TableName tableName) { + return wrap(rawAdmin.clearBlockCache(tableName)); + } } http://git-wip-us.apache.org/repos/asf/hbase/blob/895267d0/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncHBaseAdmin.java ---------------------------------------------------------------------- diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncHBaseAdmin.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncHBaseAdmin.java index ac00234..5111bfc 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncHBaseAdmin.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/RawAsyncHBaseAdmin.java @@ -42,6 +42,8 @@ import java.util.stream.Stream; import org.apache.commons.io.IOUtils; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.AsyncMetaTableAccessor; +import org.apache.hadoop.hbase.CacheEvictionStats; +import org.apache.hadoop.hbase.CacheEvictionStatsAggregator; import org.apache.hadoop.hbase.ClusterMetrics; import org.apache.hadoop.hbase.ClusterMetrics.Option; import org.apache.hadoop.hbase.ClusterMetricsBuilder; @@ -97,6 +99,8 @@ import org.apache.hadoop.hbase.shaded.protobuf.RequestConverter; import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.AdminService; import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.ClearCompactionQueuesRequest; import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.ClearCompactionQueuesResponse; +import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.ClearRegionBlockCacheRequest; +import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.ClearRegionBlockCacheResponse; import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CompactRegionRequest; import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.CompactRegionResponse; import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.FlushRegionRequest; @@ -3387,4 +3391,51 @@ class RawAsyncHBaseAdmin implements AsyncAdmin { }); return future; } + + @Override + public CompletableFuture<CacheEvictionStats> clearBlockCache(TableName tableName) { + CompletableFuture<CacheEvictionStats> future = new CompletableFuture<>(); + getTableHRegionLocations(tableName).whenComplete((locations, err) -> { + if (err != null) { + future.completeExceptionally(err); + return; + } + Map<ServerName, List<RegionInfo>> regionInfoByServerName = + locations.stream().filter(l -> l.getRegion() != null) + .filter(l -> !l.getRegion().isOffline()).filter(l -> l.getServerName() != null) + .collect(Collectors.groupingBy(l -> l.getServerName(), + Collectors.mapping(l -> l.getRegion(), Collectors.toList()))); + List<CompletableFuture<CacheEvictionStats>> futures = new ArrayList<>(); + CacheEvictionStatsAggregator aggregator = new CacheEvictionStatsAggregator(); + for (Map.Entry<ServerName, List<RegionInfo>> entry : regionInfoByServerName.entrySet()) { + futures + .add(clearBlockCache(entry.getKey(), entry.getValue()).whenComplete((stats, err2) -> { + if (err2 != null) { + future.completeExceptionally(err2); + } else { + aggregator.append(stats); + } + })); + } + CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()])) + .whenComplete((ret, err3) -> { + if (err3 != null) { + future.completeExceptionally(err3); + } else { + future.complete(aggregator.sum()); + } + }); + }); + return future; + } + + private CompletableFuture<CacheEvictionStats> clearBlockCache(ServerName serverName, + List<RegionInfo> hris) { + return this.<CacheEvictionStats> newAdminCaller().action((controller, stub) -> this + .<ClearRegionBlockCacheRequest, ClearRegionBlockCacheResponse, CacheEvictionStats> adminCall( + controller, stub, RequestConverter.buildClearRegionBlockCacheRequest(hris), + (s, c, req, done) -> s.clearRegionBlockCache(controller, req, done), + resp -> ProtobufUtil.toCacheEvictionStats(resp.getStats()))) + .serverName(serverName).call(); + } } http://git-wip-us.apache.org/repos/asf/hbase/blob/895267d0/hbase-client/src/test/java/org/apache/hadoop/hbase/client/TestInterfaceAlign.java ---------------------------------------------------------------------- diff --git a/hbase-client/src/test/java/org/apache/hadoop/hbase/client/TestInterfaceAlign.java b/hbase-client/src/test/java/org/apache/hadoop/hbase/client/TestInterfaceAlign.java index 2266d06..8ddb392 100644 --- a/hbase-client/src/test/java/org/apache/hadoop/hbase/client/TestInterfaceAlign.java +++ b/hbase-client/src/test/java/org/apache/hadoop/hbase/client/TestInterfaceAlign.java @@ -55,8 +55,6 @@ public class TestInterfaceAlign { adminMethodNames.remove("getConfiguration"); adminMethodNames.removeAll(getMethodNames(Abortable.class)); adminMethodNames.removeAll(getMethodNames(Closeable.class)); - // TODO: Remove this after HBASE-19139 - adminMethodNames.remove("clearBlockCache"); adminMethodNames.forEach(method -> { boolean contains = asyncAdminMethodNames.contains(method); http://git-wip-us.apache.org/repos/asf/hbase/blob/895267d0/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestClearRegionBlockCache.java ---------------------------------------------------------------------- diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestClearRegionBlockCache.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestClearRegionBlockCache.java index b9d38f1..9667168 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestClearRegionBlockCache.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestClearRegionBlockCache.java @@ -19,11 +19,15 @@ package org.apache.hadoop.hbase.regionserver; import org.apache.hadoop.conf.Configuration; +import org.apache.hadoop.hbase.CacheEvictionStats; import org.apache.hadoop.hbase.Cell; import org.apache.hadoop.hbase.HBaseTestingUtility; import org.apache.hadoop.hbase.HConstants; import org.apache.hadoop.hbase.MiniHBaseCluster; import org.apache.hadoop.hbase.TableName; +import org.apache.hadoop.hbase.client.Admin; +import org.apache.hadoop.hbase.client.AsyncAdmin; +import org.apache.hadoop.hbase.client.ConnectionFactory; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.client.Table; import org.apache.hadoop.hbase.io.hfile.BlockCache; @@ -35,6 +39,8 @@ import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.ArrayList; @@ -44,6 +50,7 @@ import static org.junit.Assert.assertEquals; @Category(MediumTests.class) @RunWith(Parameterized.class) public class TestClearRegionBlockCache { + private static final Logger LOG = LoggerFactory.getLogger(TestClearRegionBlockCache.class); private static final TableName TABLE_NAME = TableName.valueOf("testClearRegionBlockCache"); private static final byte[] FAMILY = Bytes.toBytes("family"); private static final byte[][] SPLIT_KEY = new byte[][] { Bytes.toBytes("5") }; @@ -77,6 +84,9 @@ public class TestClearRegionBlockCache { // Create table table = HTU.createTable(TABLE_NAME, FAMILY, SPLIT_KEY); + + HTU.loadNumericRows(table, FAMILY, 1, 10); + HTU.flush(TABLE_NAME); } @After @@ -86,9 +96,6 @@ public class TestClearRegionBlockCache { @Test public void testClearBlockCache() throws Exception { - HTU.loadNumericRows(table, FAMILY, 1, 10); - HTU.flush(TABLE_NAME); - BlockCache blockCache1 = rs1.getCacheConfig().getBlockCache(); BlockCache blockCache2 = rs2.getCacheConfig().getBlockCache(); @@ -98,18 +105,65 @@ public class TestClearRegionBlockCache { // scan will cause blocks to be added in BlockCache scanAllRegionsForRS(rs1); assertEquals(blockCache1.getBlockCount() - initialBlockCount1, - HTU.getNumHFilesForRS(rs1, TABLE_NAME, FAMILY)); + HTU.getNumHFilesForRS(rs1, TABLE_NAME, FAMILY)); clearRegionBlockCache(rs1); scanAllRegionsForRS(rs2); assertEquals(blockCache2.getBlockCount() - initialBlockCount2, - HTU.getNumHFilesForRS(rs2, TABLE_NAME, FAMILY)); + HTU.getNumHFilesForRS(rs2, TABLE_NAME, FAMILY)); clearRegionBlockCache(rs2); assertEquals(initialBlockCount1, blockCache1.getBlockCount()); assertEquals(initialBlockCount2, blockCache2.getBlockCount()); } + @Test + public void testClearBlockCacheFromAdmin() throws Exception { + Admin admin = HTU.getAdmin(); + + // All RS run in a same process, so the block cache is same for rs1 and rs2 + BlockCache blockCache = rs1.getCacheConfig().getBlockCache(); + long initialBlockCount = blockCache.getBlockCount(); + + // scan will cause blocks to be added in BlockCache + scanAllRegionsForRS(rs1); + assertEquals(blockCache.getBlockCount() - initialBlockCount, + HTU.getNumHFilesForRS(rs1, TABLE_NAME, FAMILY)); + scanAllRegionsForRS(rs2); + assertEquals(blockCache.getBlockCount() - initialBlockCount, + HTU.getNumHFilesForRS(rs1, TABLE_NAME, FAMILY) + + HTU.getNumHFilesForRS(rs2, TABLE_NAME, FAMILY)); + + CacheEvictionStats stats = admin.clearBlockCache(TABLE_NAME); + assertEquals(stats.getEvictedBlocks(), HTU.getNumHFilesForRS(rs1, TABLE_NAME, FAMILY) + + HTU.getNumHFilesForRS(rs2, TABLE_NAME, FAMILY)); + assertEquals(initialBlockCount, blockCache.getBlockCount()); + } + + @Test + public void testClearBlockCacheFromAsyncAdmin() throws Exception { + AsyncAdmin admin = + ConnectionFactory.createAsyncConnection(HTU.getConfiguration()).get().getAdmin(); + + // All RS run in a same process, so the block cache is same for rs1 and rs2 + BlockCache blockCache = rs1.getCacheConfig().getBlockCache(); + long initialBlockCount = blockCache.getBlockCount(); + + // scan will cause blocks to be added in BlockCache + scanAllRegionsForRS(rs1); + assertEquals(blockCache.getBlockCount() - initialBlockCount, + HTU.getNumHFilesForRS(rs1, TABLE_NAME, FAMILY)); + scanAllRegionsForRS(rs2); + assertEquals(blockCache.getBlockCount() - initialBlockCount, + HTU.getNumHFilesForRS(rs1, TABLE_NAME, FAMILY) + + HTU.getNumHFilesForRS(rs2, TABLE_NAME, FAMILY)); + + CacheEvictionStats stats = admin.clearBlockCache(TABLE_NAME).get(); + assertEquals(stats.getEvictedBlocks(), HTU.getNumHFilesForRS(rs1, TABLE_NAME, FAMILY) + + HTU.getNumHFilesForRS(rs2, TABLE_NAME, FAMILY)); + assertEquals(initialBlockCount, blockCache.getBlockCount()); + } + private void scanAllRegionsForRS(HRegionServer rs) throws IOException { for (Region region : rs.getRegions(TABLE_NAME)) { RegionScanner scanner = region.getScanner(new Scan());
