HDFS-9145. Tracking methods that hold FSNamesytemLock for too long. Contributed by Mingliang Liu.

This commit is contained in:
Haohui Mai 2015-10-13 13:20:11 -07:00
parent 38f86c0842
commit 5c5362980c
4 changed files with 84 additions and 0 deletions

View File

@ -196,6 +196,10 @@ public abstract class GenericTestUtils {
logger.removeAppender(appender);
}
public void clearOutput() {
sw.getBuffer().setLength(0);
}
}

View File

@ -717,6 +717,9 @@ Release 2.8.0 - UNRELEASED
HDFS-9139. Enable parallel JUnit tests for HDFS Pre-commit
(Chris Nauroth and Vinayakumar B via vinayakumarb)
HDFS-9145. Tracking methods that hold FSNamesytemLock for too long.
(Mingliang Liu via wheat9)
BUG FIXES
HDFS-8091: ACLStatus and XAttributes should be presented to

View File

@ -1443,6 +1443,11 @@ public class FSNamesystem implements Namesystem, FSNamesystemMBean,
return Util.stringCollectionAsURIs(dirNames);
}
/** Threshold (ms) for long holding write lock report. */
static final short WRITELOCK_REPORTING_THRESHOLD = 1000;
/** Last time stamp for write lock. Keep the longest one for multi-entrance.*/
private long writeLockHeldTimeStamp;
@Override
public void readLock() {
this.fsLock.readLock().lock();
@ -1454,14 +1459,30 @@ public class FSNamesystem implements Namesystem, FSNamesystemMBean,
@Override
public void writeLock() {
this.fsLock.writeLock().lock();
if (fsLock.getWriteHoldCount() == 1) {
writeLockHeldTimeStamp = monotonicNow();
}
}
@Override
public void writeLockInterruptibly() throws InterruptedException {
this.fsLock.writeLock().lockInterruptibly();
if (fsLock.getWriteHoldCount() == 1) {
writeLockHeldTimeStamp = monotonicNow();
}
}
@Override
public void writeUnlock() {
final boolean needReport = fsLock.getWriteHoldCount() == 1 &&
fsLock.isWriteLockedByCurrentThread();
this.fsLock.writeLock().unlock();
if (needReport) {
long writeLockInterval = monotonicNow() - writeLockHeldTimeStamp;
if (writeLockInterval >= WRITELOCK_REPORTING_THRESHOLD) {
LOG.info("FSNamesystem write lock held for " + writeLockInterval +
" ms via\n" + StringUtils.getStackTrace(Thread.currentThread()));
}
}
}
@Override
public boolean hasWriteLock() {

View File

@ -39,6 +39,9 @@ import org.apache.hadoop.hdfs.server.namenode.FSNamesystem.SafeModeInfo;
import org.apache.hadoop.hdfs.server.namenode.ha.HAContext;
import org.apache.hadoop.hdfs.server.namenode.ha.HAState;
import org.apache.hadoop.hdfs.server.namenode.snapshot.Snapshot;
import org.apache.hadoop.test.GenericTestUtils;
import org.apache.hadoop.test.GenericTestUtils.LogCapturer;
import org.apache.log4j.Level;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
@ -268,6 +271,59 @@ public class TestFSNamesystem {
threadCount, rwLock.getQueueLength());
}
/**
* Test when FSNamesystem lock is held for a long time, logger will report it.
*/
@Test(timeout=45000)
public void testFSLockLongHoldingReport() throws Exception {
Configuration conf = new Configuration();
FSImage fsImage = Mockito.mock(FSImage.class);
FSEditLog fsEditLog = Mockito.mock(FSEditLog.class);
Mockito.when(fsImage.getEditLog()).thenReturn(fsEditLog);
FSNamesystem fsn = new FSNamesystem(conf, fsImage);
LogCapturer logs = LogCapturer.captureLogs(FSNamesystem.LOG);
GenericTestUtils.setLogLevel(FSNamesystem.LOG, Level.INFO);
// Don't report if the write lock is held for a short time
fsn.writeLock();
Thread.sleep(FSNamesystem.WRITELOCK_REPORTING_THRESHOLD / 2);
fsn.writeUnlock();
assertFalse(logs.getOutput().contains(GenericTestUtils.getMethodName()));
// Report if the write lock is held for a long time
fsn.writeLock();
Thread.sleep(FSNamesystem.WRITELOCK_REPORTING_THRESHOLD + 100);
logs.clearOutput();
fsn.writeUnlock();
assertTrue(logs.getOutput().contains(GenericTestUtils.getMethodName()));
// Report if the write lock is held (interruptibly) for a long time
fsn.writeLockInterruptibly();
Thread.sleep(FSNamesystem.WRITELOCK_REPORTING_THRESHOLD + 100);
logs.clearOutput();
fsn.writeUnlock();
assertTrue(logs.getOutput().contains(GenericTestUtils.getMethodName()));
// Report if it's held for a long time when re-entering write lock
fsn.writeLock();
Thread.sleep(FSNamesystem.WRITELOCK_REPORTING_THRESHOLD / 2 + 1);
fsn.writeLockInterruptibly();
Thread.sleep(FSNamesystem.WRITELOCK_REPORTING_THRESHOLD / 2 + 1);
fsn.writeLock();
Thread.sleep(FSNamesystem.WRITELOCK_REPORTING_THRESHOLD / 2);
logs.clearOutput();
fsn.writeUnlock();
assertFalse(logs.getOutput().contains(GenericTestUtils.getMethodName()));
logs.clearOutput();
fsn.writeUnlock();
assertFalse(logs.getOutput().contains(GenericTestUtils.getMethodName()));
logs.clearOutput();
fsn.writeUnlock();
assertTrue(logs.getOutput().contains(GenericTestUtils.getMethodName()));
}
@Test
public void testSafemodeReplicationConf() throws IOException {
Configuration conf = new Configuration();