HBASE-2964 Deadlock when RS tries to RPC to itself inside SplitTransaction

git-svn-id: https://svn.apache.org/repos/asf/hbase/trunk@995151 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Michael Stack 2010-09-08 16:57:52 +00:00
parent d98956f9cd
commit b89eb144d1
4 changed files with 21 additions and 47 deletions

View File

@ -506,6 +506,7 @@ Release 0.21.0 - Unreleased
HBASE-2925 LRU of HConnectionManager.HBASE_INSTANCES breaks if HBASE-2925 LRU of HConnectionManager.HBASE_INSTANCES breaks if
HBaseConfiguration is changed HBaseConfiguration is changed
(Robert Mahfoud via Stack) (Robert Mahfoud via Stack)
HBASE-2964 Deadlock when RS tries to RPC to itself inside SplitTransaction
IMPROVEMENTS IMPROVEMENTS
HBASE-1760 Cleanup TODOs in HTable HBASE-1760 Cleanup TODOs in HTable

View File

@ -214,7 +214,7 @@ public class HRegion implements HeapSize { // , Writable{
final FlushRequester flushRequester; final FlushRequester flushRequester;
private final long blockingMemStoreSize; private final long blockingMemStoreSize;
final long threadWakeFrequency; final long threadWakeFrequency;
// Used to guard splits and closes // Used to guard closes
final ReentrantReadWriteLock lock = final ReentrantReadWriteLock lock =
new ReentrantReadWriteLock(); new ReentrantReadWriteLock();

View File

@ -61,6 +61,8 @@ import org.apache.zookeeper.KeeperException;
* } * }
* } * }
* </Pre> * </Pre>
* <p>This class is not thread safe. Caller needs ensure split is run by
* one thread only.
*/ */
class SplitTransaction { class SplitTransaction {
private static final Log LOG = LogFactory.getLog(SplitTransaction.class); private static final Log LOG = LogFactory.getLog(SplitTransaction.class);
@ -127,16 +129,10 @@ class SplitTransaction {
/** /**
* Does checks on split inputs. * Does checks on split inputs.
* @return <code>true</code> if the region is splittable else * @return <code>true</code> if the region is splittable else
* <code>false</code> if it is not (e.g. its already closed, etc.). If we * <code>false</code> if it is not (e.g. its already closed, etc.).
* return <code>true</code>, we'll have taken out the parent's
* <code>splitsAndClosesLock</code> and only way to unlock is successful
* {@link #execute(OnlineRegions)} or {@link #rollback(OnlineRegions)}
*/ */
public boolean prepare() { public boolean prepare() {
boolean prepared = false; if (this.parent.isClosed() || this.parent.isClosing()) return false;
this.parent.lock.writeLock().lock();
try {
if (this.parent.isClosed() || this.parent.isClosing()) return prepared;
HRegionInfo hri = this.parent.getRegionInfo(); HRegionInfo hri = this.parent.getRegionInfo();
// Check splitrow. // Check splitrow.
byte [] startKey = hri.getStartKey(); byte [] startKey = hri.getStartKey();
@ -145,18 +141,14 @@ class SplitTransaction {
!this.parent.getRegionInfo().containsRow(splitrow)) { !this.parent.getRegionInfo().containsRow(splitrow)) {
LOG.info("Split row is not inside region key range or is equal to " + LOG.info("Split row is not inside region key range or is equal to " +
"startkey: " + Bytes.toString(this.splitrow)); "startkey: " + Bytes.toString(this.splitrow));
return prepared; return false;
} }
long rid = getDaughterRegionIdTimestamp(hri); long rid = getDaughterRegionIdTimestamp(hri);
this.hri_a = new HRegionInfo(hri.getTableDesc(), startKey, this.splitrow, this.hri_a = new HRegionInfo(hri.getTableDesc(), startKey, this.splitrow,
false, rid); false, rid);
this.hri_b = new HRegionInfo(hri.getTableDesc(), this.splitrow, endKey, this.hri_b = new HRegionInfo(hri.getTableDesc(), this.splitrow, endKey,
false, rid); false, rid);
prepared = true; return true;
} finally {
if (!prepared) this.parent.lock.writeLock().unlock();
}
return prepared;
} }
/** /**
@ -188,9 +180,7 @@ class SplitTransaction {
final RegionServerServices services) final RegionServerServices services)
throws IOException { throws IOException {
LOG.info("Starting split of region " + this.parent); LOG.info("Starting split of region " + this.parent);
if (!this.parent.lock.writeLock().isHeldByCurrentThread()) { assert !this.parent.lock.writeLock().isHeldByCurrentThread() : "Unsafe to hold write lock while performing RPCs";
throw new SplitAndCloseWriteLockNotHeld();
}
// If true, no cluster to write meta edits into. // If true, no cluster to write meta edits into.
boolean testing = boolean testing =
@ -251,9 +241,6 @@ class SplitTransaction {
} }
} }
// Unlock if successful split.
this.parent.lock.writeLock().unlock();
// Leaving here, the splitdir with its dross will be in place but since the // Leaving here, the splitdir with its dross will be in place but since the
// split was successful, just leave it; it'll be cleaned when parent is // split was successful, just leave it; it'll be cleaned when parent is
// deleted and cleaned up. // deleted and cleaned up.
@ -441,9 +428,6 @@ class SplitTransaction {
* @throws IOException If thrown, rollback failed. Take drastic action. * @throws IOException If thrown, rollback failed. Take drastic action.
*/ */
public void rollback(final OnlineRegions or) throws IOException { public void rollback(final OnlineRegions or) throws IOException {
if (!this.parent.lock.writeLock().isHeldByCurrentThread()) {
throw new SplitAndCloseWriteLockNotHeld();
}
FileSystem fs = this.parent.getFilesystem(); FileSystem fs = this.parent.getFilesystem();
ListIterator<JournalEntry> iterator = ListIterator<JournalEntry> iterator =
this.journal.listIterator(this.journal.size()); this.journal.listIterator(this.journal.size());
@ -481,16 +465,7 @@ class SplitTransaction {
throw new RuntimeException("Unhandled journal entry: " + je); throw new RuntimeException("Unhandled journal entry: " + je);
} }
} }
if (this.parent.lock.writeLock().isHeldByCurrentThread()) {
this.parent.lock.writeLock().unlock();
} }
}
/**
* Thrown if lock not held.
*/
@SuppressWarnings("serial")
public class SplitAndCloseWriteLockNotHeld extends IOException {}
HRegionInfo getFirstDaughter() { HRegionInfo getFirstDaughter() {
return hri_a; return hri_a;

View File

@ -95,8 +95,6 @@ public class TestSplitTransaction {
private SplitTransaction prepareGOOD_SPLIT_ROW() { private SplitTransaction prepareGOOD_SPLIT_ROW() {
SplitTransaction st = new SplitTransaction(this.parent, GOOD_SPLIT_ROW); SplitTransaction st = new SplitTransaction(this.parent, GOOD_SPLIT_ROW);
assertTrue(st.prepare()); assertTrue(st.prepare());
// Assert the write lock is held on successful prepare as the javadoc asserts.
assertTrue(this.parent.lock.writeLock().isHeldByCurrentThread());
return st; return st;
} }