HDFS-15359. EC: Allow closing a file with committed blocks. Contributed by Ayush Saxena.
This commit is contained in:
parent
8abff5151a
commit
2326123705
@ -1158,12 +1158,19 @@ public boolean commitOrCompleteLastBlock(BlockCollection bc,
|
|||||||
/**
|
/**
|
||||||
* If IBR is not sent from expected locations yet, add the datanodes to
|
* If IBR is not sent from expected locations yet, add the datanodes to
|
||||||
* pendingReconstruction in order to keep RedundancyMonitor from scheduling
|
* pendingReconstruction in order to keep RedundancyMonitor from scheduling
|
||||||
* the block.
|
* the block. In case of erasure coding blocks, adds only in case there
|
||||||
|
* isn't any missing node.
|
||||||
*/
|
*/
|
||||||
public void addExpectedReplicasToPending(BlockInfo blk) {
|
public void addExpectedReplicasToPending(BlockInfo blk) {
|
||||||
if (!blk.isStriped()) {
|
boolean addForStriped = false;
|
||||||
DatanodeStorageInfo[] expectedStorages =
|
DatanodeStorageInfo[] expectedStorages =
|
||||||
blk.getUnderConstructionFeature().getExpectedStorageLocations();
|
blk.getUnderConstructionFeature().getExpectedStorageLocations();
|
||||||
|
if (blk.isStriped()) {
|
||||||
|
BlockInfoStriped blkStriped = (BlockInfoStriped) blk;
|
||||||
|
addForStriped =
|
||||||
|
blkStriped.getRealTotalBlockNum() == expectedStorages.length;
|
||||||
|
}
|
||||||
|
if (!blk.isStriped() || addForStriped) {
|
||||||
if (expectedStorages.length - blk.numNodes() > 0) {
|
if (expectedStorages.length - blk.numNodes() > 0) {
|
||||||
ArrayList<DatanodeStorageInfo> pendingNodes = new ArrayList<>();
|
ArrayList<DatanodeStorageInfo> pendingNodes = new ArrayList<>();
|
||||||
for (DatanodeStorageInfo storage : expectedStorages) {
|
for (DatanodeStorageInfo storage : expectedStorages) {
|
||||||
|
@ -374,17 +374,27 @@ static String checkBlockComplete(BlockInfo[] blocks, int i,
|
|||||||
if (state == BlockUCState.COMPLETE) {
|
if (state == BlockUCState.COMPLETE) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (b.isStriped() || i < blocks.length - numCommittedAllowed) {
|
if (i < blocks.length - numCommittedAllowed) {
|
||||||
return b + " is " + state + " but not COMPLETE";
|
return b + " is " + state + " but not COMPLETE";
|
||||||
}
|
}
|
||||||
if (state != BlockUCState.COMMITTED) {
|
if (state != BlockUCState.COMMITTED) {
|
||||||
return b + " is " + state + " but neither COMPLETE nor COMMITTED";
|
return b + " is " + state + " but neither COMPLETE nor COMMITTED";
|
||||||
}
|
}
|
||||||
final int numExpectedLocations
|
|
||||||
= b.getUnderConstructionFeature().getNumExpectedLocations();
|
if (b.isStriped()) {
|
||||||
if (numExpectedLocations <= minReplication) {
|
BlockInfoStriped blkStriped = (BlockInfoStriped) b;
|
||||||
return b + " is " + state + " but numExpectedLocations = "
|
if (b.getUnderConstructionFeature().getNumExpectedLocations()
|
||||||
+ numExpectedLocations + " <= minReplication = " + minReplication;
|
!= blkStriped.getRealTotalBlockNum()) {
|
||||||
|
return b + " is a striped block in " + state + " with less then "
|
||||||
|
+ "required number of blocks.";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final int numExpectedLocations =
|
||||||
|
b.getUnderConstructionFeature().getNumExpectedLocations();
|
||||||
|
if (numExpectedLocations <= minReplication) {
|
||||||
|
return b + " is " + state + " but numExpectedLocations = "
|
||||||
|
+ numExpectedLocations + " <= minReplication = " + minReplication;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -4903,7 +4903,9 @@
|
|||||||
<description>
|
<description>
|
||||||
Normally a file can only be closed with all its blocks are committed.
|
Normally a file can only be closed with all its blocks are committed.
|
||||||
When this value is set to a positive integer N, a file can be closed
|
When this value is set to a positive integer N, a file can be closed
|
||||||
when N blocks are committed and the rest complete.
|
when N blocks are committed and the rest complete. In case of Erasure Coded
|
||||||
|
blocks, the committed block shall be allowed only when the block group is
|
||||||
|
complete. i.e no missing/lost block in the blockgroup.
|
||||||
</description>
|
</description>
|
||||||
</property>
|
</property>
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
package org.apache.hadoop.hdfs;
|
package org.apache.hadoop.hdfs;
|
||||||
|
|
||||||
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.hdfs.DFSConfigKeys.DFS_NAMENODE_FILE_CLOSE_NUM_COMMITTED_ALLOWED_KEY;
|
||||||
import static org.apache.hadoop.hdfs.client.HdfsClientConfigKeys.DFS_CLIENT_CONTEXT;
|
import static org.apache.hadoop.hdfs.client.HdfsClientConfigKeys.DFS_CLIENT_CONTEXT;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
@ -100,6 +101,7 @@
|
|||||||
import org.apache.hadoop.hdfs.protocol.OpenFileEntry;
|
import org.apache.hadoop.hdfs.protocol.OpenFileEntry;
|
||||||
import org.apache.hadoop.hdfs.protocol.OpenFilesIterator;
|
import org.apache.hadoop.hdfs.protocol.OpenFilesIterator;
|
||||||
import org.apache.hadoop.hdfs.server.datanode.DataNode;
|
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.hdfs.server.datanode.fsdataset.FsDatasetSpi;
|
||||||
import org.apache.hadoop.hdfs.server.datanode.fsdataset.FsVolumeSpi;
|
import org.apache.hadoop.hdfs.server.datanode.fsdataset.FsVolumeSpi;
|
||||||
import org.apache.hadoop.hdfs.server.namenode.ErasureCodingPolicyManager;
|
import org.apache.hadoop.hdfs.server.namenode.ErasureCodingPolicyManager;
|
||||||
@ -2105,4 +2107,41 @@ public void testGetECTopologyResultForPolicies() throws Exception {
|
|||||||
assertFalse(result.isSupported());
|
assertFalse(result.isSupported());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testECCloseCommittedBlock() throws Exception {
|
||||||
|
HdfsConfiguration conf = new HdfsConfiguration();
|
||||||
|
conf.setInt(DFS_NAMENODE_FILE_CLOSE_NUM_COMMITTED_ALLOWED_KEY, 1);
|
||||||
|
try (MiniDFSCluster cluster = new MiniDFSCluster.Builder(conf)
|
||||||
|
.numDataNodes(3).build()) {
|
||||||
|
cluster.waitActive();
|
||||||
|
final DistributedFileSystem dfs = cluster.getFileSystem();
|
||||||
|
Path dir = new Path("/dir");
|
||||||
|
dfs.mkdirs(dir);
|
||||||
|
dfs.enableErasureCodingPolicy("XOR-2-1-1024k");
|
||||||
|
dfs.setErasureCodingPolicy(dir, "XOR-2-1-1024k");
|
||||||
|
|
||||||
|
try (FSDataOutputStream str = dfs.create(new Path("/dir/file"));) {
|
||||||
|
for (int i = 0; i < 1024 * 1024 * 4; i++) {
|
||||||
|
str.write(i);
|
||||||
|
}
|
||||||
|
DataNodeTestUtils.pauseIBR(cluster.getDataNodes().get(0));
|
||||||
|
DataNodeTestUtils.pauseIBR(cluster.getDataNodes().get(1));
|
||||||
|
}
|
||||||
|
DataNodeTestUtils.resumeIBR(cluster.getDataNodes().get(0));
|
||||||
|
DataNodeTestUtils.resumeIBR(cluster.getDataNodes().get(1));
|
||||||
|
|
||||||
|
// Check if the blockgroup isn't complete then file close shouldn't be
|
||||||
|
// success with block in committed state.
|
||||||
|
cluster.getDataNodes().get(0).shutdown();
|
||||||
|
FSDataOutputStream str = dfs.create(new Path("/dir/file1"));
|
||||||
|
|
||||||
|
for (int i = 0; i < 1024 * 1024 * 4; i++) {
|
||||||
|
str.write(i);
|
||||||
|
}
|
||||||
|
DataNodeTestUtils.pauseIBR(cluster.getDataNodes().get(1));
|
||||||
|
DataNodeTestUtils.pauseIBR(cluster.getDataNodes().get(2));
|
||||||
|
LambdaTestUtils.intercept(IOException.class, "", () -> str.close());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user