Skip to content

Commit a55fbf2

Browse files
committed
HBASE-27903 Skip submitting Split/Merge procedure when split/merge is disabled at table level (#6169)
- Fail fast by adding a check before even submitting a procedure - Update test cases to assert for expected exception post this change - Remove deprecated method mergeRegionsAsync's usage in test - Make use of RegionInfo.getShortNameToLog instead of logging complete region info - Update comments in procedure implementation Signed-off-by: Duo Zhang <[email protected]> (cherry picked from commit 2ac657c)
1 parent 4cfe8cc commit a55fbf2

File tree

4 files changed

+46
-30
lines changed

4 files changed

+46
-30
lines changed

hbase-server/src/main/java/org/apache/hadoop/hbase/master/HMaster.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2197,21 +2197,26 @@ public long mergeRegions(final RegionInfo[] regionsToMerge, final boolean forcib
21972197
final long nonce) throws IOException {
21982198
checkInitialized();
21992199

2200+
final String regionNamesToLog = RegionInfo.getShortNameToLog(regionsToMerge);
2201+
22002202
if (!isSplitOrMergeEnabled(MasterSwitchType.MERGE)) {
2201-
String regionsStr = Arrays.deepToString(regionsToMerge);
2202-
LOG.warn("Merge switch is off! skip merge of " + regionsStr);
2203+
LOG.warn("Merge switch is off! skip merge of " + regionNamesToLog);
2204+
throw new DoNotRetryIOException(
2205+
"Merge of " + regionNamesToLog + " failed because merge switch is off");
2206+
}
2207+
2208+
if (!getTableDescriptors().get(regionsToMerge[0].getTable()).isMergeEnabled()) {
2209+
LOG.warn("Merge is disabled for the table! Skipping merge of {}", regionNamesToLog);
22032210
throw new DoNotRetryIOException(
2204-
"Merge of " + regionsStr + " failed because merge switch is off");
2211+
"Merge of " + regionNamesToLog + " failed as region merge is disabled for the table");
22052212
}
22062213

2207-
final String mergeRegionsStr = Arrays.stream(regionsToMerge).map(RegionInfo::getEncodedName)
2208-
.collect(Collectors.joining(", "));
22092214
return MasterProcedureUtil.submitProcedure(new NonceProcedureRunnable(this, ng, nonce) {
22102215
@Override
22112216
protected void run() throws IOException {
22122217
getMaster().getMasterCoprocessorHost().preMergeRegions(regionsToMerge);
22132218
String aid = getClientIdAuditPrefix();
2214-
LOG.info("{} merge regions {}", aid, mergeRegionsStr);
2219+
LOG.info("{} merge regions {}", aid, regionNamesToLog);
22152220
submitProcedure(new MergeTableRegionsProcedure(procedureExecutor.getEnvironment(),
22162221
regionsToMerge, forcible));
22172222
getMaster().getMasterCoprocessorHost().postMergeRegions(regionsToMerge);
@@ -2235,6 +2240,12 @@ public long splitRegion(final RegionInfo regionInfo, final byte[] splitRow, fina
22352240
"Split region " + regionInfo.getRegionNameAsString() + " failed due to split switch off");
22362241
}
22372242

2243+
if (!getTableDescriptors().get(regionInfo.getTable()).isSplitEnabled()) {
2244+
LOG.warn("Split is disabled for the table! Skipping split of {}", regionInfo);
2245+
throw new DoNotRetryIOException("Split region " + regionInfo.getRegionNameAsString()
2246+
+ " failed as region split is disabled for the table");
2247+
}
2248+
22382249
return MasterProcedureUtil
22392250
.submitProcedure(new MasterProcedureUtil.NonceProcedureRunnable(this, nonceGroup, nonce) {
22402251
@Override

hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/MergeTableRegionsProcedure.java

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -444,35 +444,36 @@ protected ProcedureMetrics getProcedureMetrics(MasterProcedureEnv env) {
444444
private boolean prepareMergeRegion(final MasterProcedureEnv env) throws IOException {
445445
// Fail if we are taking snapshot for the given table
446446
TableName tn = regionsToMerge[0].getTable();
447+
final String regionNamesToLog = RegionInfo.getShortNameToLog(regionsToMerge);
447448
if (env.getMasterServices().getSnapshotManager().isTableTakingAnySnapshot(tn)) {
448-
throw new MergeRegionException("Skip merging regions "
449-
+ RegionInfo.getShortNameToLog(regionsToMerge) + ", because we are snapshotting " + tn);
449+
throw new MergeRegionException(
450+
"Skip merging regions " + regionNamesToLog + ", because we are snapshotting " + tn);
450451
}
451452

452-
// Mostly this check is not used because we already check the switch before submit a merge
453-
// procedure. Just for safe, check the switch again. This procedure can be rollbacked if
454-
// the switch was set to false after submit.
453+
// Mostly the below two checks are not used because we already check the switches before
454+
// submitting the merge procedure. Just for safety, we are checking the switch again here.
455+
// Also, in case the switch was set to false after submission, this procedure can be rollbacked,
456+
// thanks to this double check!
457+
// case 1: check for cluster level switch
455458
if (!env.getMasterServices().isSplitOrMergeEnabled(MasterSwitchType.MERGE)) {
456-
String regionsStr = Arrays.deepToString(this.regionsToMerge);
457-
LOG.warn("Merge switch is off! skip merge of " + regionsStr);
459+
LOG.warn("Merge switch is off! skip merge of " + regionNamesToLog);
458460
setFailure(getClass().getSimpleName(),
459-
new IOException("Merge of " + regionsStr + " failed because merge switch is off"));
461+
new IOException("Merge of " + regionNamesToLog + " failed because merge switch is off"));
460462
return false;
461463
}
462-
464+
// case 2: check for table level switch
463465
if (!env.getMasterServices().getTableDescriptors().get(getTableName()).isMergeEnabled()) {
464-
String regionsStr = Arrays.deepToString(regionsToMerge);
465-
LOG.warn("Merge is disabled for the table! Skipping merge of {}", regionsStr);
466+
LOG.warn("Merge is disabled for the table! Skipping merge of {}", regionNamesToLog);
466467
setFailure(getClass().getSimpleName(), new IOException(
467-
"Merge of " + regionsStr + " failed as region merge is disabled for the table"));
468+
"Merge of " + regionNamesToLog + " failed as region merge is disabled for the table"));
468469
return false;
469470
}
470471

471472
RegionStates regionStates = env.getAssignmentManager().getRegionStates();
472473
for (RegionInfo ri : this.regionsToMerge) {
473474
if (MetaTableAccessor.hasMergeRegions(env.getMasterServices().getConnection(), ri)) {
474-
String msg = "Skip merging " + RegionInfo.getShortNameToLog(regionsToMerge)
475-
+ ", because a parent, " + RegionInfo.getShortNameToLog(ri) + ", has a merge qualifier "
475+
String msg = "Skip merging " + regionNamesToLog + ", because a parent, "
476+
+ RegionInfo.getShortNameToLog(ri) + ", has a merge qualifier "
476477
+ "(if a 'merge column' in parent, it was recently merged but still has outstanding "
477478
+ "references to its parents that must be cleared before it can participate in merge -- "
478479
+ "major compact it to hurry clearing of its references)";
@@ -492,8 +493,8 @@ private boolean prepareMergeRegion(final MasterProcedureEnv env) throws IOExcept
492493
try {
493494
if (!isMergeable(env, state)) {
494495
setFailure(getClass().getSimpleName(),
495-
new MergeRegionException("Skip merging " + RegionInfo.getShortNameToLog(regionsToMerge)
496-
+ ", because a parent, " + RegionInfo.getShortNameToLog(ri) + ", is not mergeable"));
496+
new MergeRegionException("Skip merging " + regionNamesToLog + ", because a parent, "
497+
+ RegionInfo.getShortNameToLog(ri) + ", is not mergeable"));
497498
return false;
498499
}
499500
} catch (IOException e) {

hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/SplitTableRegionProcedure.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -547,16 +547,18 @@ public boolean prepareSplitRegion(final MasterProcedureEnv env) throws IOExcepti
547547
return false;
548548
}
549549

550-
// Mostly this check is not used because we already check the switch before submit a split
551-
// procedure. Just for safe, check the switch again. This procedure can be rollbacked if
552-
// the switch was set to false after submit.
550+
// Mostly the below two checks are not used because we already check the switches before
551+
// submitting the split procedure. Just for safety, we are checking the switch again here.
552+
// Also, in case the switch was set to false after submission, this procedure can be rollbacked,
553+
// thanks to this double check!
554+
// case 1: check for cluster level switch
553555
if (!env.getMasterServices().isSplitOrMergeEnabled(MasterSwitchType.SPLIT)) {
554556
LOG.warn("pid=" + getProcId() + " split switch is off! skip split of " + parentHRI);
555557
setFailure(new IOException(
556558
"Split region " + parentHRI.getRegionNameAsString() + " failed due to split switch off"));
557559
return false;
558560
}
559-
561+
// case 2: check for table level switch
560562
if (!env.getMasterServices().getTableDescriptors().get(getTableName()).isSplitEnabled()) {
561563
LOG.warn("pid={}, split is disabled for the table! Skipping split of {}", getProcId(),
562564
parentHRI);

hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestSplitOrMergeAtTableLevel.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,14 @@
2525
import java.util.concurrent.ExecutionException;
2626
import java.util.concurrent.Future;
2727
import java.util.concurrent.TimeUnit;
28+
import org.apache.hadoop.hbase.DoNotRetryIOException;
2829
import org.apache.hadoop.hbase.HBaseClassTestRule;
2930
import org.apache.hadoop.hbase.HBaseTestingUtility;
3031
import org.apache.hadoop.hbase.TableName;
3132
import org.apache.hadoop.hbase.Waiter.ExplainingPredicate;
3233
import org.apache.hadoop.hbase.testclassification.ClientTests;
3334
import org.apache.hadoop.hbase.testclassification.MediumTests;
3435
import org.apache.hadoop.hbase.util.Bytes;
35-
import org.apache.hadoop.hbase.util.Threads;
3636
import org.junit.AfterClass;
3737
import org.junit.BeforeClass;
3838
import org.junit.ClassRule;
@@ -125,7 +125,7 @@ public void testTableMergeSwitch() throws Exception {
125125
assertFalse(admin.getDescriptor(tableName).isMergeEnabled());
126126

127127
trySplitAndEnsureItIsSuccess(tableName);
128-
Threads.sleep(10000);
128+
129129
tryMergeAndEnsureItFails(tableName);
130130
admin.disableTable(tableName);
131131
enableTableMerge(tableName);
@@ -166,6 +166,7 @@ private void trySplitAndEnsureItFails(final TableName tableName) throws Exceptio
166166
// expected to reach here
167167
// check and ensure that table does not get splitted
168168
assertTrue(admin.getRegions(tableName).size() == originalCount);
169+
assertTrue("Expected DoNotRetryIOException!", ee.getCause() instanceof DoNotRetryIOException);
169170
}
170171
}
171172

@@ -217,14 +218,15 @@ private void tryMergeAndEnsureItFails(final TableName tableName) throws Exceptio
217218
byte[] nameOfRegionB = regions.get(1).getEncodedNameAsBytes();
218219

219220
// check and ensure that region do not get merged
220-
Future<?> f = admin.mergeRegionsAsync(nameOfRegionA, nameOfRegionB, true);
221+
Future<?> f = admin.mergeRegionsAsync(new byte[][] { nameOfRegionA, nameOfRegionB }, true);
221222
try {
222223
f.get(10, TimeUnit.SECONDS);
223224
fail("Should not get here.");
224225
} catch (ExecutionException ee) {
225226
// expected to reach here
226227
// check and ensure that region do not get merged
227228
assertTrue(admin.getRegions(tableName).size() == originalCount);
229+
assertTrue("Expected DoNotRetryIOException!", ee.getCause() instanceof DoNotRetryIOException);
228230
}
229231
}
230232

@@ -255,7 +257,7 @@ private void tryMergeAndEnsureItIsSuccess(final TableName tableName) throws Exce
255257
byte[] nameOfRegionB = regions.get(1).getEncodedNameAsBytes();
256258

257259
// merge the table regions and wait until region count decreases
258-
admin.mergeRegionsAsync(nameOfRegionA, nameOfRegionB, true);
260+
admin.mergeRegionsAsync(new byte[][] { nameOfRegionA, nameOfRegionB }, true);
259261
TEST_UTIL.waitFor(30000, new ExplainingPredicate<Exception>() {
260262

261263
@Override

0 commit comments

Comments
 (0)