HDFS-3981. Fix handling of FSN lock in getBlockLocations. Contributed by Xiaobo Peng and Todd Lipcon.
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1465751 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
16fedf5473
commit
087acf8fd6
|
@ -20,6 +20,9 @@ package org.apache.hadoop.test;
|
|||
import java.io.Closeable;
|
||||
|
||||
import org.mockito.Mockito;
|
||||
import org.mockito.invocation.InvocationOnMock;
|
||||
import org.mockito.stubbing.Answer;
|
||||
import org.mockito.stubbing.Stubber;
|
||||
|
||||
public abstract class MockitoUtil {
|
||||
|
||||
|
@ -33,4 +36,29 @@ public abstract class MockitoUtil {
|
|||
return Mockito.mock(clazz,
|
||||
Mockito.withSettings().extraInterfaces(Closeable.class));
|
||||
}
|
||||
|
||||
/**
|
||||
* Throw an exception from the mock/spy only in the case that the
|
||||
* call stack at the time the method has a line which matches the given
|
||||
* pattern.
|
||||
*
|
||||
* @param t the Throwable to throw
|
||||
* @param pattern the pattern against which to match the call stack trace
|
||||
* @return the stub in progress
|
||||
*/
|
||||
public static Stubber doThrowWhenCallStackMatches(
|
||||
final Throwable t, final String pattern) {
|
||||
return Mockito.doAnswer(new Answer<Object>() {
|
||||
@Override
|
||||
public Object answer(InvocationOnMock invocation) throws Throwable {
|
||||
t.setStackTrace(Thread.currentThread().getStackTrace());
|
||||
for (StackTraceElement elem : t.getStackTrace()) {
|
||||
if (elem.toString().matches(pattern)) {
|
||||
throw t;
|
||||
}
|
||||
}
|
||||
return invocation.callRealMethod();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -483,6 +483,9 @@ Release 2.0.5-beta - UNRELEASED
|
|||
HDFS-4658. Standby NN will log that it has received a block report "after
|
||||
becoming active" (atm)
|
||||
|
||||
HDFS-3981. Fix handling of FSN lock in getBlockLocations. (Xiaobo Peng
|
||||
and todd via todd)
|
||||
|
||||
Release 2.0.4-alpha - UNRELEASED
|
||||
|
||||
INCOMPATIBLE CHANGES
|
||||
|
|
|
@ -1378,15 +1378,15 @@ public class FSNamesystem implements Namesystem, FSClusterStats,
|
|||
long now = now();
|
||||
final INodeFile inode = INodeFile.valueOf(dir.getINode(src), src);
|
||||
if (doAccessTime && isAccessTimeSupported()) {
|
||||
if (now <= inode.getAccessTime() + getAccessTimePrecision()) {
|
||||
if (now > inode.getAccessTime() + getAccessTimePrecision()) {
|
||||
// if we have to set access time but we only have the readlock, then
|
||||
// restart this entire operation with the writeLock.
|
||||
if (isReadOp) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
dir.setTimes(src, inode, -1, now, false);
|
||||
}
|
||||
}
|
||||
return blockManager.createLocatedBlocks(inode.getBlocks(),
|
||||
inode.computeFileSize(false), inode.isUnderConstruction(),
|
||||
offset, length, needBlockToken);
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.net.InetSocketAddress;
|
|||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.fs.CommonConfigurationKeys;
|
||||
|
@ -36,8 +37,11 @@ import org.apache.hadoop.fs.FileSystem;
|
|||
import org.apache.hadoop.fs.Path;
|
||||
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
|
||||
import org.apache.hadoop.hdfs.protocol.HdfsConstants.DatanodeReportType;
|
||||
import org.apache.hadoop.hdfs.server.namenode.NameNodeAdapter;
|
||||
import org.apache.hadoop.test.MockitoUtil;
|
||||
import org.apache.hadoop.util.Time;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
/**
|
||||
* This class tests the access time on files.
|
||||
|
@ -274,6 +278,37 @@ public class TestSetTimes {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that when access time updates are not needed, the FSNamesystem
|
||||
* write lock is not taken by getBlockLocations.
|
||||
* Regression test for HDFS-3981.
|
||||
*/
|
||||
@Test(timeout=60000)
|
||||
public void testGetBlockLocationsOnlyUsesReadLock() throws IOException {
|
||||
Configuration conf = new HdfsConfiguration();
|
||||
conf.setInt(DFSConfigKeys.DFS_NAMENODE_ACCESSTIME_PRECISION_KEY, 100*1000);
|
||||
MiniDFSCluster cluster = new MiniDFSCluster.Builder(conf)
|
||||
.numDataNodes(0)
|
||||
.build();
|
||||
ReentrantReadWriteLock spyLock = NameNodeAdapter.spyOnFsLock(cluster.getNamesystem());
|
||||
try {
|
||||
// Create empty file in the FSN.
|
||||
Path p = new Path("/empty-file");
|
||||
DFSTestUtil.createFile(cluster.getFileSystem(), p, 0, (short)1, 0L);
|
||||
|
||||
// getBlockLocations() should not need the write lock, since we just created
|
||||
// the file (and thus its access time is already within the 100-second
|
||||
// accesstime precision configured above).
|
||||
MockitoUtil.doThrowWhenCallStackMatches(
|
||||
new AssertionError("Should not need write lock"),
|
||||
".*getBlockLocations.*")
|
||||
.when(spyLock).writeLock();
|
||||
cluster.getFileSystem().getFileBlockLocations(p, 0, 100);
|
||||
} finally {
|
||||
cluster.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
new TestSetTimes().testTimes();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue