|
19 | 19 |
|
20 | 20 | import static org.junit.Assert.assertEquals; |
21 | 21 | import static org.junit.Assert.assertNotNull; |
| 22 | +import static org.junit.Assert.fail; |
22 | 23 |
|
23 | 24 | import java.io.IOException; |
| 25 | +import java.io.RandomAccessFile; |
24 | 26 | import java.util.ArrayList; |
25 | 27 | import java.util.Arrays; |
26 | 28 | import java.util.List; |
|
36 | 38 | import org.apache.hadoop.hdfs.server.datanode.DataNodeTestUtils; |
37 | 39 | import org.apache.hadoop.hdfs.server.datanode.FsDatasetTestUtils; |
38 | 40 | import org.apache.hadoop.hdfs.server.datanode.ReplicaAlreadyExistsException; |
| 41 | +import org.apache.hadoop.hdfs.server.datanode.ReplicaBeingWritten; |
39 | 42 | import org.apache.hadoop.hdfs.server.datanode.ReplicaInPipeline; |
40 | 43 | import org.apache.hadoop.hdfs.server.datanode.ReplicaInfo; |
41 | 44 | import org.apache.hadoop.hdfs.server.datanode.ReplicaNotFoundException; |
42 | 45 | import org.apache.hadoop.hdfs.server.datanode.fsdataset.FsDatasetSpi; |
43 | 46 | import org.apache.hadoop.hdfs.server.datanode.fsdataset.FsVolumeSpi; |
44 | 47 | import org.apache.hadoop.hdfs.server.namenode.NameNode; |
| 48 | +import org.apache.hadoop.test.GenericTestUtils; |
45 | 49 | import org.apache.hadoop.util.AutoCloseableLock; |
46 | 50 | import org.apache.hadoop.util.DiskChecker.DiskOutOfSpaceException; |
47 | 51 | import org.junit.Assert; |
@@ -154,7 +158,7 @@ private ExtendedBlock[] setup(String bpid, FsDatasetTestUtils testUtils) |
154 | 158 |
|
155 | 159 | ExtendedBlock[] blocks = new ExtendedBlock[] { |
156 | 160 | new ExtendedBlock(bpid, 1, 1, 2001), new ExtendedBlock(bpid, 2, 1, 2002), |
157 | | - new ExtendedBlock(bpid, 3, 1, 2003), new ExtendedBlock(bpid, 4, 1, 2004), |
| 161 | + new ExtendedBlock(bpid, 3, 2, 2003), new ExtendedBlock(bpid, 4, 1, 2004), |
158 | 162 | new ExtendedBlock(bpid, 5, 1, 2005), new ExtendedBlock(bpid, 6, 1, 2006) |
159 | 163 | }; |
160 | 164 |
|
@@ -552,7 +556,52 @@ public void testReplicaMapAfterDatanodeRestart() throws Exception { |
552 | 556 | cluster.shutdown(); |
553 | 557 | } |
554 | 558 | } |
555 | | - |
| 559 | + |
| 560 | + /** |
| 561 | + * Test that we can successfully recover a {@link ReplicaBeingWritten} |
| 562 | + * which has inconsistent metadata (bytes were written to disk but bytesOnDisk |
| 563 | + * was not updated) but that recovery fails when the block is actually |
| 564 | + * corrupt (bytes are not present on disk). |
| 565 | + */ |
| 566 | + @Test |
| 567 | + public void testRecoverInconsistentRbw() throws IOException { |
| 568 | + Configuration conf = new HdfsConfiguration(); |
| 569 | + MiniDFSCluster cluster = new MiniDFSCluster.Builder(conf).build(); |
| 570 | + cluster.waitActive(); |
| 571 | + DataNode dn = cluster.getDataNodes().get(0); |
| 572 | + FsDatasetImpl fsDataset = (FsDatasetImpl)DataNodeTestUtils.getFSDataset(dn); |
| 573 | + |
| 574 | + // set up replicasMap |
| 575 | + String bpid = cluster.getNamesystem().getBlockPoolId(); |
| 576 | + ExtendedBlock[] blocks = setup(bpid, cluster.getFsDatasetTestUtils(dn)); |
| 577 | + |
| 578 | + ReplicaBeingWritten rbw = (ReplicaBeingWritten)fsDataset. |
| 579 | + getReplicaInfo(bpid, blocks[RBW].getBlockId()); |
| 580 | + long bytesOnDisk = rbw.getBytesOnDisk(); |
| 581 | + // simulate an inconsistent replica length update by reducing in-memory |
| 582 | + // value of on disk length |
| 583 | + rbw.setLastChecksumAndDataLen(bytesOnDisk - 1, null); |
| 584 | + fsDataset.recoverRbw(blocks[RBW], blocks[RBW].getGenerationStamp(), 0L, |
| 585 | + rbw.getNumBytes()); |
| 586 | + // after the recovery, on disk length should equal acknowledged length. |
| 587 | + Assert.assertTrue(rbw.getBytesOnDisk() == rbw.getBytesAcked()); |
| 588 | + |
| 589 | + // reduce on disk length again; this time actually truncate the file to |
| 590 | + // simulate the data not being present |
| 591 | + rbw.setLastChecksumAndDataLen(bytesOnDisk - 1, null); |
| 592 | + try (RandomAccessFile blockRAF = rbw.getFileIoProvider(). |
| 593 | + getRandomAccessFile(rbw.getVolume(), rbw.getBlockFile(), "rw")) { |
| 594 | + // truncate blockFile |
| 595 | + blockRAF.setLength(bytesOnDisk - 1); |
| 596 | + fsDataset.recoverRbw(blocks[RBW], blocks[RBW].getGenerationStamp(), 0L, |
| 597 | + rbw.getNumBytes()); |
| 598 | + fail("recovery should have failed"); |
| 599 | + } catch (ReplicaNotFoundException rnfe) { |
| 600 | + GenericTestUtils.assertExceptionContains("Found fewer bytesOnDisk than " + |
| 601 | + "bytesAcked for replica", rnfe); |
| 602 | + } |
| 603 | + } |
| 604 | + |
556 | 605 | /** |
557 | 606 | * Compare the replica map before and after the restart |
558 | 607 | **/ |
|
0 commit comments