HDFS-11701. NPE from Unresolved Host causes permanent DFSInputStream failures. Contributed by Lokesh Jain.

This commit is contained in:
Jitendra Pandey 2018-02-07 11:21:41 -08:00
parent 456705a07c
commit b061215ecf
5 changed files with 64 additions and 21 deletions

View File

@ -20,6 +20,7 @@
import static org.apache.hadoop.fs.CommonConfigurationKeys.FS_CLIENT_TOPOLOGY_RESOLUTION_ENABLED; import static org.apache.hadoop.fs.CommonConfigurationKeys.FS_CLIENT_TOPOLOGY_RESOLUTION_ENABLED;
import static org.apache.hadoop.fs.CommonConfigurationKeys.FS_CLIENT_TOPOLOGY_RESOLUTION_ENABLED_DEFAULT; import static org.apache.hadoop.fs.CommonConfigurationKeys.FS_CLIENT_TOPOLOGY_RESOLUTION_ENABLED_DEFAULT;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -238,7 +239,7 @@ public ByteArrayManager getByteArrayManager() {
return byteArrayManager; return byteArrayManager;
} }
public int getNetworkDistance(DatanodeInfo datanodeInfo) { public int getNetworkDistance(DatanodeInfo datanodeInfo) throws IOException {
// If applications disable the feature or the client machine can't // If applications disable the feature or the client machine can't
// resolve its network location, clientNode will be set to null. // resolve its network location, clientNode will be set to null.
if (clientNode == null) { if (clientNode == null) {

View File

@ -550,7 +550,11 @@ public static String dateToIso8601String(Date date) {
private static final Map<String, Boolean> localAddrMap = Collections private static final Map<String, Boolean> localAddrMap = Collections
.synchronizedMap(new HashMap<String, Boolean>()); .synchronizedMap(new HashMap<String, Boolean>());
public static boolean isLocalAddress(InetSocketAddress targetAddr) { public static boolean isLocalAddress(InetSocketAddress targetAddr)
throws IOException {
if (targetAddr.isUnresolved()) {
throw new IOException("Unresolved host: " + targetAddr);
}
InetAddress addr = targetAddr.getAddress(); InetAddress addr = targetAddr.getAddress();
Boolean cached = localAddrMap.get(addr.getHostAddress()); Boolean cached = localAddrMap.get(addr.getHostAddress());
if (cached != null) { if (cached != null) {

View File

@ -357,28 +357,32 @@ public BlockReader build() throws IOException {
return reader; return reader;
} }
final ShortCircuitConf scConf = conf.getShortCircuitConf(); final ShortCircuitConf scConf = conf.getShortCircuitConf();
if (scConf.isShortCircuitLocalReads() && allowShortCircuitLocalReads) { try {
if (clientContext.getUseLegacyBlockReaderLocal()) { if (scConf.isShortCircuitLocalReads() && allowShortCircuitLocalReads) {
reader = getLegacyBlockReaderLocal(); if (clientContext.getUseLegacyBlockReaderLocal()) {
if (reader != null) { reader = getLegacyBlockReaderLocal();
LOG.trace("{}: returning new legacy block reader local.", this); if (reader != null) {
return reader; LOG.trace("{}: returning new legacy block reader local.", this);
return reader;
}
} else {
reader = getBlockReaderLocal();
if (reader != null) {
LOG.trace("{}: returning new block reader local.", this);
return reader;
}
} }
} else { }
reader = getBlockReaderLocal(); if (scConf.isDomainSocketDataTraffic()) {
reader = getRemoteBlockReaderFromDomain();
if (reader != null) { if (reader != null) {
LOG.trace("{}: returning new block reader local.", this); LOG.trace("{}: returning new remote block reader using UNIX domain "
+ "socket on {}", this, pathInfo.getPath());
return reader; return reader;
} }
} }
} } catch (IOException e) {
if (scConf.isDomainSocketDataTraffic()) { LOG.debug("Block read failed. Getting remote block reader using TCP", e);
reader = getRemoteBlockReaderFromDomain();
if (reader != null) {
LOG.trace("{}: returning new remote block reader using UNIX domain "
+ "socket on {}", this, pathInfo.getPath());
return reader;
}
} }
Preconditions.checkState(!DFSInputStream.tcpReadsDisabledForTesting, Preconditions.checkState(!DFSInputStream.tcpReadsDisabledForTesting,
"TCP reads were disabled for testing, but we failed to " + "TCP reads were disabled for testing, but we failed to " +
@ -469,7 +473,7 @@ private BlockReader getLegacyBlockReaderLocal() throws IOException {
return null; return null;
} }
private BlockReader getBlockReaderLocal() throws InvalidToken { private BlockReader getBlockReaderLocal() throws IOException {
LOG.trace("{}: trying to construct a BlockReaderLocal for short-circuit " LOG.trace("{}: trying to construct a BlockReaderLocal for short-circuit "
+ " reads.", this); + " reads.", this);
if (pathInfo == null) { if (pathInfo == null) {

View File

@ -133,7 +133,8 @@ public DomainSocketFactory(ShortCircuitConf conf) {
* *
* @return Information about the socket path. * @return Information about the socket path.
*/ */
public PathInfo getPathInfo(InetSocketAddress addr, ShortCircuitConf conf) { public PathInfo getPathInfo(InetSocketAddress addr, ShortCircuitConf conf)
throws IOException {
// If there is no domain socket path configured, we can't use domain // If there is no domain socket path configured, we can't use domain
// sockets. // sockets.
if (conf.getDomainSocketPath().isEmpty()) return PathInfo.NOT_CONFIGURED; if (conf.getDomainSocketPath().isEmpty()) return PathInfo.NOT_CONFIGURED;

View File

@ -28,6 +28,7 @@
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.ClosedByInterruptException; import java.nio.channels.ClosedByInterruptException;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@ -53,6 +54,7 @@
import org.apache.hadoop.hdfs.protocol.LocatedBlock; import org.apache.hadoop.hdfs.protocol.LocatedBlock;
import org.apache.hadoop.hdfs.shortcircuit.DfsClientShmManager.PerDatanodeVisitorInfo; import org.apache.hadoop.hdfs.shortcircuit.DfsClientShmManager.PerDatanodeVisitorInfo;
import org.apache.hadoop.hdfs.shortcircuit.DfsClientShmManager.Visitor; import org.apache.hadoop.hdfs.shortcircuit.DfsClientShmManager.Visitor;
import org.apache.hadoop.hdfs.shortcircuit.DomainSocketFactory;
import org.apache.hadoop.hdfs.shortcircuit.ShortCircuitCache; import org.apache.hadoop.hdfs.shortcircuit.ShortCircuitCache;
import org.apache.hadoop.hdfs.shortcircuit.ShortCircuitReplicaInfo; import org.apache.hadoop.hdfs.shortcircuit.ShortCircuitReplicaInfo;
import org.apache.hadoop.io.IOUtils; import org.apache.hadoop.io.IOUtils;
@ -68,6 +70,7 @@
import org.junit.Test; import org.junit.Test;
import com.google.common.util.concurrent.Uninterruptibles; import com.google.common.util.concurrent.Uninterruptibles;
import org.junit.rules.ExpectedException;
import org.junit.rules.Timeout; import org.junit.rules.Timeout;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -79,6 +82,9 @@ public class TestBlockReaderFactory {
@Rule @Rule
public final Timeout globalTimeout = new Timeout(180000); public final Timeout globalTimeout = new Timeout(180000);
@Rule
public ExpectedException thrown = ExpectedException.none();
@Before @Before
public void init() { public void init() {
DomainSocket.disableBindPathValidation(); DomainSocket.disableBindPathValidation();
@ -144,6 +150,33 @@ public void testFallbackFromShortCircuitToUnixDomainTraffic()
sockDir.close(); sockDir.close();
} }
/**
* Test the case where address passed to DomainSocketFactory#getPathInfo is
* unresolved. In such a case an exception should be thrown.
*/
@Test(timeout=60000)
public void testGetPathInfoWithUnresolvedHost() throws Exception {
TemporarySocketDirectory sockDir = new TemporarySocketDirectory();
Configuration conf =
createShortCircuitConf("testGetPathInfoWithUnresolvedHost", sockDir);
conf.set(DFS_CLIENT_CONTEXT,
"testGetPathInfoWithUnresolvedHost_Context");
conf.setBoolean(DFS_CLIENT_DOMAIN_SOCKET_DATA_TRAFFIC, true);
DfsClientConf.ShortCircuitConf shortCircuitConf =
new DfsClientConf.ShortCircuitConf(conf);
DomainSocketFactory domainSocketFactory =
new DomainSocketFactory(shortCircuitConf);
InetSocketAddress targetAddr =
InetSocketAddress.createUnresolved("random", 32456);
thrown.expect(IOException.class);
thrown.expectMessage("Unresolved host: " + targetAddr);
domainSocketFactory.getPathInfo(targetAddr, shortCircuitConf);
sockDir.close();
}
/** /**
* Test the case where we have multiple threads waiting on the * Test the case where we have multiple threads waiting on the
* ShortCircuitCache delivering a certain ShortCircuitReplica. * ShortCircuitCache delivering a certain ShortCircuitReplica.