HBASE-2467 Concurrent flushers in HLog sync using HDFS-895

git-svn-id: https://svn.apache.org/repos/asf/hbase/trunk@1042790 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Jean-Daniel Cryans 2010-12-06 20:58:53 +00:00
parent 8d5248d3d9
commit a5ce594797
3 changed files with 45 additions and 116 deletions

View File

@ -1227,6 +1227,7 @@ Release 0.90.0 - Unreleased
HBASE-3223 Get VersionInfo for Running HBase Process HBASE-3223 Get VersionInfo for Running HBase Process
(Nicolas Spiegelberg via Stack) (Nicolas Spiegelberg via Stack)
HBASE-3303 Lower hbase.regionserver.handler.count from 25 back to 10 HBASE-3303 Lower hbase.regionserver.handler.count from 25 back to 10
HBASE-2467 Concurrent flushers in HLog sync using HDFS-895
NEW FEATURES NEW FEATURES

View File

@ -128,8 +128,8 @@ public class HLog implements Syncable {
private final long blocksize; private final long blocksize;
private final int flushlogentries; private final int flushlogentries;
private final String prefix; private final String prefix;
private final AtomicInteger unflushedEntries = new AtomicInteger(0);
private final Path oldLogDir; private final Path oldLogDir;
private boolean logRollRequested;
private static Class<? extends Writer> logWriterClass; private static Class<? extends Writer> logWriterClass;
@ -202,6 +202,7 @@ public class HLog implements Syncable {
// We synchronize on updateLock to prevent updates and to prevent a log roll // We synchronize on updateLock to prevent updates and to prevent a log roll
// during an update // during an update
// locked during appends
private final Object updateLock = new Object(); private final Object updateLock = new Object();
private final boolean enabled; private final boolean enabled;
@ -214,7 +215,7 @@ public class HLog implements Syncable {
private final int maxLogs; private final int maxLogs;
/** /**
* Thread that handles group commit * Thread that handles optional sync'ing
*/ */
private final LogSyncer logSyncerThread; private final LogSyncer logSyncerThread;
@ -506,6 +507,7 @@ public class HLog implements Syncable {
this.fs.getFileStatus(oldFile).getLen() + ". ": "") + this.fs.getFileStatus(oldFile).getLen() + ". ": "") +
"New hlog " + FSUtils.getPath(newPath)); "New hlog " + FSUtils.getPath(newPath));
this.numEntries.set(0); this.numEntries.set(0);
this.logRollRequested = false;
} }
// Tell our listeners that a new log was created // Tell our listeners that a new log was created
if (!this.listeners.isEmpty()) { if (!this.listeners.isEmpty()) {
@ -870,7 +872,6 @@ public class HLog implements Syncable {
this.lastSeqWritten.putIfAbsent(regionInfo.getEncodedNameAsBytes(), this.lastSeqWritten.putIfAbsent(regionInfo.getEncodedNameAsBytes(),
Long.valueOf(seqNum)); Long.valueOf(seqNum));
doWrite(regionInfo, logKey, logEdit); doWrite(regionInfo, logKey, logEdit);
this.unflushedEntries.incrementAndGet();
this.numEntries.incrementAndGet(); this.numEntries.incrementAndGet();
} }
@ -927,9 +928,6 @@ public class HLog implements Syncable {
HLogKey logKey = makeKey(hriKey, tableName, seqNum, now); HLogKey logKey = makeKey(hriKey, tableName, seqNum, now);
doWrite(info, logKey, edits); doWrite(info, logKey, edits);
this.numEntries.incrementAndGet(); this.numEntries.incrementAndGet();
// Only count 1 row as an unflushed entry.
this.unflushedEntries.incrementAndGet();
} }
// Sync if catalog region, and if not then check if that table supports // Sync if catalog region, and if not then check if that table supports
// deferred log flushing // deferred log flushing
@ -946,15 +944,6 @@ public class HLog implements Syncable {
*/ */
class LogSyncer extends Thread { class LogSyncer extends Thread {
// Using fairness to make sure locks are given in order
private final ReentrantLock lock = new ReentrantLock(true);
// Condition used to wait until we have something to sync
private final Condition queueEmpty = lock.newCondition();
// Condition used to signal that the sync is done
private final Condition syncDone = lock.newCondition();
private final long optionalFlushInterval; private final long optionalFlushInterval;
private boolean syncerShuttingDown = false; private boolean syncerShuttingDown = false;
@ -966,28 +955,12 @@ public class HLog implements Syncable {
@Override @Override
public void run() { public void run() {
try { try {
lock.lock();
// awaiting with a timeout doesn't always // awaiting with a timeout doesn't always
// throw exceptions on interrupt // throw exceptions on interrupt
while(!this.isInterrupted()) { while(!this.isInterrupted()) {
// Wait until something has to be hflushed or do it if we waited Thread.sleep(this.optionalFlushInterval);
// enough time (useful if something appends but does not hflush). sync();
// 0 or less means that it timed out and maybe waited a bit more.
if (!(queueEmpty.awaitNanos(
this.optionalFlushInterval*1000000) <= 0)) {
forceSync = true;
}
// We got the signal, let's hflush. We currently own the lock so new
// writes are waiting to acquire it in addToSyncQueue while the ones
// we hflush are waiting on await()
hflush();
// Release all the clients waiting on the hflush. Notice that we still
// own the lock until we get back to await at which point all the
// other threads waiting will first acquire and release locks
syncDone.signalAll();
} }
} catch (IOException e) { } catch (IOException e) {
LOG.error("Error while syncing, requesting close of hlog ", e); LOG.error("Error while syncing, requesting close of hlog ", e);
@ -996,74 +969,40 @@ public class HLog implements Syncable {
LOG.debug(getName() + " interrupted while waiting for sync requests"); LOG.debug(getName() + " interrupted while waiting for sync requests");
} finally { } finally {
syncerShuttingDown = true; syncerShuttingDown = true;
syncDone.signalAll();
lock.unlock();
LOG.info(getName() + " exiting"); LOG.info(getName() + " exiting");
} }
} }
/**
* This method first signals the thread that there's a sync needed
* and then waits for it to happen before returning.
*/
public void addToSyncQueue(boolean force) {
// Don't bother if somehow our append was already hflushed
if (unflushedEntries.get() == 0) {
return;
}
lock.lock();
try {
if (syncerShuttingDown) {
LOG.warn(getName() + " was shut down while waiting for sync");
return;
}
if(force) {
forceSync = true;
}
// Wake the thread
queueEmpty.signal();
// Wait for it to hflush
syncDone.await();
} catch (InterruptedException e) {
LOG.debug(getName() + " was interrupted while waiting for sync", e);
}
finally {
lock.unlock();
}
}
} }
public void sync(){ public void sync() throws IOException {
sync(false);
}
/**
* This method calls the LogSyncer in order to group commit the sync
* with other threads.
* @param force For catalog regions, force the sync to happen
*/
public void sync(boolean force) {
logSyncerThread.addToSyncQueue(force);
}
public void hflush() throws IOException {
synchronized (this.updateLock) { synchronized (this.updateLock) {
if (this.closed) { if (this.closed) {
return; return;
} }
boolean logRollRequested = false; }
if (this.forceSync ||
this.unflushedEntries.get() >= this.flushlogentries) {
try { try {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
// Done in parallel for all writer threads, thanks to HDFS-895
this.writer.sync(); this.writer.sync();
synchronized (this.updateLock) {
syncTime += System.currentTimeMillis() - now; syncTime += System.currentTimeMillis() - now;
syncOps++; syncOps++;
this.forceSync = false; if (!logRollRequested) {
this.unflushedEntries.set(0); checkLowReplication();
if (this.writer.getLength() > this.logrollsize) {
requestLogRoll();
}
}
}
} catch (IOException e) {
LOG.fatal("Could not append. Requesting close of hlog", e);
requestLogRoll();
throw e;
}
}
private void checkLowReplication() {
// if the number of replicas in HDFS has fallen below the initial // if the number of replicas in HDFS has fallen below the initial
// value, then roll logs. // value, then roll logs.
try { try {
@ -1081,17 +1020,6 @@ public class HLog implements Syncable {
LOG.warn("Unable to invoke DFSOutputStream.getNumCurrentReplicas" + e + LOG.warn("Unable to invoke DFSOutputStream.getNumCurrentReplicas" + e +
" still proceeding ahead..."); " still proceeding ahead...");
} }
} catch (IOException e) {
LOG.fatal("Could not append. Requesting close of hlog", e);
requestLogRoll();
throw e;
}
}
if (!logRollRequested && (this.writer.getLength() > this.logrollsize)) {
requestLogRoll();
}
}
} }
/** /**
@ -1122,7 +1050,7 @@ public class HLog implements Syncable {
public void hsync() throws IOException { public void hsync() throws IOException {
// Not yet implemented up in hdfs so just call hflush. // Not yet implemented up in hdfs so just call hflush.
hflush(); sync();
} }
private void requestLogRoll() { private void requestLogRoll() {
@ -1233,7 +1161,7 @@ public class HLog implements Syncable {
} }
} }
// sync txn to file system // sync txn to file system
this.sync(isMetaRegion); this.sync();
} finally { } finally {
this.cacheFlushLock.unlock(); this.cacheFlushLock.unlock();

View File

@ -341,7 +341,7 @@ public class TestHLog {
wal.append(regioninfo, tableName, kvs, System.currentTimeMillis()); wal.append(regioninfo, tableName, kvs, System.currentTimeMillis());
} }
// Now call sync to send the data to HDFS datanodes // Now call sync to send the data to HDFS datanodes
wal.sync(true); wal.sync();
int namenodePort = cluster.getNameNodePort(); int namenodePort = cluster.getNameNodePort();
final Path walPath = wal.computeFilename(); final Path walPath = wal.computeFilename();