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:
parent
d98956f9cd
commit
b89eb144d1
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue