-
Notifications
You must be signed in to change notification settings - Fork 9.2k
HDFS-15963. Unreleased volume references cause an infinite loop. #2889
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,6 +33,9 @@ | |
| import java.nio.ByteBuffer; | ||
| import java.util.Random; | ||
|
|
||
| import org.apache.hadoop.hdfs.server.datanode.DataNode; | ||
| import org.apache.hadoop.hdfs.server.datanode.DataNodeTestUtils; | ||
| import org.apache.hadoop.hdfs.server.datanode.fsdataset.impl.FsVolumeImpl; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
| import org.apache.hadoop.conf.Configuration; | ||
|
|
@@ -562,4 +565,57 @@ void writeBlock(ExtendedBlock block, BlockConstructionStage stage, | |
| checksum, CachingStrategy.newDefaultStrategy(), false, false, | ||
| null, null, new String[0]); | ||
| } | ||
|
|
||
| @Test | ||
| public void testReleaseVolumeRefIfExceptionThrown() throws IOException { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be great if we could rewritten this test as a true unit test: A BlockSender with mocks. However, given the dependency on the DataNode class, I recognize this is not trivial. |
||
| Path file = new Path("dataprotocol.dat"); | ||
| int numDataNodes = 1; | ||
|
|
||
| Configuration conf = new HdfsConfiguration(); | ||
| conf.setInt(DFSConfigKeys.DFS_REPLICATION_KEY, numDataNodes); | ||
| MiniDFSCluster cluster = new MiniDFSCluster.Builder(conf).numDataNodes( | ||
| numDataNodes).build(); | ||
| try { | ||
| cluster.waitActive(); | ||
| datanode = cluster.getFileSystem().getDataNodeStats( | ||
| DatanodeReportType.LIVE)[0]; | ||
| dnAddr = NetUtils.createSocketAddr(datanode.getXferAddr()); | ||
| FileSystem fileSys = cluster.getFileSystem(); | ||
|
|
||
| int fileLen = Math.min( | ||
| conf.getInt(DFSConfigKeys.DFS_BLOCK_SIZE_KEY, 4096), 4096); | ||
|
|
||
| DFSTestUtil.createFile(fileSys, file, fileLen, fileLen, | ||
| fileSys.getDefaultBlockSize(file), | ||
| fileSys.getDefaultReplication(file), 0L); | ||
|
|
||
| // get the first blockid for the file | ||
| final ExtendedBlock firstBlock = DFSTestUtil.getFirstBlock(fileSys, file); | ||
|
|
||
| String bpid = cluster.getNamesystem().getBlockPoolId(); | ||
| ExtendedBlock blk = new ExtendedBlock(bpid, firstBlock.getLocalBlock()); | ||
| sendBuf.reset(); | ||
| recvBuf.reset(); | ||
|
|
||
| // delete the meta file to create a exception in BlockSender constructor | ||
| DataNode dn = cluster.getDataNodes().get(0); | ||
| cluster.getMaterializedReplica(0, blk).deleteMeta(); | ||
|
|
||
| FsVolumeImpl volume = (FsVolumeImpl) DataNodeTestUtils.getFSDataset( | ||
| dn).getVolume(blk); | ||
| int beforeCnt = volume.getReferenceCount(); | ||
|
|
||
| sender.copyBlock(blk, BlockTokenSecretManager.DUMMY_TOKEN); | ||
| sendRecvData("Copy a block.", false); | ||
| Thread.sleep(1000); | ||
|
|
||
| int afterCnt = volume.getReferenceCount(); | ||
| assertEquals(beforeCnt, afterCnt); | ||
|
jojochuang marked this conversation as resolved.
|
||
|
|
||
| } catch (InterruptedException e) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it interrupts, let it throw. No need to handle the exception.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry I wasn't being clear myself. You don't have to catch the exception. Instead, add InterruptedException to the test method signature. |
||
| e.printStackTrace(); | ||
| } finally { | ||
| cluster.shutdown(); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,6 +24,7 @@ | |
| import org.apache.hadoop.fs.DF; | ||
| import org.apache.hadoop.hdfs.server.blockmanagement.BlockManager; | ||
| import org.apache.hadoop.hdfs.server.datanode.DirectoryScanner; | ||
| import org.apache.hadoop.hdfs.server.datanode.LocalReplica; | ||
| import org.apache.hadoop.thirdparty.com.google.common.collect.Lists; | ||
|
|
||
| import java.io.OutputStream; | ||
|
|
@@ -1805,4 +1806,38 @@ public void testNotifyNamenodeMissingOrNewBlock() throws Exception { | |
| cluster.shutdown(); | ||
| } | ||
| } | ||
|
|
||
| @Test | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should add a timeout (a class-wide timeout is preferred) here.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks, I'll add it. |
||
| public void testReleaseVolumeRefIfExceptionThrown() throws IOException { | ||
| MiniDFSCluster cluster = new MiniDFSCluster.Builder( | ||
| new HdfsConfiguration()).build(); | ||
| cluster.waitActive(); | ||
| FsVolumeImpl vol = (FsVolumeImpl) dataset.getFsVolumeReferences().get(0); | ||
| ExtendedBlock eb; | ||
| ReplicaInfo info; | ||
| int beforeCnt = 0; | ||
| try { | ||
| List<Block> blockList = new ArrayList<Block>(); | ||
| eb = new ExtendedBlock(BLOCKPOOL, 1, 1, 1001); | ||
| info = new FinalizedReplica( | ||
| eb.getLocalBlock(), vol, vol.getCurrentDir().getParentFile()); | ||
| dataset.volumeMap.add(BLOCKPOOL, info); | ||
| ((LocalReplica) info).getBlockFile().createNewFile(); | ||
| ((LocalReplica) info).getMetaFile().createNewFile(); | ||
| blockList.add(info); | ||
|
|
||
| // Create a runtime exception | ||
| dataset.asyncDiskService.shutdown(); | ||
|
|
||
| beforeCnt = vol.getReferenceCount(); | ||
| dataset.invalidate(BLOCKPOOL, blockList.toArray(new Block[0])); | ||
|
|
||
| } catch (RuntimeException re) { | ||
| int afterCnt = vol.getReferenceCount(); | ||
| assertEquals(beforeCnt, afterCnt); | ||
| re.printStackTrace(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. printing the stack trace is redundant here. let's remove it?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I'll remove it. |
||
| } finally { | ||
| cluster.shutdown(); | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,6 +18,9 @@ | |
| package org.apache.hadoop.hdfs.server.datanode.fsdataset.impl; | ||
| import org.apache.hadoop.fs.CreateFlag; | ||
| import org.apache.hadoop.fs.Path; | ||
| import org.apache.hadoop.hdfs.server.datanode.DataNode; | ||
| import org.apache.hadoop.hdfs.server.datanode.DataNodeTestUtils; | ||
| import org.apache.hadoop.hdfs.server.datanode.fsdataset.FsDatasetSpi; | ||
| import org.apache.hadoop.test.GenericTestUtils; | ||
| import org.apache.hadoop.util.ThreadUtil; | ||
| import org.junit.Assert; | ||
|
|
@@ -280,4 +283,40 @@ public void run() { | |
| } | ||
| } | ||
| } | ||
| @Test | ||
| public void testReleaseVolumeRefIfExceptionThrown() throws IOException { | ||
| getClusterBuilder().setRamDiskReplicaCapacity(2).build(); | ||
| final String METHOD_NAME = GenericTestUtils.getMethodName(); | ||
| final int SEED = 0xFADED; | ||
| Path path = new Path("/" + METHOD_NAME + ".Writer.File.dat"); | ||
|
|
||
| DataNode dn = cluster.getDataNodes().get(0); | ||
| FsDatasetSpi.FsVolumeReferences volumes = | ||
| DataNodeTestUtils.getFSDataset(dn).getFsVolumeReferences(); | ||
| int[] beforeCnts = new int[volumes.size()]; | ||
| try { | ||
| FsDatasetImpl ds = (FsDatasetImpl) DataNodeTestUtils.getFSDataset(dn); | ||
|
|
||
| // Create a runtime exception | ||
| ds.asyncLazyPersistService.shutdown(); | ||
| for (int i = 0; i < volumes.size(); ++i) { | ||
| beforeCnts[i] = ((FsVolumeImpl) volumes.get(i)).getReferenceCount(); | ||
| } | ||
|
|
||
| makeRandomTestFile(path, BLOCK_SIZE, true, SEED); | ||
| Thread.sleep(3 * LAZY_WRITER_INTERVAL_SEC * 1000); | ||
|
|
||
| for (int i = 0; i < volumes.size(); ++i) { | ||
| int afterCnt = ((FsVolumeImpl) volumes.get(i)).getReferenceCount(); | ||
| // LazyWriter keeps trying to save copies even if | ||
| // asyncLazyPersistService is already shutdown. | ||
| // If we do not release references, the number of | ||
| // references will increase infinitely. | ||
| Assert.assertTrue( | ||
| beforeCnts[i] == afterCnt || beforeCnts[i] == (afterCnt - 1)); | ||
| } | ||
| } catch (InterruptedException e) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need to handle the interruption
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry I wasn't being clear myself. You don't have to catch the exception. Instead, add InterruptedException to the test method signature. |
||
| e.printStackTrace(); | ||
| } | ||
| } | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.