Skip to content

Commit 21240f6

Browse files
PR feedback: move plans truncation into RegionNormalizerWorker
1 parent ea41bf2 commit 21240f6

File tree

4 files changed

+81
-43
lines changed

4 files changed

+81
-43
lines changed

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

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import java.io.IOException;
2121
import java.time.Duration;
22+
import java.util.ArrayList;
2223
import java.util.Collections;
2324
import java.util.List;
2425
import org.apache.hadoop.conf.Configuration;
@@ -53,6 +54,9 @@ class RegionNormalizerWorker implements PropagatingConfigurationObserver, Runnab
5354
"hbase.normalizer.throughput.max_bytes_per_sec";
5455
private static final long RATE_UNLIMITED_BYTES = 1_000_000_000_000L; // 1TB/sec
5556

57+
static final String CUMULATIVE_SIZE_LIMIT_MB_KEY = "hbase.normalizer.plans_size_limit.mb";
58+
static final long DEFAULT_CUMULATIVE_SIZE_LIMIT_MB = Long.MAX_VALUE;
59+
5660
private final MasterServices masterServices;
5761
private final RegionNormalizer regionNormalizer;
5862
private final RegionNormalizerWorkQueue<TableName> workQueue;
@@ -62,6 +66,7 @@ class RegionNormalizerWorker implements PropagatingConfigurationObserver, Runnab
6266
private final boolean defaultNormalizerTableLevel;
6367
private long splitPlanCount;
6468
private long mergePlanCount;
69+
private long cumulativePlansSizeLimitMb;
6570

6671
RegionNormalizerWorker(final Configuration configuration, final MasterServices masterServices,
6772
final RegionNormalizer regionNormalizer, final RegionNormalizerWorkQueue<TableName> workQueue) {
@@ -73,6 +78,8 @@ class RegionNormalizerWorker implements PropagatingConfigurationObserver, Runnab
7378
this.mergePlanCount = 0;
7479
this.rateLimiter = loadRateLimiter(configuration);
7580
this.defaultNormalizerTableLevel = extractDefaultNormalizerValue(configuration);
81+
this.cumulativePlansSizeLimitMb =
82+
configuration.getLong(CUMULATIVE_SIZE_LIMIT_MB_KEY, DEFAULT_CUMULATIVE_SIZE_LIMIT_MB);
7683
}
7784

7885
private boolean extractDefaultNormalizerValue(final Configuration configuration) {
@@ -207,14 +214,34 @@ private List<NormalizationPlan> calculatePlans(final TableName tableName) {
207214
return Collections.emptyList();
208215
}
209216

210-
final List<NormalizationPlan> plans = regionNormalizer.computePlansForTable(tblDesc);
217+
List<NormalizationPlan> plans = regionNormalizer.computePlansForTable(tblDesc);
218+
219+
plans = truncateForSize(plans);
220+
211221
if (CollectionUtils.isEmpty(plans)) {
212222
LOG.debug("No normalization required for table {}.", tableName);
213223
return Collections.emptyList();
214224
}
215225
return plans;
216226
}
217227

228+
private List<NormalizationPlan> truncateForSize(List<NormalizationPlan> plans) {
229+
if (cumulativePlansSizeLimitMb != DEFAULT_CUMULATIVE_SIZE_LIMIT_MB) {
230+
List<NormalizationPlan> maybeTruncatedPlans = new ArrayList<>(plans.size());
231+
long cumulativeSizeMb = 0;
232+
for (NormalizationPlan plan : plans) {
233+
cumulativeSizeMb += plan.getPlanSizeMb();
234+
if (cumulativeSizeMb > cumulativePlansSizeLimitMb) {
235+
break;
236+
}
237+
maybeTruncatedPlans.add(plan);
238+
}
239+
return maybeTruncatedPlans;
240+
} else {
241+
return plans;
242+
}
243+
}
244+
218245
private void submitPlans(final List<NormalizationPlan> plans) {
219246
// as of this writing, `plan.submit()` is non-blocking and uses Async Admin APIs to submit
220247
// task, so there's no artificial rate-limiting of merge/split requests due to this serial loop.

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

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,13 @@ public List<NormalizationPlan> computePlansForTable(final TableDescriptor tableD
231231
plans.addAll(mergePlans);
232232
}
233233

234-
plans = truncateForSize(plans);
234+
if (
235+
normalizerConfiguration.getCumulativePlansSizeLimitMb() != DEFAULT_CUMULATIVE_SIZE_LIMIT_MB
236+
) {
237+
// If we are going to truncate our list of plans, shuffle the split and merge plans together
238+
// so that the merge plans, which are listed last, are not starved out.
239+
shuffleNormalizationPlans(plans);
240+
}
235241

236242
LOG.debug("Computed normalization plans for table {}. Total plans: {}, split plans: {}, "
237243
+ "merge plans: {}", table, plans.size(), splitPlansCount, mergePlansCount);
@@ -468,25 +474,12 @@ private boolean isLargeEnoughForMerge(final NormalizerConfiguration normalizerCo
468474
return getRegionSizeMB(regionInfo) >= normalizerConfiguration.getMergeMinRegionSizeMb(ctx);
469475
}
470476

471-
private List<NormalizationPlan> truncateForSize(List<NormalizationPlan> plans) {
472-
if (
473-
normalizerConfiguration.getCumulativePlansSizeLimitMb() != DEFAULT_CUMULATIVE_SIZE_LIMIT_MB
474-
) {
475-
// If we are going to truncate our list of plans, shuffle the split and merge plans together
476-
// so that the merge plans, which are listed last, are not starved out.
477-
List<NormalizationPlan> maybeTruncatedPlans = new ArrayList<>();
478-
Collections.shuffle(plans);
479-
long cumulativeSizeMb = 0;
480-
for (NormalizationPlan plan : plans) {
481-
cumulativeSizeMb += plan.getPlanSizeMb();
482-
if (cumulativeSizeMb < normalizerConfiguration.getCumulativePlansSizeLimitMb()) {
483-
maybeTruncatedPlans.add(plan);
484-
}
485-
}
486-
return maybeTruncatedPlans;
487-
} else {
488-
return plans;
489-
}
477+
/**
478+
* This very simple method exists so we can verify it was called in a unit test. Visible for
479+
* testing.
480+
*/
481+
void shuffleNormalizationPlans(List<NormalizationPlan> plans) {
482+
Collections.shuffle(plans);
490483
}
491484

492485
private static boolean logTraceReason(final BooleanSupplier predicate, final String fmtWhenTrue,

hbase-server/src/test/java/org/apache/hadoop/hbase/master/normalizer/TestRegionNormalizerWorker.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,36 @@ public void testRateLimit() throws Exception {
204204
Duration.ofNanos(endTime - startTime), greaterThanOrEqualTo(Duration.ofSeconds(5)));
205205
}
206206

207+
@Test
208+
public void testPlansSizeLimit() throws Exception {
209+
final TableName tn = tableName.getTableName();
210+
final TableDescriptor tnDescriptor =
211+
TableDescriptorBuilder.newBuilder(tn).setNormalizationEnabled(true).build();
212+
final RegionInfo splitRegionInfo = RegionInfoBuilder.newBuilder(tn).build();
213+
final RegionInfo mergeRegionInfo1 = RegionInfoBuilder.newBuilder(tn).build();
214+
final RegionInfo mergeRegionInfo2 = RegionInfoBuilder.newBuilder(tn).build();
215+
when(masterServices.getTableDescriptors().get(tn)).thenReturn(tnDescriptor);
216+
when(masterServices.splitRegion(any(), any(), anyLong(), anyLong())).thenReturn(1L);
217+
when(masterServices.mergeRegions(any(), anyBoolean(), anyLong(), anyLong())).thenReturn(1L);
218+
when(regionNormalizer.computePlansForTable(tnDescriptor)).thenReturn(Arrays.asList(
219+
new SplitNormalizationPlan(splitRegionInfo, 2), new MergeNormalizationPlan.Builder()
220+
.addTarget(mergeRegionInfo1, 1).addTarget(mergeRegionInfo2, 2).build(),
221+
new SplitNormalizationPlan(splitRegionInfo, 1)));
222+
223+
final Configuration conf = testingUtility.getConfiguration();
224+
conf.setLong(RegionNormalizerWorker.CUMULATIVE_SIZE_LIMIT_MB_KEY, 5);
225+
226+
final RegionNormalizerWorker worker = new RegionNormalizerWorker(
227+
testingUtility.getConfiguration(), masterServices, regionNormalizer, queue);
228+
workerPool.submit(worker);
229+
queue.put(tn);
230+
231+
assertThatEventually("worker should process first split plan, but not second",
232+
worker::getSplitPlanCount, comparesEqualTo(1L));
233+
assertThatEventually("worker should process merge plan", worker::getMergePlanCount,
234+
comparesEqualTo(1L));
235+
}
236+
207237
/**
208238
* Repeatedly evaluates {@code matcher} against the result of calling {@code actualSupplier} until
209239
* the matcher succeeds or the timeout period of 30 seconds is exhausted.

hbase-server/src/test/java/org/apache/hadoop/hbase/master/normalizer/TestSimpleRegionNormalizer.java

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,11 @@
3838
import static org.junit.Assert.assertFalse;
3939
import static org.junit.Assert.assertTrue;
4040
import static org.mockito.ArgumentMatchers.any;
41+
import static org.mockito.ArgumentMatchers.anyList;
4142
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
43+
import static org.mockito.Mockito.spy;
44+
import static org.mockito.Mockito.times;
45+
import static org.mockito.Mockito.verify;
4246
import static org.mockito.Mockito.when;
4347

4448
import java.time.Instant;
@@ -610,36 +614,20 @@ public void testNormalizerCannotMergeNonAdjacentRegions() {
610614
}
611615

612616
@Test
613-
public void testMergeSizeLimit() {
614-
conf.setBoolean(SPLIT_ENABLED_KEY, false);
615-
conf.setLong(CUMULATIVE_SIZE_LIMIT_MB_KEY, 5);
616-
final TableName tableName = name.getTableName();
617-
final List<RegionInfo> regionInfos = createRegionInfos(tableName, 6);
618-
final Map<byte[], Integer> regionSizes = createRegionSizesMap(regionInfos, 1, 1, 1, 1, 1, 1);
619-
setupMocksForNormalizer(regionSizes, regionInfos);
620-
when(tableDescriptor.getNormalizerTargetRegionSize()).thenReturn(2L);
621-
622-
assertTrue(normalizer.isMergeEnabled());
623-
assertFalse(normalizer.isSplitEnabled());
624-
// creates 2 merge plans, even though there are 3 otherwise eligible pairs of regions
625-
assertThat(normalizer.computePlansForTable(tableDescriptor), hasSize(2));
626-
}
627-
628-
@Test
629-
public void testSplitSizeLimit() {
630-
conf.setBoolean(MERGE_ENABLED_KEY, false);
617+
public void testSizeLimitShufflesPlans() {
631618
conf.setLong(CUMULATIVE_SIZE_LIMIT_MB_KEY, 10);
632619
final TableName tableName = name.getTableName();
633620
final List<RegionInfo> regionInfos = createRegionInfos(tableName, 4);
634621
final Map<byte[], Integer> regionSizes = createRegionSizesMap(regionInfos, 3, 3, 3, 3);
635622
setupMocksForNormalizer(regionSizes, regionInfos);
636623
when(tableDescriptor.getNormalizerTargetRegionSize()).thenReturn(1L);
624+
normalizer = spy(normalizer);
637625

638626
assertTrue(normalizer.isSplitEnabled());
639-
assertFalse(normalizer.isMergeEnabled());
640-
// the plan includes only 3 regions, even though the table has 4 otherwise split-eligible
641-
// regions
642-
assertThat(normalizer.computePlansForTable(tableDescriptor), hasSize(3));
627+
assertTrue(normalizer.isMergeEnabled());
628+
List<NormalizationPlan> computedPlans = normalizer.computePlansForTable(tableDescriptor);
629+
assertThat(computedPlans, hasSize(4));
630+
verify(normalizer, times(1)).shuffleNormalizationPlans(anyList());
643631
}
644632

645633
@SuppressWarnings("MockitoCast")

0 commit comments

Comments
 (0)