HDFS-14422. RBF: Router shouldn't allow READ operations in safe mode. Contributed by Inigo Goiri.

This commit is contained in:
Ayush Saxena 2019-04-16 19:45:51 +05:30 committed by Brahma Reddy Battula
parent 0f9b8d7a75
commit de7da9b69e
3 changed files with 74 additions and 3 deletions

View File

@ -87,6 +87,8 @@ public class MountTableResolver
/** If the tree has been initialized. */ /** If the tree has been initialized. */
private boolean init = false; private boolean init = false;
/** If the mount table is manually disabled*/
private boolean disabled = false;
/** Path -> Remote HDFS location. */ /** Path -> Remote HDFS location. */
private final TreeMap<String, MountTable> tree = new TreeMap<>(); private final TreeMap<String, MountTable> tree = new TreeMap<>();
/** Path -> Remote location. */ /** Path -> Remote location. */
@ -391,7 +393,14 @@ public class MountTableResolver
}; };
return this.locationCache.get(path, meh); return this.locationCache.get(path, meh);
} catch (ExecutionException e) { } catch (ExecutionException e) {
throw new IOException(e); Throwable cause = e.getCause();
final IOException ioe;
if (cause instanceof IOException) {
ioe = (IOException) cause;
} else {
ioe = new IOException(cause);
}
throw ioe;
} finally { } finally {
readLock.unlock(); readLock.unlock();
} }
@ -504,7 +513,7 @@ public class MountTableResolver
* @throws StateStoreUnavailableException If it cannot connect to the store. * @throws StateStoreUnavailableException If it cannot connect to the store.
*/ */
private void verifyMountTable() throws StateStoreUnavailableException { private void verifyMountTable() throws StateStoreUnavailableException {
if (!this.init) { if (!this.init || disabled) {
throw new StateStoreUnavailableException("Mount Table not initialized"); throw new StateStoreUnavailableException("Mount Table not initialized");
} }
} }
@ -654,4 +663,9 @@ public class MountTableResolver
public void setDefaultNSEnable(boolean defaultNSRWEnable) { public void setDefaultNSEnable(boolean defaultNSRWEnable) {
this.defaultNSEnable = defaultNSRWEnable; this.defaultNSEnable = defaultNSRWEnable;
} }
@VisibleForTesting
public void setDisabled(boolean disable) {
this.disabled = disable;
}
} }

View File

@ -114,6 +114,7 @@ import org.apache.hadoop.hdfs.server.federation.resolver.FileSubclusterResolver;
import org.apache.hadoop.hdfs.server.federation.resolver.MountTableResolver; import org.apache.hadoop.hdfs.server.federation.resolver.MountTableResolver;
import org.apache.hadoop.hdfs.server.federation.resolver.PathLocation; import org.apache.hadoop.hdfs.server.federation.resolver.PathLocation;
import org.apache.hadoop.hdfs.server.federation.resolver.RemoteLocation; import org.apache.hadoop.hdfs.server.federation.resolver.RemoteLocation;
import org.apache.hadoop.hdfs.server.federation.store.StateStoreUnavailableException;
import org.apache.hadoop.hdfs.server.federation.store.records.MountTable; import org.apache.hadoop.hdfs.server.federation.store.records.MountTable;
import org.apache.hadoop.hdfs.server.federation.router.security.RouterSecurityManager; import org.apache.hadoop.hdfs.server.federation.router.security.RouterSecurityManager;
import org.apache.hadoop.hdfs.server.namenode.CheckpointSignature; import org.apache.hadoop.hdfs.server.namenode.CheckpointSignature;
@ -480,17 +481,26 @@ public class RouterRpcServer extends AbstractService
// Store the category of the operation category for this thread // Store the category of the operation category for this thread
opCategory.set(op); opCategory.set(op);
// We allow unchecked and read operations // We allow unchecked and read operations to try, fail later
if (op == OperationCategory.UNCHECKED || op == OperationCategory.READ) { if (op == OperationCategory.UNCHECKED || op == OperationCategory.READ) {
return; return;
} }
checkSafeMode();
}
/**
* Check if the Router is in safe mode.
* @throws StandbyException If the Router is in safe mode and cannot serve
* client requests.
*/
private void checkSafeMode() throws StandbyException {
RouterSafemodeService safemodeService = router.getSafemodeService(); RouterSafemodeService safemodeService = router.getSafemodeService();
if (safemodeService != null && safemodeService.isInSafeMode()) { if (safemodeService != null && safemodeService.isInSafeMode()) {
// Throw standby exception, router is not available // Throw standby exception, router is not available
if (rpcMonitor != null) { if (rpcMonitor != null) {
rpcMonitor.routerFailureSafemode(); rpcMonitor.routerFailureSafemode();
} }
OperationCategory op = opCategory.get();
throw new StandbyException("Router " + router.getRouterId() + throw new StandbyException("Router " + router.getRouterId() +
" is in safe mode and cannot handle " + op + " requests"); " is in safe mode and cannot handle " + op + " requests");
} }
@ -1469,6 +1479,9 @@ public class RouterRpcServer extends AbstractService
if (this.rpcMonitor != null) { if (this.rpcMonitor != null) {
this.rpcMonitor.routerFailureStateStore(); this.rpcMonitor.routerFailureStateStore();
} }
if (ioe instanceof StateStoreUnavailableException) {
checkSafeMode();
}
throw ioe; throw ioe;
} }
} }

View File

@ -34,9 +34,13 @@ import java.util.concurrent.TimeUnit;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdfs.server.federation.RouterConfigBuilder; import org.apache.hadoop.hdfs.server.federation.RouterConfigBuilder;
import org.apache.hadoop.hdfs.server.federation.resolver.MountTableResolver;
import org.apache.hadoop.hdfs.server.federation.store.StateStoreUnavailableException;
import org.apache.hadoop.hdfs.server.federation.store.protocol.EnterSafeModeRequest;
import org.apache.hadoop.hdfs.tools.federation.RouterAdmin; import org.apache.hadoop.hdfs.tools.federation.RouterAdmin;
import org.apache.hadoop.ipc.StandbyException; import org.apache.hadoop.ipc.StandbyException;
import org.apache.hadoop.service.Service.STATE; import org.apache.hadoop.service.Service.STATE;
import org.apache.hadoop.test.GenericTestUtils;
import org.apache.hadoop.util.Time; import org.apache.hadoop.util.Time;
import org.apache.hadoop.util.ToolRunner; import org.apache.hadoop.util.ToolRunner;
import org.junit.After; import org.junit.After;
@ -234,4 +238,44 @@ public class TestRouterSafemode {
throws IllegalStateException, IOException { throws IllegalStateException, IOException {
assertEquals(status, router.getRouterState()); assertEquals(status, router.getRouterState());
} }
@Test
public void testRouterNotInitMountTable() throws Exception {
// Manually disable the mount table to trigger unavailable exceptions
MountTableResolver mountTable =
(MountTableResolver)router.getSubclusterResolver();
mountTable.setDisabled(true);
// Wait until it gets out of safe mode
int interval = 2 * (int)conf.getTimeDuration(DFS_ROUTER_SAFEMODE_EXTENSION,
TimeUnit.SECONDS.toMillis(2), TimeUnit.MILLISECONDS);
GenericTestUtils.waitFor(
() -> router.getRouterState() == RouterServiceState.RUNNING,
100, interval);
// Getting file info should fail
try {
router.getRpcServer().getFileInfo("/mnt/file.txt");
fail("We should have thrown StateStoreUnavailableException");
} catch (StateStoreUnavailableException e) {
assertEquals("Mount Table not initialized", e.getMessage());
}
// Enter safe mode
RouterAdminServer admin = router.getAdminServer();
EnterSafeModeRequest request = EnterSafeModeRequest.newInstance();
admin.enterSafeMode(request);
verifyRouter(RouterServiceState.SAFEMODE);
// This time it should report safe mode
try {
router.getRpcServer().getFileInfo("/mnt/file.txt");
fail("We should have thrown a safe mode exception");
} catch (StandbyException e) {
String msg = e.getMessage();
assertTrue("Wrong message: " + msg,
msg.endsWith("is in safe mode and cannot handle READ requests"));
}
}
} }