Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,9 @@ public String dumpQueue() {
}

public synchronized boolean requestSplit(final Region r) {
// don't split regions that are blocking
// Don't split regions that are blocking is the default behavior.
// But in some circumstances, split here is needed to prevent the region size from
// continuously growing, as well as the number of store files, see HBASE-26242.
HRegion hr = (HRegion)r;
try {
if (shouldSplitRegion() && hr.getCompactPriority() >= PRIORITY_USER) {
Expand All @@ -218,14 +220,14 @@ public synchronized boolean requestSplit(final Region r) {
return false;
}

public synchronized void requestSplit(final Region r, byte[] midKey) {
private synchronized void requestSplit(final Region r, byte[] midKey) {
requestSplit(r, midKey, null);
}

/*
* The User parameter allows the split thread to assume the correct user identity
*/
public synchronized void requestSplit(final Region r, byte[] midKey, User user) {
private synchronized void requestSplit(final Region r, byte[] midKey, User user) {
if (midKey == null) {
LOG.debug("Region " + r.getRegionInfo().getRegionNameAsString() +
" not splittable because midkey=null");
Expand Down Expand Up @@ -487,9 +489,9 @@ public int getSplitQueueSize() {
}

private boolean shouldSplitRegion() {
if(server.getNumberOfOnlineRegions() > 0.9*regionSplitLimit) {
if (server.getNumberOfOnlineRegions() > 0.9 * regionSplitLimit) {
LOG.warn("Total number of regions is approaching the upper limit " + regionSplitLimit + ". "
+ "Please consider taking a look at http://hbase.apache.org/book.html#ops.regionmgt");
+ "Please consider taking a look at http://hbase.apache.org/book.html#ops.regionmgt");
}
return (regionSplitLimit > server.getNumberOfOnlineRegions());
}
Expand Down Expand Up @@ -657,11 +659,14 @@ private void doCompaction(User user) {
this + "; duration=" + StringUtils.formatTimeDiff(now, start));
if (completed) {
// degenerate case: blocked regions require recursive enqueues
if (store.getCompactPriority() <= 0) {
if (region.getCompactPriority() < Store.PRIORITY_USER
&& store.getCompactPriority() <= 0) {
requestSystemCompaction(region, store, "Recursive enqueue");
} else {
// see if the compaction has caused us to exceed max region size
requestSplit(region);
if (!requestSplit(region) && store.getCompactPriority() <= 0) {
requestSystemCompaction(region, store, "Recursive enqueue");
}
}
}
} catch (IOException ex) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,10 @@ public class HRegion implements HeapSize, PropagatingConfigurationObserver, Regi
public static final String COMPACTION_AFTER_BULKLOAD_ENABLE =
"hbase.compaction.after.bulkload.enable";

/** Config for allow split when file count greater than the configured blocking file count*/
public static final String SPLIT_IGNORE_BLOCKING_ENABLED_KEY =
"hbase.hregion.split.ignore.blocking.enabled";

/**
* This is for for using HRegion as a local storage, where we may put the recovered edits in a
* special place. Once this is set, we will only replay the recovered edits under this directory
Expand Down Expand Up @@ -8241,6 +8245,10 @@ public Optional<byte[]> checkSplit(boolean force) {
* @return The priority that this region should have in the compaction queue
*/
public int getCompactPriority() {
if (checkSplit().isPresent() && conf.getBoolean(SPLIT_IGNORE_BLOCKING_ENABLED_KEY, false)) {
// if a region should split, split it before compact
return Store.PRIORITY_USER;
}
return stores.values().stream().mapToInt(HStore::getCompactPriority).min()
.orElse(Store.NO_PRIORITY);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*
* 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 static org.apache.hadoop.hbase.regionserver.HRegion.SPLIT_IGNORE_BLOCKING_ENABLED_KEY;
import static org.apache.hadoop.hbase.regionserver.Store.PRIORITY_USER;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import java.util.List;
import org.apache.hadoop.hbase.HBaseClassTestRule;
import org.apache.hadoop.hbase.HBaseTestingUtil;
import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.client.Table;
import org.apache.hadoop.hbase.client.TableDescriptor;
import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
import org.apache.hadoop.hbase.master.assignment.SplitTableRegionProcedure;
import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv;
import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
import org.apache.hadoop.hbase.testclassification.MediumTests;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.hbase.thirdparty.com.google.common.io.Closeables;

@Category({ MediumTests.class})
public class TestSplitWithBlockingFiles {

@ClassRule
public static final HBaseClassTestRule CLASS_RULE =
HBaseClassTestRule.forClass(TestSplitWithBlockingFiles.class);

private static final Logger LOG = LoggerFactory.getLogger(TestSplitWithBlockingFiles.class);

protected static final HBaseTestingUtil UTIL = new HBaseTestingUtil();
private static TableName TABLE_NAME = TableName.valueOf("test");
private static Admin ADMIN;
private static byte[] CF = Bytes.toBytes("cf");
private static Table TABLE;


@BeforeClass
public static void setupCluster() throws Exception {
UTIL.getConfiguration().setLong(HConstants.HREGION_MAX_FILESIZE, 8 * 2 * 10240L);
UTIL.getConfiguration().setInt(HStore.BLOCKING_STOREFILES_KEY, 1);
UTIL.getConfiguration().set(HConstants.HBASE_REGION_SPLIT_POLICY_KEY,
ConstantSizeRegionSplitPolicy.class.getName());
UTIL.getConfiguration().setBoolean(SPLIT_IGNORE_BLOCKING_ENABLED_KEY, true);
UTIL.startMiniCluster(1);
ADMIN = UTIL.getAdmin();
TableDescriptor td = TableDescriptorBuilder.newBuilder(TABLE_NAME)
.setColumnFamily(
ColumnFamilyDescriptorBuilder.newBuilder(CF).setBlocksize(1000).build()).build();
TABLE = UTIL.createTable(td, null);
UTIL.waitTableAvailable(TABLE_NAME);
}

@AfterClass
public static void cleanupTest() throws Exception {
Closeables.close(TABLE, true);
UTIL.shutdownMiniCluster();
}

@Test
public void testSplitIgnoreBlockingFiles() throws Exception {
ADMIN.splitSwitch(false, true);
byte[] value = new byte[1024];
for (int m = 0; m < 10; m++) {
String rowPrefix = "row" + m;
for (int i = 0; i < 10; i++) {
Put p = new Put(Bytes.toBytes(rowPrefix + i));
p.addColumn(CF, Bytes.toBytes("qualifier"), value);
p.addColumn(CF, Bytes.toBytes("qualifier2"), value);
TABLE.put(p);
}
ADMIN.flush(TABLE_NAME);
}
Scan scan = new Scan();
ResultScanner results = TABLE.getScanner(scan);
int count = 0;
while (results.next() != null) {
count++;
}
Assert.assertEquals("There should be 100 rows!", 100, count);
List<HRegion> regions = UTIL.getMiniHBaseCluster().getRegionServer(0).getRegions();
regions.removeIf(r -> !r.getRegionInfo().getTable().equals(TABLE_NAME));
assertEquals(1, regions.size());
assertNotNull(regions.get(0).getSplitPolicy().getSplitPoint());
assertTrue(regions.get(0).getCompactPriority() >= PRIORITY_USER);
assertTrue(UTIL.getMiniHBaseCluster().getRegionServer(0).getCompactSplitThread()
.requestSplit(regions.get(0)));

// split region
ADMIN.splitSwitch(true, true);
MasterProcedureEnv env =
UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor().getEnvironment();
final ProcedureExecutor<MasterProcedureEnv> executor =
UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor();
SplitTableRegionProcedure splitProcedure =
new SplitTableRegionProcedure(env, regions.get(0).getRegionInfo(), Bytes.toBytes("row5"));
executor.submitProcedure(splitProcedure);
ProcedureTestingUtility.waitProcedure(executor, splitProcedure.getProcId());

regions = UTIL.getMiniHBaseCluster().getRegionServer(0).getRegions();
regions.removeIf(r -> !r.getRegionInfo().getTable().equals(TABLE_NAME));
assertEquals(2, regions.size());
scan = new Scan();
results = TABLE.getScanner(scan);
count = 0;
while (results.next() != null) {
count++;
}
Assert.assertEquals("There should be 100 rows!", 100, count);
for (HRegion region : regions) {
assertTrue(region.getCompactPriority() < PRIORITY_USER);
assertFalse(
UTIL.getMiniHBaseCluster().getRegionServer(0).getCompactSplitThread().requestSplit(region));
}
}
}