diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/MergeTableRegionsProcedure.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/MergeTableRegionsProcedure.java index 20ae444..0ad6f57 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/MergeTableRegionsProcedure.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/MergeTableRegionsProcedure.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.stream.Collectors; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; @@ -51,6 +52,7 @@ import org.apache.hadoop.hbase.master.normalizer.NormalizationPlan; import org.apache.hadoop.hbase.master.procedure.AbstractStateMachineTableProcedure; import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv; import org.apache.hadoop.hbase.master.procedure.MasterProcedureUtil; +import org.apache.hadoop.hbase.master.procedure.TableQueue; import org.apache.hadoop.hbase.procedure2.ProcedureMetrics; import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer; import org.apache.hadoop.hbase.quotas.QuotaExceededException; @@ -528,6 +530,24 @@ public class MergeTableRegionsProcedure return false; } + // A safe fence here, if there is a table procedure going on, abort the merge. + // There some cases that may lead to table procedure roll back (more serious + // than roll back the merge procedure here), or the merged regions was brought online + // by the table procedure because of the race between merge procedure and table procedure + List tableProcedures = env + .getMasterServices().getProcedures().stream() + .filter(p -> p instanceof AbstractStateMachineTableProcedure) + .map(p -> (AbstractStateMachineTableProcedure) p) + .filter(p -> p.getProcId() != this.getProcId() && p.getTableName() + .equals(regionsToMerge[0].getTable()) && !p.isFinished() + && TableQueue.requireTableExclusiveLock(p)) + .collect(Collectors.toList()); + if (tableProcedures != null && tableProcedures.size() > 0) { + throw new MergeRegionException(tableProcedures.get(0).toString() + + " is going on against the same table, abort the merge of " + this + .toString()); + } + // Ask the remote regionserver if regions are mergeable. If we get an IOE, report it // along with the failure, so we can see why regions are not mergeable at this time. IOException mergeableCheckIOE = null; diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/SplitTableRegionProcedure.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/SplitTableRegionProcedure.java index 411077f..bb57f09 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/SplitTableRegionProcedure.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/assignment/SplitTableRegionProcedure.java @@ -32,6 +32,8 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.FileSystem; import org.apache.hadoop.fs.Path; @@ -54,8 +56,10 @@ import org.apache.hadoop.hbase.master.RegionState.State; import org.apache.hadoop.hbase.master.assignment.RegionStates.RegionStateNode; import org.apache.hadoop.hbase.master.normalizer.NormalizationPlan; import org.apache.hadoop.hbase.master.procedure.AbstractStateMachineRegionProcedure; +import org.apache.hadoop.hbase.master.procedure.AbstractStateMachineTableProcedure; import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv; import org.apache.hadoop.hbase.master.procedure.MasterProcedureUtil; +import org.apache.hadoop.hbase.master.procedure.TableQueue; import org.apache.hadoop.hbase.procedure2.ProcedureMetrics; import org.apache.hadoop.hbase.procedure2.ProcedureStateSerializer; import org.apache.hadoop.hbase.quotas.QuotaExceededException; @@ -503,6 +507,23 @@ public class SplitTableRegionProcedure return false; } + // A safe fence here, if there is a table procedure going on, abort the split. + // There some cases that may lead to table procedure roll back (more serious + // than roll back the split procedure here), or the split parent was brought online + // by the table procedure because of the race between split procedure and table procedure + List tableProcedures = env + .getMasterServices().getProcedures().stream() + .filter(p -> p instanceof AbstractStateMachineTableProcedure) + .map(p -> (AbstractStateMachineTableProcedure) p) + .filter(p -> p.getTableName().equals(getParentRegion().getTable()) && + !p.isFinished() && TableQueue.requireTableExclusiveLock(p)) + .collect(Collectors.toList()); + if (tableProcedures != null && tableProcedures.size() > 0) { + throw new DoNotRetryIOException(tableProcedures.get(0).toString() + + " is going on against the same table, abort the split of " + this + .toString()); + } + // set node state as SPLITTING node.setState(State.SPLITTING); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/TableQueue.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/TableQueue.java index 106dfc3..c7fa872 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/TableQueue.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/procedure/TableQueue.java @@ -23,7 +23,7 @@ import org.apache.hadoop.hbase.procedure2.Procedure; import org.apache.yetus.audience.InterfaceAudience; @InterfaceAudience.Private -class TableQueue extends Queue { +public class TableQueue extends Queue { private final LockStatus namespaceLockStatus; public TableQueue(TableName tableName, int priority, LockStatus tableLock, @@ -59,7 +59,7 @@ class TableQueue extends Queue { /** * @param proc must not be null */ - private static boolean requireTableExclusiveLock(TableProcedureInterface proc) { + public static boolean requireTableExclusiveLock(TableProcedureInterface proc) { switch (proc.getTableOperationType()) { case CREATE: case DELETE: