From 9678020e59cf073f74cce70ac57d1f6869349a36 Mon Sep 17 00:00:00 2001 From: Colin McCabe Date: Wed, 11 Dec 2013 21:30:16 +0000 Subject: [PATCH] HDFS-4201. NPE in BPServiceActor#sendHeartBeat (jxiang via cmccabe) git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1550269 13f79535-47bb-0310-9956-ffa450edef68 --- hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt | 2 + .../hdfs/server/datanode/BPOfferService.java | 16 +++++-- .../server/datanode/TestBPOfferService.java | 48 +++++++++++++++++++ 3 files changed, 63 insertions(+), 3 deletions(-) diff --git a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt index 2a97b9a11d8..ee9b964724c 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt +++ b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt @@ -813,6 +813,8 @@ Release 2.3.0 - UNRELEASED HDFS-5074. Allow starting up from an fsimage checkpoint in the middle of a segment. (Todd Lipcon via atm) + HDFS-4201. NPE in BPServiceActor#sendHeartBeat. (jxiang via cmccabe) + Release 2.2.0 - 2013-10-13 INCOMPATIBLE CHANGES diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/BPOfferService.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/BPOfferService.java index 65da792048e..e646be9a650 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/BPOfferService.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/BPOfferService.java @@ -274,12 +274,22 @@ class BPOfferService { synchronized void verifyAndSetNamespaceInfo(NamespaceInfo nsInfo) throws IOException { if (this.bpNSInfo == null) { this.bpNSInfo = nsInfo; - + boolean success = false; + // Now that we know the namespace ID, etc, we can pass this to the DN. // The DN can now initialize its local storage if we are the // first BP to handshake, etc. - dn.initBlockPool(this); - return; + try { + dn.initBlockPool(this); + success = true; + } finally { + if (!success) { + // The datanode failed to initialize the BP. We need to reset + // the namespace info so that other BPService actors still have + // a chance to set it, and re-initialize the datanode. + this.bpNSInfo = null; + } + } } else { checkNSEquality(bpNSInfo.getBlockPoolID(), nsInfo.getBlockPoolID(), "Blockpool ID"); diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestBPOfferService.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestBPOfferService.java index 420dfca2c8a..c5cb6c77f12 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestBPOfferService.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/datanode/TestBPOfferService.java @@ -25,7 +25,9 @@ import static org.junit.Assert.assertSame; import java.io.File; import java.io.IOException; import java.net.InetSocketAddress; +import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -294,6 +296,47 @@ public class TestBPOfferService { } } + /** + * Test datanode block pool initialization error handling. + * Failure in initializing a block pool should not cause NPE. + */ + @Test + public void testBPInitErrorHandling() throws Exception { + final DataNode mockDn = Mockito.mock(DataNode.class); + Mockito.doReturn(true).when(mockDn).shouldRun(); + Configuration conf = new Configuration(); + File dnDataDir = new File( + new File(TEST_BUILD_DATA, "testBPInitErrorHandling"), "data"); + conf.set(DFS_DATANODE_DATA_DIR_KEY, dnDataDir.toURI().toString()); + Mockito.doReturn(conf).when(mockDn).getConf(); + Mockito.doReturn(new DNConf(conf)).when(mockDn).getDnConf(); + Mockito.doReturn(DataNodeMetrics.create(conf, "fake dn")). + when(mockDn).getMetrics(); + final AtomicInteger count = new AtomicInteger(); + Mockito.doAnswer(new Answer() { + @Override + public Void answer(InvocationOnMock invocation) throws Throwable { + if (count.getAndIncrement() == 0) { + throw new IOException("faked initBlockPool exception"); + } + // The initBlockPool is called again. Now mock init is done. + Mockito.doReturn(mockFSDataset).when(mockDn).getFSDataset(); + return null; + } + }).when(mockDn).initBlockPool(Mockito.any(BPOfferService.class)); + BPOfferService bpos = setupBPOSForNNs(mockDn, mockNN1, mockNN2); + bpos.start(); + try { + waitForInitialization(bpos); + List actors = bpos.getBPServiceActors(); + assertEquals(1, actors.size()); + BPServiceActor actor = actors.get(0); + waitForBlockReport(actor.getNameNodeProxy()); + } finally { + bpos.stop(); + } + } + private void waitForOneToFail(final BPOfferService bpos) throws Exception { GenericTestUtils.waitFor(new Supplier() { @@ -311,6 +354,11 @@ public class TestBPOfferService { */ private BPOfferService setupBPOSForNNs( DatanodeProtocolClientSideTranslatorPB ... nns) throws IOException { + return setupBPOSForNNs(mockDn, nns); + } + + private BPOfferService setupBPOSForNNs(DataNode mockDn, + DatanodeProtocolClientSideTranslatorPB ... nns) throws IOException { // Set up some fake InetAddresses, then override the connectToNN // function to return the corresponding proxies.