Skip to content

Commit 1a866f4

Browse files
committed
HBASE-26242 Region never split when store file count larger than the configed blocking file count
1 parent a0864ed commit 1a866f4

File tree

3 files changed

+166
-9
lines changed

3 files changed

+166
-9
lines changed

hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/CompactSplit.java

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -188,10 +188,13 @@ public String dumpQueue() {
188188
}
189189

190190
public synchronized boolean requestSplit(final Region r) {
191-
// don't split regions that are blocking
191+
// We should split regions ignore blocking when it should split, if not a region will
192+
// never split if its speed of reducing the number of files through compaction is slower
193+
// than the speed of flush. Another reason is that, 1 parent compaction + 2 child compaction
194+
// can be reduced to 2 child compaction, which is faster and uses less IO.
192195
HRegion hr = (HRegion)r;
193196
try {
194-
if (shouldSplitRegion() && hr.getCompactPriority() >= PRIORITY_USER) {
197+
if (allowSplitRegion() && hr.getCompactPriority() >= PRIORITY_USER) {
195198
byte[] midKey = hr.checkSplit().orElse(null);
196199
if (midKey != null) {
197200
requestSplit(r, midKey);
@@ -206,14 +209,14 @@ public synchronized boolean requestSplit(final Region r) {
206209
return false;
207210
}
208211

209-
public synchronized void requestSplit(final Region r, byte[] midKey) {
212+
private synchronized void requestSplit(final Region r, byte[] midKey) {
210213
requestSplit(r, midKey, null);
211214
}
212215

213216
/*
214217
* The User parameter allows the split thread to assume the correct user identity
215218
*/
216-
public synchronized void requestSplit(final Region r, byte[] midKey, User user) {
219+
private synchronized void requestSplit(final Region r, byte[] midKey, User user) {
217220
if (midKey == null) {
218221
LOG.debug("Region " + r.getRegionInfo().getRegionNameAsString() +
219222
" not splittable because midkey=null");
@@ -456,10 +459,10 @@ public int getSplitQueueSize() {
456459
return splits.getQueue().size();
457460
}
458461

459-
private boolean shouldSplitRegion() {
460-
if(server.getNumberOfOnlineRegions() > 0.9*regionSplitLimit) {
462+
private boolean allowSplitRegion() {
463+
if (server.getNumberOfOnlineRegions() > 0.9 * regionSplitLimit) {
461464
LOG.warn("Total number of regions is approaching the upper limit " + regionSplitLimit + ". "
462-
+ "Please consider taking a look at http://hbase.apache.org/book.html#ops.regionmgt");
465+
+ "Please consider taking a look at http://hbase.apache.org/book.html#ops.regionmgt");
463466
}
464467
return (regionSplitLimit > server.getNumberOfOnlineRegions());
465468
}
@@ -620,11 +623,14 @@ private void doCompaction(User user) {
620623
this + "; duration=" + StringUtils.formatTimeDiff(now, start));
621624
if (completed) {
622625
// degenerate case: blocked regions require recursive enqueues
623-
if (store.getCompactPriority() <= 0) {
626+
if (region.getCompactPriority() < Store.PRIORITY_USER
627+
&& store.getCompactPriority() <= 0) {
624628
requestSystemCompaction(region, store, "Recursive enqueue");
625629
} else {
626630
// see if the compaction has caused us to exceed max region size
627-
requestSplit(region);
631+
if (!requestSplit(region) && store.getCompactPriority() <= 0) {
632+
requestSystemCompaction(region, store, "Recursive enqueue");
633+
}
628634
}
629635
}
630636
} catch (IOException ex) {

hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/HRegion.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7938,6 +7938,10 @@ public Optional<byte[]> checkSplit(boolean force) {
79387938
* @return The priority that this region should have in the compaction queue
79397939
*/
79407940
public int getCompactPriority() {
7941+
if (checkSplit().isPresent()) {
7942+
// if a region should split, split it before compact
7943+
return Store.PRIORITY_USER;
7944+
}
79417945
return stores.values().stream().mapToInt(HStore::getCompactPriority).min()
79427946
.orElse(Store.NO_PRIORITY);
79437947
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.hbase.regionserver;
19+
20+
import static org.apache.hadoop.hbase.regionserver.Store.PRIORITY_USER;
21+
import static org.junit.Assert.assertEquals;
22+
import static org.junit.Assert.assertFalse;
23+
import static org.junit.Assert.assertNotNull;
24+
import static org.junit.Assert.assertTrue;
25+
import java.util.List;
26+
import org.apache.hadoop.hbase.HBaseClassTestRule;
27+
import org.apache.hadoop.hbase.HBaseTestingUtil;
28+
import org.apache.hadoop.hbase.HConstants;
29+
import org.apache.hadoop.hbase.TableName;
30+
import org.apache.hadoop.hbase.client.Admin;
31+
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptorBuilder;
32+
import org.apache.hadoop.hbase.client.Put;
33+
import org.apache.hadoop.hbase.client.ResultScanner;
34+
import org.apache.hadoop.hbase.client.Scan;
35+
import org.apache.hadoop.hbase.client.Table;
36+
import org.apache.hadoop.hbase.client.TableDescriptor;
37+
import org.apache.hadoop.hbase.client.TableDescriptorBuilder;
38+
import org.apache.hadoop.hbase.master.assignment.SplitTableRegionProcedure;
39+
import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv;
40+
import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
41+
import org.apache.hadoop.hbase.procedure2.ProcedureTestingUtility;
42+
import org.apache.hadoop.hbase.testclassification.MediumTests;
43+
import org.apache.hadoop.hbase.util.Bytes;
44+
import org.junit.AfterClass;
45+
import org.junit.Assert;
46+
import org.junit.BeforeClass;
47+
import org.junit.ClassRule;
48+
import org.junit.Test;
49+
import org.junit.experimental.categories.Category;
50+
import org.slf4j.Logger;
51+
import org.slf4j.LoggerFactory;
52+
import org.apache.hbase.thirdparty.com.google.common.io.Closeables;
53+
54+
@Category({ MediumTests.class})
55+
public class TestSplitWithBlockingFiles {
56+
57+
@ClassRule
58+
public static final HBaseClassTestRule CLASS_RULE =
59+
HBaseClassTestRule.forClass(TestSplitWithBlockingFiles.class);
60+
61+
private static final Logger LOG = LoggerFactory.getLogger(TestSplitWithBlockingFiles.class);
62+
63+
protected static final HBaseTestingUtil UTIL = new HBaseTestingUtil();
64+
private static TableName TABLE_NAME = TableName.valueOf("test");
65+
private static Admin ADMIN;
66+
private static byte[] CF = Bytes.toBytes("cf");
67+
private static Table TABLE;
68+
69+
70+
@BeforeClass
71+
public static void setupCluster() throws Exception {
72+
UTIL.getConfiguration().setLong(HConstants.HREGION_MAX_FILESIZE, 8 * 2 * 10240L);
73+
UTIL.getConfiguration().setInt(HStore.BLOCKING_STOREFILES_KEY, 1);
74+
UTIL.getConfiguration().set(HConstants.HBASE_REGION_SPLIT_POLICY_KEY,
75+
ConstantSizeRegionSplitPolicy.class.getName());
76+
UTIL.startMiniCluster(1);
77+
ADMIN = UTIL.getAdmin();
78+
TableDescriptor td = TableDescriptorBuilder.newBuilder(TABLE_NAME)
79+
.setColumnFamily(
80+
ColumnFamilyDescriptorBuilder.newBuilder(CF).setBlocksize(1000).build()).build();
81+
TABLE = UTIL.createTable(td, null);
82+
UTIL.waitTableAvailable(TABLE_NAME);
83+
}
84+
85+
@AfterClass
86+
public static void cleanupTest() throws Exception {
87+
Closeables.close(TABLE, true);
88+
UTIL.shutdownMiniCluster();
89+
}
90+
91+
@Test
92+
public void test() throws Exception {
93+
ADMIN.splitSwitch(false, true);
94+
byte[] value = new byte[1024];
95+
for (int m = 0; m < 10; m++) {
96+
String rowPrefix = "row" + m;
97+
for (int i = 0; i < 10; i++) {
98+
Put p = new Put(Bytes.toBytes(rowPrefix + i));
99+
p.addColumn(CF, Bytes.toBytes("qualifier"), value);
100+
p.addColumn(CF, Bytes.toBytes("qualifier2"), value);
101+
TABLE.put(p);
102+
}
103+
ADMIN.flush(TABLE_NAME);
104+
}
105+
Scan scan = new Scan();
106+
ResultScanner results = TABLE.getScanner(scan);
107+
int count = 0;
108+
while (results.next() != null) {
109+
count++;
110+
}
111+
Assert.assertEquals("There should be 100 rows!", 100, count);
112+
List<HRegion> regions = UTIL.getMiniHBaseCluster().getRegionServer(0).getRegions();
113+
regions.removeIf(r -> !r.getRegionInfo().getTable().equals(TABLE_NAME));
114+
assertEquals(1, regions.size());
115+
assertNotNull(regions.get(0).getSplitPolicy().getSplitPoint());
116+
assertTrue(regions.get(0).getCompactPriority() >= PRIORITY_USER);
117+
assertTrue(UTIL.getMiniHBaseCluster().getRegionServer(0).getCompactSplitThread()
118+
.requestSplit(regions.get(0)));
119+
120+
// split region
121+
ADMIN.splitSwitch(true, true);
122+
MasterProcedureEnv env =
123+
UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor().getEnvironment();
124+
final ProcedureExecutor<MasterProcedureEnv> executor =
125+
UTIL.getMiniHBaseCluster().getMaster().getMasterProcedureExecutor();
126+
SplitTableRegionProcedure splitProcedure =
127+
new SplitTableRegionProcedure(env, regions.get(0).getRegionInfo(), Bytes.toBytes("row5"));
128+
executor.submitProcedure(splitProcedure);
129+
ProcedureTestingUtility.waitProcedure(executor, splitProcedure.getProcId());
130+
131+
regions = UTIL.getMiniHBaseCluster().getRegionServer(0).getRegions();
132+
regions.removeIf(r -> !r.getRegionInfo().getTable().equals(TABLE_NAME));
133+
assertEquals(2, regions.size());
134+
scan = new Scan();
135+
results = TABLE.getScanner(scan);
136+
count = 0;
137+
while (results.next() != null) {
138+
count++;
139+
}
140+
Assert.assertEquals("There should be 100 rows!", 100, count);
141+
for (HRegion region : regions) {
142+
assertTrue(region.getCompactPriority() < PRIORITY_USER);
143+
assertFalse(
144+
UTIL.getMiniHBaseCluster().getRegionServer(0).getCompactSplitThread().requestSplit(region));
145+
}
146+
}
147+
}

0 commit comments

Comments
 (0)