HBASE-21269 Forward-port HBASE-21213 [hbck2] bypass leaves behind state in RegionStates when assign/unassign

This commit is contained in:
jingyuntian 2018-10-18 17:14:32 +08:00 committed by Michael Stack
parent 132bea9a1c
commit 5fbb227deb
No known key found for this signature in database
GPG Key ID: 9816C7FC8ACC93D2
26 changed files with 426 additions and 90 deletions

View File

@ -102,11 +102,12 @@ public class HBaseHbck implements Hbck {
} }
@Override @Override
public List<Long> assigns(List<String> encodedRegionNames) throws IOException { public List<Long> assigns(List<String> encodedRegionNames, boolean override)
throws IOException {
try { try {
MasterProtos.AssignsResponse response = MasterProtos.AssignsResponse response =
this.hbck.assigns(rpcControllerFactory.newController(), this.hbck.assigns(rpcControllerFactory.newController(),
RequestConverter.toAssignRegionsRequest(encodedRegionNames)); RequestConverter.toAssignRegionsRequest(encodedRegionNames, override));
return response.getPidList(); return response.getPidList();
} catch (ServiceException se) { } catch (ServiceException se) {
LOG.debug(toCommaDelimitedString(encodedRegionNames), se); LOG.debug(toCommaDelimitedString(encodedRegionNames), se);
@ -115,11 +116,12 @@ public class HBaseHbck implements Hbck {
} }
@Override @Override
public List<Long> unassigns(List<String> encodedRegionNames) throws IOException { public List<Long> unassigns(List<String> encodedRegionNames, boolean override)
throws IOException {
try { try {
MasterProtos.UnassignsResponse response = MasterProtos.UnassignsResponse response =
this.hbck.unassigns(rpcControllerFactory.newController(), this.hbck.unassigns(rpcControllerFactory.newController(),
RequestConverter.toUnassignRegionsRequest(encodedRegionNames)); RequestConverter.toUnassignRegionsRequest(encodedRegionNames, override));
return response.getPidList(); return response.getPidList();
} catch (ServiceException se) { } catch (ServiceException se) {
LOG.debug(toCommaDelimitedString(encodedRegionNames), se); LOG.debug(toCommaDelimitedString(encodedRegionNames), se);
@ -132,7 +134,8 @@ public class HBaseHbck implements Hbck {
} }
@Override @Override
public List<Boolean> bypassProcedure(List<Long> pids, long waitTime, boolean force) public List<Boolean> bypassProcedure(List<Long> pids, long waitTime, boolean override,
boolean recursive)
throws IOException { throws IOException {
MasterProtos.BypassProcedureResponse response = ProtobufUtil.call( MasterProtos.BypassProcedureResponse response = ProtobufUtil.call(
new Callable<MasterProtos.BypassProcedureResponse>() { new Callable<MasterProtos.BypassProcedureResponse>() {
@ -141,7 +144,7 @@ public class HBaseHbck implements Hbck {
try { try {
return hbck.bypassProcedure(rpcControllerFactory.newController(), return hbck.bypassProcedure(rpcControllerFactory.newController(),
MasterProtos.BypassProcedureRequest.newBuilder().addAllProcId(pids). MasterProtos.BypassProcedureRequest.newBuilder().addAllProcId(pids).
setWaitTime(waitTime).setForce(force).build()); setWaitTime(waitTime).setOverride(override).setRecursive(recursive).build());
} catch (Throwable t) { } catch (Throwable t) {
LOG.error(pids.stream().map(i -> i.toString()). LOG.error(pids.stream().map(i -> i.toString()).
collect(Collectors.joining(", ")), t); collect(Collectors.joining(", ")), t);

View File

@ -28,11 +28,14 @@ import org.apache.yetus.audience.InterfaceAudience;
/** /**
* Hbck fixup tool APIs. Obtain an instance from {@link ClusterConnection#getHbck()} and call * Hbck fixup tool APIs. Obtain an instance from {@link ClusterConnection#getHbck()} and call
* {@link #close()} when done. * {@link #close()} when done.
* <p>WARNING: the below methods can damage the cluster. For experienced users only. * <p>WARNING: the below methods can damage the cluster. It may leave the cluster in an
* indeterminate state, e.g. region not assigned, or some hdfs files left behind. After running
* any of the below, operators may have to do some clean up on hdfs or schedule some assign
* procedures to get regions back online. DO AT YOUR OWN RISK. For experienced users only.
* *
* @see ConnectionFactory * @see ConnectionFactory
* @see ClusterConnection * @see ClusterConnection
* @since 2.2.0 * @since 2.0.2, 2.1.1
*/ */
@InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.HBCK) @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.HBCK)
public interface Hbck extends Abortable, Closeable { public interface Hbck extends Abortable, Closeable {
@ -49,22 +52,38 @@ public interface Hbck extends Abortable, Closeable {
* -- good if many Regions to online -- and it will schedule the assigns even in the case where * -- good if many Regions to online -- and it will schedule the assigns even in the case where
* Master is initializing (as long as the ProcedureExecutor is up). Does NOT call Coprocessor * Master is initializing (as long as the ProcedureExecutor is up). Does NOT call Coprocessor
* hooks. * hooks.
* @param override You need to add the override for case where a region has previously been
* bypassed. When a Procedure has been bypassed, a Procedure will have completed
* but no other Procedure will be able to make progress on the target entity
* (intentionally). This override flag will override this fencing mechanism.
* @param encodedRegionNames Region encoded names; e.g. 1588230740 is the hard-coded encoding * @param encodedRegionNames Region encoded names; e.g. 1588230740 is the hard-coded encoding
* for hbase:meta region and de00010733901a05f5a2a3a382e27dd4 is an * for hbase:meta region and de00010733901a05f5a2a3a382e27dd4 is an
* example of what a random user-space encoded Region name looks like. * example of what a random user-space encoded Region name looks like.
*/ */
List<Long> assigns(List<String> encodedRegionNames) throws IOException; List<Long> assigns(List<String> encodedRegionNames, boolean override) throws IOException;
default List<Long> assigns(List<String> encodedRegionNames) throws IOException {
return assigns(encodedRegionNames, false);
}
/** /**
* Like {@link Admin#unassign(byte[], boolean)} but 'raw' in that it can do more than one Region * Like {@link Admin#unassign(byte[], boolean)} but 'raw' in that it can do more than one Region
* at a time -- good if many Regions to offline -- and it will schedule the assigns even in the * at a time -- good if many Regions to offline -- and it will schedule the assigns even in the
* case where Master is initializing (as long as the ProcedureExecutor is up). Does NOT call * case where Master is initializing (as long as the ProcedureExecutor is up). Does NOT call
* Coprocessor hooks. * Coprocessor hooks.
* @param override You need to add the override for case where a region has previously been
* bypassed. When a Procedure has been bypassed, a Procedure will have completed
* but no other Procedure will be able to make progress on the target entity
* (intentionally). This override flag will override this fencing mechanism.
* @param encodedRegionNames Region encoded names; e.g. 1588230740 is the hard-coded encoding * @param encodedRegionNames Region encoded names; e.g. 1588230740 is the hard-coded encoding
* for hbase:meta region and de00010733901a05f5a2a3a382e27dd4 is an * for hbase:meta region and de00010733901a05f5a2a3a382e27dd4 is an
* example of what a random user-space encoded Region name looks like. * example of what a random user-space encoded Region name looks like.
*/ */
List<Long> unassigns(List<String> encodedRegionNames) throws IOException; List<Long> unassigns(List<String> encodedRegionNames, boolean override) throws IOException;
default List<Long> unassigns(List<String> encodedRegionNames) throws IOException {
return unassigns(encodedRegionNames, false);
}
/** /**
* Bypass specified procedure and move it to completion. Procedure is marked completed but * Bypass specified procedure and move it to completion. Procedure is marked completed but
@ -73,9 +92,13 @@ public interface Hbck extends Abortable, Closeable {
* *
* @param pids of procedures to complete. * @param pids of procedures to complete.
* @param waitTime wait time in ms for acquiring lock for a procedure * @param waitTime wait time in ms for acquiring lock for a procedure
* @param force if force set to true, we will bypass the procedure even if it is executing. * @param override if override set to true, we will bypass the procedure even if it is executing.
* This is for procedures which can't break out during execution (bugs?). * This is for procedures which can't break out during execution (bugs?).
* @param recursive If set, if a parent procedure, we will find and bypass children and then
* the parent procedure (Dangerous but useful in case where child procedure has been 'lost').
* Does not always work. Experimental.
* @return true if procedure is marked for bypass successfully, false otherwise * @return true if procedure is marked for bypass successfully, false otherwise
*/ */
List<Boolean> bypassProcedure(List<Long> pids, long waitTime, boolean force) throws IOException; List<Boolean> bypassProcedure(List<Long> pids, long waitTime, boolean override, boolean recursive)
throws IOException;
} }

View File

@ -1892,16 +1892,18 @@ public final class RequestConverter {
// HBCK2 // HBCK2
public static MasterProtos.AssignsRequest toAssignRegionsRequest( public static MasterProtos.AssignsRequest toAssignRegionsRequest(
List<String> encodedRegionNames) { List<String> encodedRegionNames, boolean override) {
MasterProtos.AssignsRequest.Builder b = MasterProtos.AssignsRequest.newBuilder(); MasterProtos.AssignsRequest.Builder b = MasterProtos.AssignsRequest.newBuilder();
return b.addAllRegion(toEncodedRegionNameRegionSpecifiers(encodedRegionNames)).build(); return b.addAllRegion(toEncodedRegionNameRegionSpecifiers(encodedRegionNames)).
setOverride(override).build();
} }
public static MasterProtos.UnassignsRequest toUnassignRegionsRequest( public static MasterProtos.UnassignsRequest toUnassignRegionsRequest(
List<String> encodedRegionNames) { List<String> encodedRegionNames, boolean override) {
MasterProtos.UnassignsRequest.Builder b = MasterProtos.UnassignsRequest.Builder b =
MasterProtos.UnassignsRequest.newBuilder(); MasterProtos.UnassignsRequest.newBuilder();
return b.addAllRegion(toEncodedRegionNameRegionSpecifiers(encodedRegionNames)).build(); return b.addAllRegion(toEncodedRegionNameRegionSpecifiers(encodedRegionNames)).
setOverride(override).build();
} }
private static List<RegionSpecifier> toEncodedRegionNameRegionSpecifiers( private static List<RegionSpecifier> toEncodedRegionNameRegionSpecifiers(

View File

@ -145,12 +145,16 @@ public abstract class Procedure<TEnvironment> implements Comparable<Procedure<TE
private boolean lockedWhenLoading = false; private boolean lockedWhenLoading = false;
/** /**
* Used for force complete of the procedure without * Used for override complete of the procedure without actually doing any logic in the procedure.
* actually doing any logic in the procedure.
* If bypass is set to true, when executing it will return null when * If bypass is set to true, when executing it will return null when
* {@link #doExecute(Object)} to finish the procedure and releasing any locks * {@link #doExecute(Object)} is called to finish the procedure and release any locks
* it may currently hold. * it may currently hold. The bypass does cleanup around the Procedure as far as the
* Bypassing a procedure is not like aborting. Aborting a procedure will trigger * Procedure framework is concerned. It does not clean any internal state that the
* Procedure's themselves may have set. That is for the Procedures to do themselves
* when bypass is called. They should override bypass and do their cleanup in the
* overridden bypass method (be sure to call the parent bypass to ensure proper
* processing).
* <p></p>Bypassing a procedure is not like aborting. Aborting a procedure will trigger
* a rollback. And since the {@link #abort(Object)} method is overrideable * a rollback. And since the {@link #abort(Object)} method is overrideable
* Some procedures may have chosen to ignore the aborting. * Some procedures may have chosen to ignore the aborting.
*/ */
@ -175,12 +179,15 @@ public abstract class Procedure<TEnvironment> implements Comparable<Procedure<TE
} }
/** /**
* set the bypass to true * Set the bypass to true.
* Only called in {@link ProcedureExecutor#bypassProcedure(long, long, boolean)} for now, * Only called in {@link ProcedureExecutor#bypassProcedure(long, long, boolean, boolean)} for now.
* DO NOT use this method alone, since we can't just bypass * DO NOT use this method alone, since we can't just bypass one single procedure. We need to
* one single procedure. We need to bypass its ancestor too. So making it package private * bypass its ancestor too. If your Procedure has set state, it needs to undo it in here.
* @param env Current environment. May be null because of context; e.g. pretty-printing
* procedure WALs where there is no 'environment' (and where Procedures that require
* an 'environment' won't be run.
*/ */
void bypass() { protected void bypass(TEnvironment env) {
this.bypass = true; this.bypass = true;
} }
@ -704,7 +711,7 @@ public abstract class Procedure<TEnvironment> implements Comparable<Procedure<TE
/** /**
* Will only be called when loading procedures from procedure store, where we need to record * Will only be called when loading procedures from procedure store, where we need to record
* whether the procedure has already held a lock. Later we will call * whether the procedure has already held a lock. Later we will call
* {@link #doAcquireLock(Object)} to actually acquire the lock. * {@link #doAcquireLock(Object, ProcedureStore)} to actually acquire the lock.
*/ */
final void lockedWhenLoading() { final void lockedWhenLoading() {
this.lockedWhenLoading = true; this.lockedWhenLoading = true;

View File

@ -318,7 +318,7 @@ public class ProcedureExecutor<TEnvironment> {
private Configuration conf; private Configuration conf;
/** /**
* Created in the {@link #start(int, boolean)} method. Destroyed in {@link #join()} (FIX! Doing * Created in the {@link #init(int, boolean)} method. Destroyed in {@link #join()} (FIX! Doing
* resource handling rather than observing in a #join is unexpected). * resource handling rather than observing in a #join is unexpected).
* Overridden when we do the ProcedureTestingUtility.testRecoveryAndDoubleExecution trickery * Overridden when we do the ProcedureTestingUtility.testRecoveryAndDoubleExecution trickery
* (Should be ok). * (Should be ok).
@ -326,7 +326,7 @@ public class ProcedureExecutor<TEnvironment> {
private ThreadGroup threadGroup; private ThreadGroup threadGroup;
/** /**
* Created in the {@link #start(int, boolean)} method. Terminated in {@link #join()} (FIX! Doing * Created in the {@link #init(int, boolean)} method. Terminated in {@link #join()} (FIX! Doing
* resource handling rather than observing in a #join is unexpected). * resource handling rather than observing in a #join is unexpected).
* Overridden when we do the ProcedureTestingUtility.testRecoveryAndDoubleExecution trickery * Overridden when we do the ProcedureTestingUtility.testRecoveryAndDoubleExecution trickery
* (Should be ok). * (Should be ok).
@ -334,7 +334,7 @@ public class ProcedureExecutor<TEnvironment> {
private CopyOnWriteArrayList<WorkerThread> workerThreads; private CopyOnWriteArrayList<WorkerThread> workerThreads;
/** /**
* Created in the {@link #start(int, boolean)} method. Terminated in {@link #join()} (FIX! Doing * Created in the {@link #init(int, boolean)} method. Terminated in {@link #join()} (FIX! Doing
* resource handling rather than observing in a #join is unexpected). * resource handling rather than observing in a #join is unexpected).
* Overridden when we do the ProcedureTestingUtility.testRecoveryAndDoubleExecution trickery * Overridden when we do the ProcedureTestingUtility.testRecoveryAndDoubleExecution trickery
* (Should be ok). * (Should be ok).
@ -1021,7 +1021,7 @@ public class ProcedureExecutor<TEnvironment> {
* Bypass a procedure. If the procedure is set to bypass, all the logic in * Bypass a procedure. If the procedure is set to bypass, all the logic in
* execute/rollback will be ignored and it will return success, whatever. * execute/rollback will be ignored and it will return success, whatever.
* It is used to recover buggy stuck procedures, releasing the lock resources * It is used to recover buggy stuck procedures, releasing the lock resources
* and letting other procedures to run. Bypassing one procedure (and its ancestors will * and letting other procedures run. Bypassing one procedure (and its ancestors will
* be bypassed automatically) may leave the cluster in a middle state, e.g. region * be bypassed automatically) may leave the cluster in a middle state, e.g. region
* not assigned, or some hdfs files left behind. After getting rid of those stuck procedures, * not assigned, or some hdfs files left behind. After getting rid of those stuck procedures,
* the operators may have to do some clean up on hdfs or schedule some assign procedures * the operators may have to do some clean up on hdfs or schedule some assign procedures
@ -1048,35 +1048,39 @@ public class ProcedureExecutor<TEnvironment> {
* there. We need to restart the master after bypassing, and letting the problematic * there. We need to restart the master after bypassing, and letting the problematic
* procedure to execute wth bypass=true, so in that condition, the procedure can be * procedure to execute wth bypass=true, so in that condition, the procedure can be
* successfully bypassed. * successfully bypassed.
* @param recursive We will do an expensive search for children of each pid. EXPENSIVE!
* @return true if bypass success * @return true if bypass success
* @throws IOException IOException * @throws IOException IOException
*/ */
public List<Boolean> bypassProcedure(List<Long> pids, long lockWait, boolean force) public List<Boolean> bypassProcedure(List<Long> pids, long lockWait, boolean force,
boolean recursive)
throws IOException { throws IOException {
List<Boolean> result = new ArrayList<Boolean>(pids.size()); List<Boolean> result = new ArrayList<Boolean>(pids.size());
for(long pid: pids) { for(long pid: pids) {
result.add(bypassProcedure(pid, lockWait, force)); result.add(bypassProcedure(pid, lockWait, force, recursive));
} }
return result; return result;
} }
boolean bypassProcedure(long pid, long lockWait, boolean force) throws IOException { boolean bypassProcedure(long pid, long lockWait, boolean override, boolean recursive)
throws IOException {
Preconditions.checkArgument(lockWait > 0, "lockWait should be positive"); Preconditions.checkArgument(lockWait > 0, "lockWait should be positive");
Procedure<TEnvironment> procedure = getProcedure(pid); final Procedure<TEnvironment> procedure = getProcedure(pid);
if (procedure == null) { if (procedure == null) {
LOG.debug("Procedure with id={} does not exist, skipping bypass", pid); LOG.debug("Procedure pid={} does not exist, skipping bypass", pid);
return false; return false;
} }
LOG.debug("Begin bypass {} with lockWait={}, force={}", procedure, lockWait, force); LOG.debug("Begin bypass {} with lockWait={}, override={}, recursive={}",
procedure, lockWait, override, recursive);
IdLock.Entry lockEntry = procExecutionLock.tryLockEntry(procedure.getProcId(), lockWait); IdLock.Entry lockEntry = procExecutionLock.tryLockEntry(procedure.getProcId(), lockWait);
if (lockEntry == null && !force) { if (lockEntry == null && !override) {
LOG.debug("Waited {} ms, but {} is still running, skipping bypass with force={}", LOG.debug("Waited {} ms, but {} is still running, skipping bypass with force={}",
lockWait, procedure, force); lockWait, procedure, override);
return false; return false;
} else if (lockEntry == null) { } else if (lockEntry == null) {
LOG.debug("Waited {} ms, but {} is still running, begin bypass with force={}", LOG.debug("Waited {} ms, but {} is still running, begin bypass with force={}",
lockWait, procedure, force); lockWait, procedure, override);
} }
try { try {
// check whether the procedure is already finished // check whether the procedure is already finished
@ -1086,8 +1090,25 @@ public class ProcedureExecutor<TEnvironment> {
} }
if (procedure.hasChildren()) { if (procedure.hasChildren()) {
LOG.debug("{} has children, skipping bypass", procedure); if (recursive) {
return false; // EXPENSIVE. Checks each live procedure of which there could be many!!!
// Is there another way to get children of a procedure?
LOG.info("Recursive bypass on children of pid={}", procedure.getProcId());
this.procedures.forEachValue(1 /*Single-threaded*/,
// Transformer
v -> v.getParentProcId() == procedure.getProcId()? v: null,
// Consumer
v -> {
try {
bypassProcedure(v.getProcId(), lockWait, override, recursive);
} catch (IOException e) {
LOG.warn("Recursive bypass of pid={}", v.getProcId(), e);
}
});
} else {
LOG.debug("{} has children, skipping bypass", procedure);
return false;
}
} }
// If the procedure has no parent or no child, we are safe to bypass it in whatever state // If the procedure has no parent or no child, we are safe to bypass it in whatever state
@ -1097,6 +1118,7 @@ public class ProcedureExecutor<TEnvironment> {
LOG.debug("Bypassing procedures in RUNNABLE, WAITING and WAITING_TIMEOUT states " LOG.debug("Bypassing procedures in RUNNABLE, WAITING and WAITING_TIMEOUT states "
+ "(with no parent), {}", + "(with no parent), {}",
procedure); procedure);
// Question: how is the bypass done here?
return false; return false;
} }
@ -1106,7 +1128,7 @@ public class ProcedureExecutor<TEnvironment> {
Procedure<TEnvironment> current = procedure; Procedure<TEnvironment> current = procedure;
while (current != null) { while (current != null) {
LOG.debug("Bypassing {}", current); LOG.debug("Bypassing {}", current);
current.bypass(); current.bypass(getEnvironment());
store.update(procedure); store.update(procedure);
long parentID = current.getParentProcId(); long parentID = current.getParentProcId();
current = getProcedure(parentID); current = getProcedure(parentID);

View File

@ -272,7 +272,7 @@ public final class ProcedureUtil {
} }
if (proto.getBypass()) { if (proto.getBypass()) {
proc.bypass(); proc.bypass(null);
} }
ProcedureStateSerializer serializer = null; ProcedureStateSerializer serializer = null;

View File

@ -74,6 +74,10 @@ public class RemoteProcedureException extends ProcedureException {
return new Exception(cause); return new Exception(cause);
} }
// NOTE: Does not throw DoNotRetryIOE because it does not
// have access (DNRIOE is in the client module). Use
// MasterProcedureUtil.unwrapRemoteIOException if need to
// throw DNRIOE.
public IOException unwrapRemoteIOException() { public IOException unwrapRemoteIOException() {
final Exception cause = unwrapRemoteException(); final Exception cause = unwrapRemoteException();
if (cause instanceof IOException) { if (cause instanceof IOException) {

View File

@ -89,7 +89,7 @@ public class TestProcedureBypass {
long id = procExecutor.submitProcedure(proc); long id = procExecutor.submitProcedure(proc);
Thread.sleep(500); Thread.sleep(500);
//bypass the procedure //bypass the procedure
assertTrue(procExecutor.bypassProcedure(id, 30000, false)); assertTrue(procExecutor.bypassProcedure(id, 30000, false, false));
htu.waitFor(5000, () -> proc.isSuccess() && proc.isBypass()); htu.waitFor(5000, () -> proc.isSuccess() && proc.isBypass());
LOG.info("{} finished", proc); LOG.info("{} finished", proc);
} }
@ -100,7 +100,7 @@ public class TestProcedureBypass {
long id = procExecutor.submitProcedure(proc); long id = procExecutor.submitProcedure(proc);
Thread.sleep(500); Thread.sleep(500);
//bypass the procedure //bypass the procedure
assertTrue(procExecutor.bypassProcedure(id, 1000, true)); assertTrue(procExecutor.bypassProcedure(id, 1000, true, false));
//Since the procedure is stuck there, we need to restart the executor to recovery. //Since the procedure is stuck there, we need to restart the executor to recovery.
ProcedureTestingUtility.restart(procExecutor); ProcedureTestingUtility.restart(procExecutor);
htu.waitFor(5000, () -> proc.isSuccess() && proc.isBypass()); htu.waitFor(5000, () -> proc.isSuccess() && proc.isBypass());
@ -116,7 +116,7 @@ public class TestProcedureBypass {
.size() > 0); .size() > 0);
SuspendProcedure suspendProcedure = (SuspendProcedure)procExecutor.getProcedures().stream() SuspendProcedure suspendProcedure = (SuspendProcedure)procExecutor.getProcedures().stream()
.filter(p -> p.getParentProcId() == rootId).collect(Collectors.toList()).get(0); .filter(p -> p.getParentProcId() == rootId).collect(Collectors.toList()).get(0);
assertTrue(procExecutor.bypassProcedure(suspendProcedure.getProcId(), 1000, false)); assertTrue(procExecutor.bypassProcedure(suspendProcedure.getProcId(), 1000, false, false));
htu.waitFor(5000, () -> proc.isSuccess() && proc.isBypass()); htu.waitFor(5000, () -> proc.isSuccess() && proc.isBypass());
LOG.info("{} finished", proc); LOG.info("{} finished", proc);
} }
@ -128,14 +128,26 @@ public class TestProcedureBypass {
long id = procExecutor.submitProcedure(proc); long id = procExecutor.submitProcedure(proc);
Thread.sleep(500); Thread.sleep(500);
// bypass the procedure // bypass the procedure
assertFalse(procExecutor.bypassProcedure(id, 1000, false)); assertFalse(procExecutor.bypassProcedure(id, 1000, false, false));
assertTrue(procExecutor.bypassProcedure(id, 1000, true)); assertTrue(procExecutor.bypassProcedure(id, 1000, true, false));
htu.waitFor(5000, () -> proc.isSuccess() && proc.isBypass()); htu.waitFor(5000, () -> proc.isSuccess() && proc.isBypass());
LOG.info("{} finished", proc); LOG.info("{} finished", proc);
} }
@Test
public void testBypassingProcedureWithParentRecursive() throws Exception {
final RootProcedure proc = new RootProcedure();
long rootId = procExecutor.submitProcedure(proc);
htu.waitFor(5000, () -> procExecutor.getProcedures().stream()
.filter(p -> p.getParentProcId() == rootId).collect(Collectors.toList())
.size() > 0);
SuspendProcedure suspendProcedure = (SuspendProcedure)procExecutor.getProcedures().stream()
.filter(p -> p.getParentProcId() == rootId).collect(Collectors.toList()).get(0);
assertTrue(procExecutor.bypassProcedure(rootId, 1000, false, true));
htu.waitFor(5000, () -> proc.isSuccess() && proc.isBypass());
LOG.info("{} finished", proc);
}
@AfterClass @AfterClass
public static void tearDown() throws Exception { public static void tearDown() throws Exception {

View File

@ -99,6 +99,7 @@ message MergeTableRegionsResponse {
message AssignRegionRequest { message AssignRegionRequest {
required RegionSpecifier region = 1; required RegionSpecifier region = 1;
optional bool override = 2 [default = false];
} }
message AssignRegionResponse { message AssignRegionResponse {
@ -1009,6 +1010,7 @@ message SetTableStateInMetaRequest {
// Region at a time. // Region at a time.
message AssignsRequest { message AssignsRequest {
repeated RegionSpecifier region = 1; repeated RegionSpecifier region = 1;
optional bool override = 2 [default = false];
} }
/** Like Admin's AssignRegionResponse except it can /** Like Admin's AssignRegionResponse except it can
@ -1023,6 +1025,7 @@ message AssignsResponse {
*/ */
message UnassignsRequest { message UnassignsRequest {
repeated RegionSpecifier region = 1; repeated RegionSpecifier region = 1;
optional bool override = 2 [default = false];
} }
/** Like Admin's UnassignRegionResponse except it can /** Like Admin's UnassignRegionResponse except it can
@ -1035,7 +1038,8 @@ message UnassignsResponse {
message BypassProcedureRequest { message BypassProcedureRequest {
repeated uint64 proc_id = 1; repeated uint64 proc_id = 1;
optional uint64 waitTime = 2; // wait time in ms to acquire lock on a procedure optional uint64 waitTime = 2; // wait time in ms to acquire lock on a procedure
optional bool force = 3; // if true, procedure is marked for bypass even if its executing optional bool override = 3 [default = false]; // if true, procedure is marked for bypass even if its executing
optional bool recursive = 4;
} }
message BypassProcedureResponse { message BypassProcedureResponse {

View File

@ -533,6 +533,7 @@ message RegionStateTransitionStateData {
required RegionStateTransitionState lastState = 2; required RegionStateTransitionState lastState = 2;
optional ServerName assign_candidate = 3; optional ServerName assign_candidate = 3;
required bool force_new_plan = 4; required bool force_new_plan = 4;
optional bool override = 5 [default = false];
} }
message RegionRemoteProcedureBaseStateData { message RegionRemoteProcedureBaseStateData {

View File

@ -65,6 +65,7 @@ import org.apache.hadoop.hbase.master.MasterServices;
import org.apache.hadoop.hbase.master.ServerListener; import org.apache.hadoop.hbase.master.ServerListener;
import org.apache.hadoop.hbase.master.TableStateManager; import org.apache.hadoop.hbase.master.TableStateManager;
import org.apache.hadoop.hbase.master.assignment.RegionStateNode; import org.apache.hadoop.hbase.master.assignment.RegionStateNode;
import org.apache.hadoop.hbase.master.procedure.MasterProcedureUtil;
import org.apache.hadoop.hbase.net.Address; import org.apache.hadoop.hbase.net.Address;
import org.apache.hadoop.hbase.procedure2.Procedure; import org.apache.hadoop.hbase.procedure2.Procedure;
import org.apache.hadoop.hbase.protobuf.ProtobufMagic; import org.apache.hadoop.hbase.protobuf.ProtobufMagic;
@ -874,7 +875,7 @@ final class RSGroupInfoManagerImpl implements RSGroupInfoManager {
Procedure<?> result = masterServices.getMasterProcedureExecutor().getResult(procId); Procedure<?> result = masterServices.getMasterProcedureExecutor().getResult(procId);
if (result != null && result.isFailed()) { if (result != null && result.isFailed()) {
throw new IOException("Failed to create group table. " + throw new IOException("Failed to create group table. " +
result.getException().unwrapRemoteIOException()); MasterProcedureUtil.unwrapRemoteIOException(result));
} }
} }
} }

View File

@ -1060,7 +1060,7 @@ public class HMaster extends HRegionServer implements MasterServices {
// as procedures run -- in particular SCPs for crashed servers... One should put up hbase:meta // as procedures run -- in particular SCPs for crashed servers... One should put up hbase:meta
// if it is down. It may take a while to come online. So, wait here until meta if for sure // if it is down. It may take a while to come online. So, wait here until meta if for sure
// available. Thats what waitUntilMetaOnline does. // available. Thats what waitUntilMetaOnline does.
if (!waitUntilMetaOnline()) { if (!waitForMetaOnline()) {
return; return;
} }
this.assignmentManager.joinCluster(); this.assignmentManager.joinCluster();
@ -1093,7 +1093,7 @@ public class HMaster extends HRegionServer implements MasterServices {
// Here we expect hbase:namespace to be online. See inside initClusterSchemaService. // Here we expect hbase:namespace to be online. See inside initClusterSchemaService.
// TODO: Fix this. Namespace is a pain being a sort-of system table. Fold it in to hbase:meta. // TODO: Fix this. Namespace is a pain being a sort-of system table. Fold it in to hbase:meta.
// isNamespace does like isMeta and waits until namespace is onlined before allowing progress. // isNamespace does like isMeta and waits until namespace is onlined before allowing progress.
if (!waitUntilNamespaceOnline()) { if (!waitForNamespaceOnline()) {
return; return;
} }
status.setStatus("Starting cluster schema service"); status.setStatus("Starting cluster schema service");
@ -1177,7 +1177,7 @@ public class HMaster extends HRegionServer implements MasterServices {
* and we will hold here until operator intervention. * and we will hold here until operator intervention.
*/ */
@VisibleForTesting @VisibleForTesting
public boolean waitUntilMetaOnline() throws InterruptedException { public boolean waitForMetaOnline() throws InterruptedException {
return isRegionOnline(RegionInfoBuilder.FIRST_META_REGIONINFO); return isRegionOnline(RegionInfoBuilder.FIRST_META_REGIONINFO);
} }
@ -1218,7 +1218,7 @@ public class HMaster extends HRegionServer implements MasterServices {
* @return True if namespace table is up/online. * @return True if namespace table is up/online.
*/ */
@VisibleForTesting @VisibleForTesting
public boolean waitUntilNamespaceOnline() throws InterruptedException { public boolean waitForNamespaceOnline() throws InterruptedException {
List<RegionInfo> ris = this.assignmentManager.getRegionStates(). List<RegionInfo> ris = this.assignmentManager.getRegionStates().
getRegionsOfTable(TableName.NAMESPACE_TABLE_NAME); getRegionsOfTable(TableName.NAMESPACE_TABLE_NAME);
if (ris.isEmpty()) { if (ris.isEmpty()) {

View File

@ -29,7 +29,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.ClusterMetricsBuilder; import org.apache.hadoop.hbase.ClusterMetricsBuilder;
@ -44,7 +43,6 @@ import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.UnknownRegionException; import org.apache.hadoop.hbase.UnknownRegionException;
import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor; import org.apache.hadoop.hbase.client.ColumnFamilyDescriptor;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.MasterSwitchType; import org.apache.hadoop.hbase.client.MasterSwitchType;
import org.apache.hadoop.hbase.client.RegionInfo; import org.apache.hadoop.hbase.client.RegionInfo;
import org.apache.hadoop.hbase.client.RegionInfoBuilder; import org.apache.hadoop.hbase.client.RegionInfoBuilder;
@ -599,7 +597,7 @@ public class MasterRpcServices extends RSRpcServices
BalanceRequest request) throws ServiceException { BalanceRequest request) throws ServiceException {
try { try {
return BalanceResponse.newBuilder().setBalancerRan(master.balance( return BalanceResponse.newBuilder().setBalancerRan(master.balance(
request.hasForce() ? request.getForce() : false)).build(); request.hasForce()? request.getForce(): false)).build();
} catch (IOException ex) { } catch (IOException ex) {
throw new ServiceException(ex); throw new ServiceException(ex);
} }
@ -1185,7 +1183,8 @@ public class MasterRpcServices extends RSRpcServices
if (executor.isFinished(procId)) { if (executor.isFinished(procId)) {
builder.setState(GetProcedureResultResponse.State.FINISHED); builder.setState(GetProcedureResultResponse.State.FINISHED);
if (result.isFailed()) { if (result.isFailed()) {
IOException exception = result.getException().unwrapRemoteIOException(); IOException exception =
MasterProcedureUtil.unwrapRemoteIOException(result);
builder.setException(ForeignExceptionUtil.toProtoForeignException(exception)); builder.setException(ForeignExceptionUtil.toProtoForeignException(exception));
} }
byte[] resultData = result.getResult(); byte[] resultData = result.getResult();
@ -2380,15 +2379,16 @@ public class MasterRpcServices extends RSRpcServices
MasterProtos.AssignsResponse.Builder responseBuilder = MasterProtos.AssignsResponse.Builder responseBuilder =
MasterProtos.AssignsResponse.newBuilder(); MasterProtos.AssignsResponse.newBuilder();
try { try {
boolean override = request.getOverride();
for (HBaseProtos.RegionSpecifier rs: request.getRegionList()) { for (HBaseProtos.RegionSpecifier rs: request.getRegionList()) {
// Assign is synchronous as of hbase-2.2. Need an asynchronous one.
RegionInfo ri = getRegionInfo(rs); RegionInfo ri = getRegionInfo(rs);
if (ri == null) { if (ri == null) {
LOG.info("Unknown={}", rs); LOG.info("Unknown={}", rs);
responseBuilder.addPid(Procedure.NO_PROC_ID); responseBuilder.addPid(Procedure.NO_PROC_ID);
continue; continue;
} }
responseBuilder.addPid(this.master.getAssignmentManager().assign(ri)); responseBuilder.addPid(this.master.getMasterProcedureExecutor().submitProcedure(this.master
.getAssignmentManager().createOneAssignProcedure(ri, override)));
} }
return responseBuilder.build(); return responseBuilder.build();
} catch (IOException ioe) { } catch (IOException ioe) {
@ -2411,15 +2411,16 @@ public class MasterRpcServices extends RSRpcServices
MasterProtos.UnassignsResponse.Builder responseBuilder = MasterProtos.UnassignsResponse.Builder responseBuilder =
MasterProtos.UnassignsResponse.newBuilder(); MasterProtos.UnassignsResponse.newBuilder();
try { try {
boolean override = request.getOverride();
for (HBaseProtos.RegionSpecifier rs: request.getRegionList()) { for (HBaseProtos.RegionSpecifier rs: request.getRegionList()) {
// Unassign is synchronous as of hbase-2.2. Need an asynchronous one.
RegionInfo ri = getRegionInfo(rs); RegionInfo ri = getRegionInfo(rs);
if (ri == null) { if (ri == null) {
LOG.info("Unknown={}", rs); LOG.info("Unknown={}", rs);
responseBuilder.addPid(Procedure.NO_PROC_ID); responseBuilder.addPid(Procedure.NO_PROC_ID);
continue; continue;
} }
responseBuilder.addPid(this.master.getAssignmentManager().unassign(ri)); responseBuilder.addPid(this.master.getMasterProcedureExecutor().submitProcedure(this.master
.getAssignmentManager().createOneUnassignProcedure(ri, override)));
} }
return responseBuilder.build(); return responseBuilder.build();
} catch (IOException ioe) { } catch (IOException ioe) {
@ -2444,7 +2445,7 @@ public class MasterRpcServices extends RSRpcServices
try { try {
List<Boolean> ret = List<Boolean> ret =
master.getMasterProcedureExecutor().bypassProcedure(request.getProcIdList(), master.getMasterProcedureExecutor().bypassProcedure(request.getProcIdList(),
request.getWaitTime(), request.getForce()); request.getWaitTime(), request.getOverride(), request.getRecursive());
return MasterProtos.BypassProcedureResponse.newBuilder().addAllBypassed(ret).build(); return MasterProtos.BypassProcedureResponse.newBuilder().addAllBypassed(ret).build();
} catch (IOException e) { } catch (IOException e) {
throw new ServiceException(e); throw new ServiceException(e);

View File

@ -682,10 +682,13 @@ public class AssignmentManager implements ServerListener {
} }
private TransitRegionStateProcedure createAssignProcedure(RegionStateNode regionNode, private TransitRegionStateProcedure createAssignProcedure(RegionStateNode regionNode,
ServerName targetServer) { ServerName targetServer, boolean override) {
TransitRegionStateProcedure proc; TransitRegionStateProcedure proc;
regionNode.lock(); regionNode.lock();
try { try {
if(override && regionNode.getProcedure() != null) {
regionNode.unsetProcedure(regionNode.getProcedure());
}
assert regionNode.getProcedure() == null; assert regionNode.getProcedure() == null;
proc = TransitRegionStateProcedure.assign(getProcedureEnvironment(), proc = TransitRegionStateProcedure.assign(getProcedureEnvironment(),
regionNode.getRegionInfo(), targetServer); regionNode.getRegionInfo(), targetServer);
@ -696,6 +699,42 @@ public class AssignmentManager implements ServerListener {
return proc; return proc;
} }
private TransitRegionStateProcedure createUnassignProcedure(RegionStateNode regionNode,
boolean override) {
TransitRegionStateProcedure proc;
regionNode.lock();
try {
if(override && regionNode.getProcedure() != null) {
regionNode.unsetProcedure(regionNode.getProcedure());
}
assert regionNode.getProcedure() == null;
proc = TransitRegionStateProcedure.unassign(getProcedureEnvironment(),
regionNode.getRegionInfo());
regionNode.setProcedure(proc);
} finally {
regionNode.unlock();
}
return proc;
}
/**
* Create one TransitRegionStateProcedure to assign a region w/o specifying a target server.
* This method is specified for HBCK2
*/
public TransitRegionStateProcedure createOneAssignProcedure(RegionInfo hri, boolean override) {
RegionStateNode regionNode = regionStates.getOrCreateRegionStateNode(hri);
return createAssignProcedure(regionNode, null, override);
}
/**
* Create one TransitRegionStateProcedure to unassign a region.
* This method is specified for HBCK2
*/
public TransitRegionStateProcedure createOneUnassignProcedure(RegionInfo hri, boolean override) {
RegionStateNode regionNode = regionStates.getOrCreateRegionStateNode(hri);
return createUnassignProcedure(regionNode, override);
}
/** /**
* Create an array of TransitRegionStateProcedure w/o specifying a target server. * Create an array of TransitRegionStateProcedure w/o specifying a target server.
* <p/> * <p/>
@ -707,8 +746,8 @@ public class AssignmentManager implements ServerListener {
*/ */
public TransitRegionStateProcedure[] createAssignProcedures(List<RegionInfo> hris) { public TransitRegionStateProcedure[] createAssignProcedures(List<RegionInfo> hris) {
return hris.stream().map(hri -> regionStates.getOrCreateRegionStateNode(hri)) return hris.stream().map(hri -> regionStates.getOrCreateRegionStateNode(hri))
.map(regionNode -> createAssignProcedure(regionNode, null)).sorted(AssignmentManager::compare) .map(regionNode -> createAssignProcedure(regionNode, null, false))
.toArray(TransitRegionStateProcedure[]::new); .sorted(AssignmentManager::compare).toArray(TransitRegionStateProcedure[]::new);
} }
/** /**
@ -719,7 +758,7 @@ public class AssignmentManager implements ServerListener {
Map<ServerName, List<RegionInfo>> assignments) { Map<ServerName, List<RegionInfo>> assignments) {
return assignments.entrySet().stream() return assignments.entrySet().stream()
.flatMap(e -> e.getValue().stream().map(hri -> regionStates.getOrCreateRegionStateNode(hri)) .flatMap(e -> e.getValue().stream().map(hri -> regionStates.getOrCreateRegionStateNode(hri))
.map(regionNode -> createAssignProcedure(regionNode, e.getKey()))) .map(regionNode -> createAssignProcedure(regionNode, e.getKey(), false)))
.sorted(AssignmentManager::compare).toArray(TransitRegionStateProcedure[]::new); .sorted(AssignmentManager::compare).toArray(TransitRegionStateProcedure[]::new);
} }

View File

@ -39,6 +39,7 @@ import org.apache.yetus.audience.InterfaceAudience;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting;
import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil;
import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.RegionStateTransitionState; import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.RegionStateTransitionState;
import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.RegionStateTransitionStateData; import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.RegionStateTransitionStateData;
@ -114,7 +115,8 @@ public class TransitRegionStateProcedure
public TransitRegionStateProcedure() { public TransitRegionStateProcedure() {
} }
private TransitRegionStateProcedure(MasterProcedureEnv env, RegionInfo hri, @VisibleForTesting
protected TransitRegionStateProcedure(MasterProcedureEnv env, RegionInfo hri,
ServerName assignCandidate, boolean forceNewPlan, RegionStateTransitionState initialState, ServerName assignCandidate, boolean forceNewPlan, RegionStateTransitionState initialState,
RegionStateTransitionState lastState) { RegionStateTransitionState lastState) {
super(env, hri); super(env, hri);
@ -476,8 +478,9 @@ public class TransitRegionStateProcedure
@Override @Override
protected void serializeStateData(ProcedureStateSerializer serializer) throws IOException { protected void serializeStateData(ProcedureStateSerializer serializer) throws IOException {
super.serializeStateData(serializer); super.serializeStateData(serializer);
RegionStateTransitionStateData.Builder builder = RegionStateTransitionStateData.newBuilder() RegionStateTransitionStateData.Builder builder =
.setInitialState(initialState).setLastState(lastState).setForceNewPlan(forceNewPlan); RegionStateTransitionStateData.newBuilder().setInitialState(initialState)
.setLastState(lastState).setForceNewPlan(forceNewPlan);
if (assignCandidate != null) { if (assignCandidate != null) {
builder.setAssignCandidate(ProtobufUtil.toServerName(assignCandidate)); builder.setAssignCandidate(ProtobufUtil.toServerName(assignCandidate));
} }

View File

@ -51,6 +51,7 @@ import org.apache.hadoop.hbase.master.LoadBalancer;
import org.apache.hadoop.hbase.master.MasterServices; import org.apache.hadoop.hbase.master.MasterServices;
import org.apache.hadoop.hbase.master.RackManager; import org.apache.hadoop.hbase.master.RackManager;
import org.apache.hadoop.hbase.master.RegionPlan; import org.apache.hadoop.hbase.master.RegionPlan;
import org.apache.hadoop.hbase.master.assignment.AssignmentManager;
import org.apache.hadoop.hbase.master.assignment.RegionStates; import org.apache.hadoop.hbase.master.assignment.RegionStates;
import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster.Action.Type; import org.apache.hadoop.hbase.master.balancer.BaseLoadBalancer.Cluster.Action.Type;
import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting; import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting;
@ -1457,11 +1458,13 @@ public abstract class BaseLoadBalancer implements LoadBalancer {
ServerName oldServerName = entry.getValue(); ServerName oldServerName = entry.getValue();
// In the current set of regions even if one has region replica let us go with // In the current set of regions even if one has region replica let us go with
// getting the entire snapshot // getting the entire snapshot
if (this.services != null && this.services.getAssignmentManager() != null) { // for tests if (this.services != null) { // for tests
RegionStates states = this.services.getAssignmentManager().getRegionStates(); AssignmentManager am = this.services.getAssignmentManager();
if (!hasRegionReplica && states != null && if (am != null) {
states.isReplicaAvailableForRegion(region)) { RegionStates states = am.getRegionStates();
hasRegionReplica = true; if (!hasRegionReplica && states != null && states.isReplicaAvailableForRegion(region)) {
hasRegionReplica = true;
}
} }
} }
List<ServerName> localServers = new ArrayList<>(); List<ServerName> localServers = new ArrayList<>();

View File

@ -19,9 +19,12 @@ package org.apache.hadoop.hbase.master.procedure;
import java.io.IOException; import java.io.IOException;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.apache.hadoop.hbase.DoNotRetryIOException;
import org.apache.hadoop.hbase.TableName; import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.master.MasterServices; import org.apache.hadoop.hbase.master.MasterServices;
import org.apache.hadoop.hbase.procedure2.Procedure; import org.apache.hadoop.hbase.procedure2.Procedure;
import org.apache.hadoop.hbase.procedure2.ProcedureException;
import org.apache.hadoop.hbase.procedure2.ProcedureExecutor; import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
import org.apache.hadoop.hbase.security.User; import org.apache.hadoop.hbase.security.User;
import org.apache.hadoop.hbase.util.NonceKey; import org.apache.hadoop.hbase.util.NonceKey;
@ -176,4 +179,17 @@ public final class MasterProcedureUtil {
public static int getServerPriority(ServerProcedureInterface proc) { public static int getServerPriority(ServerProcedureInterface proc) {
return proc.hasMetaTableRegion() ? 100 : 1; return proc.hasMetaTableRegion() ? 100 : 1;
} }
/**
* This is a version of unwrapRemoteIOException that can do DoNotRetryIOE.
* We need to throw DNRIOE to clients if a failed Procedure else they will
* keep trying. The default proc.getException().unwrapRemoteException
* doesn't have access to DNRIOE from the procedure2 module.
*/
public static IOException unwrapRemoteIOException(Procedure proc) {
Exception e = proc.getException().unwrapRemoteException();
// Do not retry ProcedureExceptions!
return (e instanceof ProcedureException)? new DoNotRetryIOException(e):
proc.getException().unwrapRemoteIOException();
}
} }

View File

@ -69,7 +69,8 @@ public class ProcedureDescriber {
description.put("LAST_UPDATE", new Date(proc.getLastUpdate())); description.put("LAST_UPDATE", new Date(proc.getLastUpdate()));
if (proc.isFailed()) { if (proc.isFailed()) {
description.put("ERRORS", proc.getException().unwrapRemoteIOException().getMessage()); description.put("ERRORS",
MasterProcedureUtil.unwrapRemoteIOException(proc).getMessage());
} }
description.put("PARAMETERS", parametersToObject(proc)); description.put("PARAMETERS", parametersToObject(proc));

View File

@ -99,7 +99,7 @@ public abstract class ProcedurePrepareLatch {
@Override @Override
protected void countDown(final Procedure proc) { protected void countDown(final Procedure proc) {
if (proc.hasException()) { if (proc.hasException()) {
exception = proc.getException().unwrapRemoteIOException(); exception = MasterProcedureUtil.unwrapRemoteIOException(proc);
} }
latch.countDown(); latch.countDown();
} }

View File

@ -160,9 +160,10 @@ public final class ProcedureSyncWait {
throw new IOException("The Master is Aborting"); throw new IOException("The Master is Aborting");
} }
// If the procedure fails, we should always have an exception captured. Throw it.
// Needs to be an IOE to get out of here.
if (proc.hasException()) { if (proc.hasException()) {
// If the procedure fails, we should always have an exception captured. Throw it. throw MasterProcedureUtil.unwrapRemoteIOException(proc);
throw proc.getException().unwrapRemoteIOException();
} else { } else {
return proc.getResult(); return proc.getResult();
} }

View File

@ -109,7 +109,7 @@ public class TestMetaTableAccessor {
@Test @Test
public void testIsMetaWhenAllHealthy() throws InterruptedException { public void testIsMetaWhenAllHealthy() throws InterruptedException {
HMaster m = UTIL.getMiniHBaseCluster().getMaster(); HMaster m = UTIL.getMiniHBaseCluster().getMaster();
assertTrue(m.waitUntilMetaOnline()); assertTrue(m.waitForMetaOnline());
} }
@Test @Test
@ -118,7 +118,7 @@ public class TestMetaTableAccessor {
int index = UTIL.getMiniHBaseCluster().getServerWithMeta(); int index = UTIL.getMiniHBaseCluster().getServerWithMeta();
HRegionServer rsWithMeta = UTIL.getMiniHBaseCluster().getRegionServer(index); HRegionServer rsWithMeta = UTIL.getMiniHBaseCluster().getRegionServer(index);
rsWithMeta.abort("TESTING"); rsWithMeta.abort("TESTING");
assertTrue(m.waitUntilMetaOnline()); assertTrue(m.waitForMetaOnline());
} }
/** /**

View File

@ -116,7 +116,8 @@ public class TestHbck {
//bypass the procedure //bypass the procedure
List<Long> pids = Arrays.<Long>asList(procId); List<Long> pids = Arrays.<Long>asList(procId);
List<Boolean> results = TEST_UTIL.getHbck().bypassProcedure(pids, 30000, false); List<Boolean> results =
TEST_UTIL.getHbck().bypassProcedure(pids, 30000, false, false);
assertTrue("Failed to by pass procedure!", results.get(0)); assertTrue("Failed to by pass procedure!", results.get(0));
TEST_UTIL.waitFor(5000, () -> proc.isSuccess() && proc.isBypass()); TEST_UTIL.waitFor(5000, () -> proc.isSuccess() && proc.isBypass());
LOG.info("{} finished", proc); LOG.info("{} finished", proc);

View File

@ -0,0 +1,194 @@
/**
* 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.master.assignment;
import static org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.RegionStateTransitionState.REGION_STATE_TRANSITION_OPEN;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import org.apache.hadoop.hbase.HBaseClassTestRule;
import org.apache.hadoop.hbase.HBaseTestingUtility;
import org.apache.hadoop.hbase.ServerName;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Admin;
import org.apache.hadoop.hbase.client.RegionInfo;
import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv;
import org.apache.hadoop.hbase.procedure2.Procedure;
import org.apache.hadoop.hbase.procedure2.ProcedureSuspendedException;
import org.apache.hadoop.hbase.procedure2.ProcedureYieldException;
import org.apache.hadoop.hbase.testclassification.LargeTests;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.TestName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.hadoop.hbase.shaded.protobuf.generated.MasterProcedureProtos.RegionStateTransitionState;
/**
* Tests bypass on a region assign/unassign
*/
@Category({LargeTests.class})
public class TestRegionBypass {
private final static Logger LOG = LoggerFactory.getLogger(TestRegionBypass.class);
@ClassRule
public static final HBaseClassTestRule CLASS_RULE =
HBaseClassTestRule.forClass(TestRegionBypass.class);
@Rule
public TestName name = new TestName();
private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
private TableName tableName;
@BeforeClass
public static void startCluster() throws Exception {
TEST_UTIL.startMiniCluster(2);
}
@AfterClass
public static void stopCluster() throws Exception {
TEST_UTIL.shutdownMiniCluster();
}
@Before
public void before() throws IOException {
this.tableName = TableName.valueOf(this.name.getMethodName());
// Create a table. Has one region at least.
TEST_UTIL.createTable(this.tableName, Bytes.toBytes("cf"));
}
@Test
public void testBypass() throws IOException {
Admin admin = TEST_UTIL.getAdmin();
MasterProcedureEnv env =
TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor().getEnvironment();
List<RegionInfo> regions = admin.getRegions(this.tableName);
for (RegionInfo ri: regions) {
admin.unassign(ri.getRegionName(), false);
}
List<Long> pids = new ArrayList<>(regions.size());
for (RegionInfo ri: regions) {
Procedure<MasterProcedureEnv> p = new StallingAssignProcedure(env, ri, null, false,
RegionStateTransitionState.REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE,
RegionStateTransitionState.REGION_STATE_TRANSITION_CONFIRM_OPENED);
pids.add(TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor().
submitProcedure(p));
}
for (Long pid: pids) {
while (!TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor().isStarted(pid)) {
Thread.currentThread().yield();
}
}
List<Procedure<MasterProcedureEnv>> ps =
TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor().getProcedures();
for (Procedure<MasterProcedureEnv> p: ps) {
if (p instanceof StallingAssignProcedure) {
List<Boolean> bs = TEST_UTIL.getHbck().
bypassProcedure(Arrays.asList(p.getProcId()), 1000, true, false);
for (Boolean b: bs) {
LOG.info("BYPASSED {} {}", p.getProcId(), b);
}
}
}
// Try and assign WITHOUT override flag. Should fail!.
for (RegionInfo ri: regions) {
try {
admin.assign(ri.getRegionName());
} catch (Throwable dnrioe) {
// Expected
LOG.info("Expected {}", dnrioe);
}
}
while (!TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor().
getActiveProcIds().isEmpty()) {
Thread.currentThread().yield();
}
// Now assign with the override flag.
for (RegionInfo ri: regions) {
TEST_UTIL.getHbck().assigns(Arrays.<String>asList(ri.getEncodedName()), true);
}
while (!TEST_UTIL.getHBaseCluster().getMaster().getMasterProcedureExecutor().
getActiveProcIds().isEmpty()) {
Thread.currentThread().yield();
}
for (RegionInfo ri: regions) {
assertTrue(ri.toString(), TEST_UTIL.getMiniHBaseCluster().getMaster().getAssignmentManager().
getRegionStates().isRegionOnline(ri));
}
}
/**
* An AssignProcedure that Stalls just before the finish.
*/
public static class StallingAssignProcedure extends TransitRegionStateProcedure{
public final CountDownLatch latch = new CountDownLatch(2);
public StallingAssignProcedure(){}
public StallingAssignProcedure(MasterProcedureEnv env, RegionInfo hri,
ServerName assignCandidate, boolean forceNewPlan, RegionStateTransitionState initialState,
RegionStateTransitionState lastState) {
super(env, hri, assignCandidate, forceNewPlan, initialState, lastState);
init(env);
}
private void init(MasterProcedureEnv env){
RegionStateNode regionNode =
env.getAssignmentManager().getRegionStates().getOrCreateRegionStateNode(getRegion());
regionNode.setProcedure(this);
}
@Override
protected Flow executeFromState(MasterProcedureEnv env, RegionStateTransitionState state)
throws ProcedureSuspendedException, ProcedureYieldException, InterruptedException {
switch (state) {
case REGION_STATE_TRANSITION_GET_ASSIGN_CANDIDATE:
LOG.info("LATCH1 {}", this.latch.getCount());
this.latch.countDown();
setNextState(REGION_STATE_TRANSITION_OPEN);
return Flow.HAS_MORE_STATE;
case REGION_STATE_TRANSITION_OPEN:
if (latch.getCount() == 0) {
LOG.info("LATCH3 {}", this.latch.getCount());
return Flow.NO_MORE_STATE;
} else {
LOG.info("LATCH2 {}", this.latch.getCount());
return Flow.HAS_MORE_STATE;
}
default:
throw new UnsupportedOperationException("unhandled state=" + state);
}
}
}
}

View File

@ -31,8 +31,7 @@ EOF
end end
def command def command
formatter.header(%w[Id Name State Submitted_Time Last_Update Parameters]) formatter.header(%w[PID Name State Submitted Last_Update Parameters])
list = JSON.parse(admin.list_procedures) list = JSON.parse(admin.list_procedures)
list.each do |proc| list.each do |proc|
submitted_time = Time.at(Integer(proc['submittedTime']) / 1000).to_s submitted_time = Time.at(Integer(proc['submittedTime']) / 1000).to_s

View File

@ -39,5 +39,4 @@ public class TestShell extends AbstractTestShell {
// Start all ruby tests // Start all ruby tests
jruby.runScriptlet(PathType.ABSOLUTE, "src/test/ruby/tests_runner.rb"); jruby.runScriptlet(PathType.ABSOLUTE, "src/test/ruby/tests_runner.rb");
} }
} }

View File

@ -85,9 +85,9 @@ class ShellTest < Test::Unit::TestCase
define_test "Shell::Shell interactive mode should not throw" do define_test "Shell::Shell interactive mode should not throw" do
# incorrect number of arguments # incorrect number of arguments
@shell.command('create', 'foo') @shell.command('create', 'nothrow_table')
@shell.command('create', 'foo', 'family_1') @shell.command('create', 'nothrow_table', 'family_1')
# create a table that exists # create a table that exists
@shell.command('create', 'foo', 'family_1') @shell.command('create', 'nothrow_table', 'family_1')
end end
end end