blob: 2e084fca0b1ed66491aa65b5580b9d7c7160e2bc [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.hadoop.hdfs.server.blockmanagement;
import static org.apache.hadoop.hdfs.server.common.HdfsServerConstants.BLOCK_GROUP_INDEX_MASK;
import static org.apache.hadoop.hdfs.server.common.HdfsServerConstants.MAX_BLOCKS_IN_GROUP;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.spy;
import java.io.IOException;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hdfs.DFSConfigKeys;
import org.apache.hadoop.hdfs.DFSTestUtil;
import org.apache.hadoop.hdfs.HdfsConfiguration;
import org.apache.hadoop.hdfs.MiniDFSCluster;
import org.apache.hadoop.hdfs.protocol.HdfsConstants;
import org.apache.hadoop.hdfs.protocol.LocatedBlock;
import org.apache.hadoop.hdfs.server.namenode.FSNamesystem;
import org.apache.hadoop.test.GenericTestUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.internal.util.reflection.Whitebox;
import org.mockito.stubbing.Answer;
/**
* Tests the sequential blockGroup ID generation mechanism and blockGroup ID
* collision handling.
*/
public class TestSequentialBlockGroupId {
private static final Log LOG = LogFactory
.getLog("TestSequentialBlockGroupId");
private final short REPLICATION = 1;
private final long SEED = 0;
private final int dataBlocks = HdfsConstants.NUM_DATA_BLOCKS;
private final int parityBlocks = HdfsConstants.NUM_PARITY_BLOCKS;
private final int cellSize = HdfsConstants.BLOCK_STRIPED_CELL_SIZE;
private final int stripesPerBlock = 2;
private final int blockSize = cellSize * stripesPerBlock;
private final int numDNs = dataBlocks + parityBlocks + 2;
private final int blockGrpCount = 4;
private final int fileLen = blockSize * dataBlocks * blockGrpCount;
private MiniDFSCluster cluster;
private FileSystem fs;
private SequentialBlockGroupIdGenerator blockGrpIdGenerator;
private Path eczone = new Path("/eczone");
@Before
public void setup() throws Exception {
Configuration conf = new HdfsConfiguration();
conf.setInt(DFSConfigKeys.DFS_REPLICATION_KEY, 1);
conf.setLong(DFSConfigKeys.DFS_BLOCK_SIZE_KEY, blockSize);
cluster = new MiniDFSCluster.Builder(conf).numDataNodes(numDNs).build();
cluster.waitActive();
fs = cluster.getFileSystem();
blockGrpIdGenerator = cluster.getNamesystem().getBlockIdManager()
.getBlockGroupIdGenerator();
fs.mkdirs(eczone);
cluster.getFileSystem().getClient()
.createErasureCodingZone("/eczone", null);
}
@After
public void teardown() {
if (cluster != null) {
cluster.shutdown();
}
}
/**
* Test that blockGroup IDs are generating unique value.
*/
@Test(timeout = 60000)
public void testBlockGroupIdGeneration() throws IOException {
long blockGroupIdInitialValue = blockGrpIdGenerator.getCurrentValue();
// Create a file that is 4 blocks long.
Path path = new Path(eczone, "testBlockGrpIdGeneration.dat");
DFSTestUtil.createFile(fs, path, cellSize, fileLen, blockSize, REPLICATION,
SEED);
List<LocatedBlock> blocks = DFSTestUtil.getAllBlocks(fs, path);
assertThat("Wrong BlockGrps", blocks.size(), is(blockGrpCount));
// initialising the block group generator for verifying the block id
blockGrpIdGenerator.setCurrentValue(blockGroupIdInitialValue);
// Ensure that the block IDs are generating unique value.
for (int i = 0; i < blocks.size(); ++i) {
blockGrpIdGenerator
.skipTo((blockGrpIdGenerator.getCurrentValue() & ~BLOCK_GROUP_INDEX_MASK)
+ MAX_BLOCKS_IN_GROUP);
long nextBlockExpectedId = blockGrpIdGenerator.getCurrentValue();
long nextBlockGrpId = blocks.get(i).getBlock().getBlockId();
LOG.info("BlockGrp" + i + " id is " + nextBlockGrpId);
assertThat("BlockGrpId mismatches!", nextBlockGrpId,
is(nextBlockExpectedId));
}
}
/**
* Test that collisions in the blockGroup ID space are handled gracefully.
*/
@Test(timeout = 60000)
public void testTriggerBlockGroupIdCollision() throws IOException {
long blockGroupIdInitialValue = blockGrpIdGenerator.getCurrentValue();
// Create a file with a few blocks to rev up the global block ID
// counter.
Path path1 = new Path(eczone, "testBlockGrpIdCollisionDetection_file1.dat");
DFSTestUtil.createFile(fs, path1, cellSize, fileLen, blockSize,
REPLICATION, SEED);
List<LocatedBlock> blocks1 = DFSTestUtil.getAllBlocks(fs, path1);
assertThat("Wrong BlockGrps", blocks1.size(), is(blockGrpCount));
// Rewind the block ID counter in the name system object. This will result
// in block ID collisions when we try to allocate new blocks.
blockGrpIdGenerator.setCurrentValue(blockGroupIdInitialValue);
// Trigger collisions by creating a new file.
Path path2 = new Path(eczone, "testBlockGrpIdCollisionDetection_file2.dat");
DFSTestUtil.createFile(fs, path2, cellSize, fileLen, blockSize,
REPLICATION, SEED);
List<LocatedBlock> blocks2 = DFSTestUtil.getAllBlocks(fs, path2);
assertThat("Wrong BlockGrps", blocks2.size(), is(blockGrpCount));
// Make sure that file1 and file2 block IDs are different
for (LocatedBlock locBlock1 : blocks1) {
long blockId1 = locBlock1.getBlock().getBlockId();
for (LocatedBlock locBlock2 : blocks2) {
long blockId2 = locBlock2.getBlock().getBlockId();
assertThat("BlockGrpId mismatches!", blockId1, is(not(blockId2)));
}
}
}
/**
* Test that collisions in the blockGroup ID when the id is occupied by legacy
* block.
*/
@Test(timeout = 60000)
public void testTriggerBlockGroupIdCollisionWithLegacyBlockId()
throws Exception {
long blockGroupIdInitialValue = blockGrpIdGenerator.getCurrentValue();
blockGrpIdGenerator
.skipTo((blockGrpIdGenerator.getCurrentValue() & ~BLOCK_GROUP_INDEX_MASK)
+ MAX_BLOCKS_IN_GROUP);
final long curBlockGroupIdValue = blockGrpIdGenerator.getCurrentValue();
// Creates contiguous block with negative blockId so that it would trigger
// collision during blockGroup Id generation
FSNamesystem fsn = cluster.getNamesystem();
// Replace SequentialBlockIdGenerator with a spy
SequentialBlockIdGenerator blockIdGenerator = spy(fsn.getBlockIdManager()
.getBlockIdGenerator());
Whitebox.setInternalState(fsn.getBlockIdManager(), "blockIdGenerator",
blockIdGenerator);
SequentialBlockIdGenerator spySequentialBlockIdGenerator = new SequentialBlockIdGenerator(
null) {
@Override
public long nextValue() {
return curBlockGroupIdValue;
}
};
final Answer<Object> delegator = new GenericTestUtils.DelegateAnswer(
spySequentialBlockIdGenerator);
doAnswer(delegator).when(blockIdGenerator).nextValue();
Path path1 = new Path("/testCollisionWithLegacyBlock_file1.dat");
DFSTestUtil.createFile(fs, path1, 1024, REPLICATION, SEED);
List<LocatedBlock> contiguousBlocks = DFSTestUtil.getAllBlocks(fs, path1);
assertThat(contiguousBlocks.size(), is(1));
Assert.assertEquals("Unexpected BlockId!", curBlockGroupIdValue,
contiguousBlocks.get(0).getBlock().getBlockId());
// Reset back to the initial value to trigger collision
blockGrpIdGenerator.setCurrentValue(blockGroupIdInitialValue);
// Trigger collisions by creating a new file.
Path path2 = new Path(eczone, "testCollisionWithLegacyBlock_file2.dat");
DFSTestUtil.createFile(fs, path2, cellSize, fileLen, blockSize,
REPLICATION, SEED);
List<LocatedBlock> blocks2 = DFSTestUtil.getAllBlocks(fs, path2);
assertThat("Wrong BlockGrps", blocks2.size(), is(blockGrpCount));
// Make sure that file1 and file2 block IDs are different
for (LocatedBlock locBlock1 : contiguousBlocks) {
long blockId1 = locBlock1.getBlock().getBlockId();
for (LocatedBlock locBlock2 : blocks2) {
long blockId2 = locBlock2.getBlock().getBlockId();
assertThat("BlockGrpId mismatches!", blockId1, is(not(blockId2)));
}
}
}
}