org.apache.hadoop.hbase.mob.MobFileCompactionChore Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of hbase-server Show documentation
Show all versions of hbase-server Show documentation
Server functionality for HBase
/*
* 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.mob;
import com.google.errorprone.annotations.RestrictedApi;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.ScheduledChore;
import org.apache.hadoop.hbase.TableDescriptors;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
import org.apache.hadoop.hbase.client.CompactionState;
import org.apache.hadoop.hbase.client.RegionInfo;
import org.apache.hadoop.hbase.client.TableDescriptor;
import org.apache.hadoop.hbase.client.TableState;
import org.apache.hadoop.hbase.master.HMaster;
import org.apache.hadoop.hbase.util.Bytes;
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
import org.apache.yetus.audience.InterfaceAudience;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Periodic MOB compaction chore. It runs MOB compaction on region servers in parallel, thus
* utilizing distributed cluster resources. To avoid possible major compaction storms, one can
* specify maximum number regions to be compacted in parallel by setting configuration parameter:
*
* 'hbase.mob.major.compaction.region.batch.size', which by default is 0 (unlimited).
*/
@InterfaceAudience.Private
public class MobFileCompactionChore extends ScheduledChore {
private static final Logger LOG = LoggerFactory.getLogger(MobFileCompactionChore.class);
private HMaster master;
private int regionBatchSize = 0;// not set - compact all
public MobFileCompactionChore(HMaster master) {
super(master.getServerName() + "-MobFileCompactionChore", master,
master.getConfiguration().getInt(MobConstants.MOB_COMPACTION_CHORE_PERIOD,
MobConstants.DEFAULT_MOB_COMPACTION_CHORE_PERIOD),
master.getConfiguration().getInt(MobConstants.MOB_COMPACTION_CHORE_PERIOD,
MobConstants.DEFAULT_MOB_COMPACTION_CHORE_PERIOD),
TimeUnit.SECONDS);
this.master = master;
this.regionBatchSize =
master.getConfiguration().getInt(MobConstants.MOB_MAJOR_COMPACTION_REGION_BATCH_SIZE,
MobConstants.DEFAULT_MOB_MAJOR_COMPACTION_REGION_BATCH_SIZE);
}
@RestrictedApi(explanation = "Should only be called in tests", link = "",
allowedOnPath = ".*/src/test/.*")
public MobFileCompactionChore(Configuration conf, int batchSize) {
this.regionBatchSize = batchSize;
}
@Override
protected void chore() {
boolean reported = false;
try (Admin admin = master.getConnection().getAdmin()) {
TableDescriptors htds = master.getTableDescriptors();
Map map = htds.getAll();
for (TableDescriptor htd : map.values()) {
if (
!master.getTableStateManager().isTableState(htd.getTableName(), TableState.State.ENABLED)
) {
LOG.info("Skipping MOB compaction on table {} because it is not ENABLED",
htd.getTableName());
continue;
} else {
LOG.info("Starting MOB compaction on table {}, checking {} column families",
htd.getTableName(), htd.getColumnFamilyCount());
}
for (ColumnFamilyDescriptor hcd : htd.getColumnFamilies()) {
try {
if (hcd.isMobEnabled()) {
if (!reported) {
master.reportMobCompactionStart(htd.getTableName());
reported = true;
}
LOG.info("Major MOB compacting table={} cf={}", htd.getTableName(),
hcd.getNameAsString());
if (regionBatchSize == MobConstants.DEFAULT_MOB_MAJOR_COMPACTION_REGION_BATCH_SIZE) {
LOG.debug(
"Table={} cf ={}: batch MOB compaction is disabled, {}=0 -"
+ " all regions will be compacted in parallel",
htd.getTableName(), hcd.getNameAsString(), "hbase.mob.compaction.batch.size");
admin.majorCompact(htd.getTableName(), hcd.getName());
} else {
LOG.info(
"Table={} cf={}: performing MOB major compaction in batches "
+ "'hbase.mob.compaction.batch.size'={}",
htd.getTableName(), hcd.getNameAsString(), regionBatchSize);
performMajorCompactionInBatches(admin, htd, hcd);
}
} else {
LOG.debug("Skipping table={} column family={} because it is not MOB-enabled",
htd.getTableName(), hcd.getNameAsString());
}
} catch (IOException e) {
LOG.error("Failed to compact table={} cf={}", htd.getTableName(), hcd.getNameAsString(),
e);
} catch (InterruptedException ee) {
Thread.currentThread().interrupt();
master.reportMobCompactionEnd(htd.getTableName());
LOG.warn("Failed to compact table={} cf={}", htd.getTableName(), hcd.getNameAsString(),
ee);
// Quit the chore
return;
}
}
if (reported) {
master.reportMobCompactionEnd(htd.getTableName());
reported = false;
}
}
} catch (IOException e) {
LOG.error("Failed to compact", e);
}
}
@RestrictedApi(explanation = "Should only be called in tests", link = "",
allowedOnPath = ".*(/src/test/.*|MobFileCompactionChore).java")
public void performMajorCompactionInBatches(Admin admin, TableDescriptor htd,
ColumnFamilyDescriptor hcd) throws IOException, InterruptedException {
List regions = admin.getRegions(htd.getTableName());
if (regions.size() <= this.regionBatchSize) {
LOG.debug(
"Table={} cf={} - performing major MOB compaction in non-batched mode,"
+ "regions={}, batch size={}",
htd.getTableName(), hcd.getNameAsString(), regions.size(), regionBatchSize);
admin.majorCompact(htd.getTableName(), hcd.getName());
return;
}
// Shuffle list of regions in case if they come ordered by region server
Collections.shuffle(regions);
// Create first batch
List toCompact = new ArrayList(this.regionBatchSize);
for (int i = 0; i < this.regionBatchSize; i++) {
toCompact.add(regions.remove(0));
}
// Start compaction now
for (RegionInfo ri : toCompact) {
startCompaction(admin, htd.getTableName(), ri, hcd.getName());
}
List compacted = new ArrayList(toCompact.size());
List failed = new ArrayList();
int totalCompacted = 0;
while (!toCompact.isEmpty()) {
// Check status of active compactions
for (RegionInfo ri : toCompact) {
try {
if (admin.getCompactionStateForRegion(ri.getRegionName()) == CompactionState.NONE) {
totalCompacted++;
LOG.info("Finished major MOB compaction: table={} cf={} region={} compacted regions={}",
htd.getTableName(), hcd.getNameAsString(), ri.getRegionNameAsString(),
totalCompacted);
compacted.add(ri);
}
} catch (IOException e) {
LOG.error(
"Could not get compaction state for table={} cf={} region={}, compaction will"
+ " aborted for the region.",
htd.getTableName(), hcd.getNameAsString(), ri.getEncodedName());
LOG.error("Because of:", e);
failed.add(ri);
}
}
// Remove failed regions to avoid
// endless compaction loop
toCompact.removeAll(failed);
failed.clear();
// Update batch: remove compacted regions and add new ones
for (RegionInfo ri : compacted) {
toCompact.remove(ri);
if (regions.size() > 0) {
RegionInfo region = regions.remove(0);
toCompact.add(region);
startCompaction(admin, htd.getTableName(), region, hcd.getName());
}
}
compacted.clear();
LOG.debug(
"Table={} cf={}. Wait for 10 sec, toCompact size={} regions left={}"
+ " compacted so far={}",
htd.getTableName(), hcd.getNameAsString(), toCompact.size(), regions.size(),
totalCompacted);
Thread.sleep(10000);
}
LOG.info("Finished major MOB compacting table={}. cf={}", htd.getTableName(),
hcd.getNameAsString());
}
private void startCompaction(Admin admin, TableName table, RegionInfo region, byte[] cf)
throws IOException, InterruptedException {
LOG.info("Started major compaction: table={} cf={} region={}", table, Bytes.toString(cf),
region.getRegionNameAsString());
admin.majorCompactRegion(region.getRegionName(), cf);
// Wait until it really starts
// but with finite timeout
long waitTime = 300000; // 5 min
long startTime = EnvironmentEdgeManager.currentTime();
while (admin.getCompactionStateForRegion(region.getRegionName()) == CompactionState.NONE) {
// Is 1 second too aggressive?
Thread.sleep(1000);
if (EnvironmentEdgeManager.currentTime() - startTime > waitTime) {
LOG.warn("Waited for {} ms to start major MOB compaction on table={} cf={} region={}."
+ " Stopped waiting for request confirmation. This is not an ERROR, continue next region.",
waitTime, table.getNameAsString(), Bytes.toString(cf), region.getRegionNameAsString());
break;
}
}
}
}