blob: 13428e5983e608abd47312dcd90cce5371b212f7 [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.namenode.snapshot;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.HashMap;
import java.util.Map;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hdfs.DFSTestUtil;
import org.apache.hadoop.hdfs.DistributedFileSystem;
import org.apache.hadoop.hdfs.MiniDFSCluster;
import org.apache.hadoop.hdfs.server.namenode.FSDirectory;
import org.apache.hadoop.hdfs.server.namenode.FSNamesystem;
import org.apache.hadoop.hdfs.server.namenode.INode;
import org.apache.hadoop.hdfs.server.namenode.INodeFile;
import org.apache.hadoop.hdfs.server.namenode.INodesInPath;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* This class tests the replication handling/calculation of snapshots. In
* particular, {@link INodeFile#getFileReplication()} and
* {@link INodeFileWithSnapshot#getBlockReplication()} are tested to make sure
* the number of replication is calculated correctly with/without snapshots.
*/
public class TestSnapshotReplication {
private static final long seed = 0;
private static final short REPLICATION = 3;
private static final int NUMDATANODE = 5;
private static final long BLOCKSIZE = 1024;
private final Path dir = new Path("/TestSnapshot");
private final Path sub1 = new Path(dir, "sub1");
private final Path file1 = new Path(sub1, "file1");
Configuration conf;
MiniDFSCluster cluster;
FSNamesystem fsn;
DistributedFileSystem hdfs;
FSDirectory fsdir;
@Before
public void setUp() throws Exception {
conf = new Configuration();
cluster = new MiniDFSCluster.Builder(conf).numDataNodes(NUMDATANODE)
.build();
cluster.waitActive();
fsn = cluster.getNamesystem();
hdfs = cluster.getFileSystem();
fsdir = fsn.getFSDirectory();
}
@After
public void tearDown() throws Exception {
if (cluster != null) {
cluster.shutdown();
}
}
/**
* Check the replication of a given file. We test both
* {@link INodeFile#getFileReplication()} and
* {@link INodeFile#getBlockReplication()}.
*
* @param file The given file
* @param replication The expected replication number
* @param blockReplication The expected replication number for the block
* @throws Exception
*/
private void checkFileReplication(Path file, short replication,
short blockReplication) throws Exception {
// Get FileStatus of file1, and identify the replication number of file1.
// Note that the replication number in FileStatus was derived from
// INodeFile#getFileReplication().
short fileReplication = hdfs.getFileStatus(file1).getReplication();
assertEquals(replication, fileReplication);
// Check the correctness of getBlockReplication()
INode inode = fsdir.getINode(file1.toString());
assertTrue(inode instanceof INodeFile);
assertEquals(blockReplication, ((INodeFile) inode).getBlockReplication());
}
/**
* Test replication number calculation for a normal file without snapshots.
*/
@Test (timeout=60000)
public void testReplicationWithoutSnapshot() throws Exception {
// Create file1, set its replication to REPLICATION
DFSTestUtil.createFile(hdfs, file1, BLOCKSIZE, REPLICATION, seed);
// Check the replication of file1
checkFileReplication(file1, REPLICATION, REPLICATION);
// Change the replication factor of file1 from 3 to 2
hdfs.setReplication(file1, (short) (REPLICATION - 1));
// Check the replication again
checkFileReplication(file1, (short) (REPLICATION - 1),
(short) (REPLICATION - 1));
}
INodeFile getINodeFile(Path p) throws Exception {
final String s = p.toString();
return INodeFile.valueOf(fsdir.getINode(s), s);
}
/**
* Check the replication for both the current file and all its prior snapshots
*
* @param currentFile
* the Path of the current file
* @param snapshotRepMap
* A map maintaining all the snapshots of the current file, as well
* as their expected replication number stored in their corresponding
* INodes
* @param expectedBlockRep
* The expected replication number that should be returned by
* {@link INodeFileWithSnapshot#getBlockReplication()} of all the INodes
* @throws Exception
*/
private void checkSnapshotFileReplication(Path currentFile,
Map<Path, Short> snapshotRepMap, short expectedBlockRep) throws Exception {
// First check the getBlockReplication for the INode of the currentFile
final INodeFile inodeOfCurrentFile = getINodeFile(currentFile);
assertEquals(expectedBlockRep, inodeOfCurrentFile.getBlockReplication());
// Then check replication for every snapshot
for (Path ss : snapshotRepMap.keySet()) {
final INodesInPath iip = fsdir.getLastINodeInPath(ss.toString());
final INodeFile ssInode = (INodeFile)iip.getLastINode();
// The replication number derived from the
// INodeFileWithLink#getBlockReplication should always == expectedBlockRep
assertEquals(expectedBlockRep, ssInode.getBlockReplication());
// Also check the number derived from INodeFile#getFileReplication
assertEquals(snapshotRepMap.get(ss).shortValue(),
ssInode.getFileReplication(iip.getPathSnapshot()));
}
}
/**
* Test replication number calculation for a file with snapshots.
*/
@Test (timeout=60000)
public void testReplicationWithSnapshot() throws Exception {
short fileRep = 1;
// Create file1, set its replication to 1
DFSTestUtil.createFile(hdfs, file1, BLOCKSIZE, fileRep, seed);
Map<Path, Short> snapshotRepMap = new HashMap<Path, Short>();
// Change replication factor from 1 to 5. In the meanwhile, keep taking
// snapshots for sub1
for (; fileRep < NUMDATANODE; ) {
// Create snapshot for sub1
Path snapshotRoot = SnapshotTestHelper.createSnapshot(hdfs, sub1, "s"
+ fileRep);
Path snapshot = new Path(snapshotRoot, file1.getName());
// Check the replication stored in the INode of the snapshot of file1
assertEquals(fileRep, getINodeFile(snapshot).getFileReplication());
snapshotRepMap.put(snapshot, fileRep);
// Increase the replication factor by 1
hdfs.setReplication(file1, ++fileRep);
// Check the replication for file1
checkFileReplication(file1, fileRep, fileRep);
// Also check the replication for all the prior snapshots of file1
checkSnapshotFileReplication(file1, snapshotRepMap, fileRep);
}
// Change replication factor back to 3.
hdfs.setReplication(file1, REPLICATION);
// Check the replication for file1
// Currently the max replication among snapshots should be 4
checkFileReplication(file1, REPLICATION, (short) (NUMDATANODE - 1));
// Also check the replication for all the prior snapshots of file1.
// Currently the max replication among snapshots should be 4
checkSnapshotFileReplication(file1, snapshotRepMap,
(short) (NUMDATANODE - 1));
}
/**
* Test replication for a file with snapshots, also including the scenario
* where the original file is deleted
*/
@Test (timeout=60000)
public void testReplicationAfterDeletion() throws Exception {
// Create file1, set its replication to 3
DFSTestUtil.createFile(hdfs, file1, BLOCKSIZE, REPLICATION, seed);
Map<Path, Short> snapshotRepMap = new HashMap<Path, Short>();
// Take 3 snapshots of sub1
for (int i = 1; i <= 3; i++) {
Path root = SnapshotTestHelper.createSnapshot(hdfs, sub1, "s" + i);
Path ssFile = new Path(root, file1.getName());
snapshotRepMap.put(ssFile, REPLICATION);
}
// Check replication
checkFileReplication(file1, REPLICATION, REPLICATION);
checkSnapshotFileReplication(file1, snapshotRepMap, REPLICATION);
// Delete file1
hdfs.delete(file1, true);
// Check replication of snapshots
for (Path ss : snapshotRepMap.keySet()) {
final INodeFile ssInode = getINodeFile(ss);
// The replication number derived from the
// INodeFileWithLink#getBlockReplication should always == expectedBlockRep
assertEquals(REPLICATION, ssInode.getBlockReplication());
// Also check the number derived from INodeFile#getFileReplication
assertEquals(snapshotRepMap.get(ss).shortValue(),
ssInode.getFileReplication());
}
}
}