HBASE-14497 Reverse Scan threw StackOverflow caused by readPt checking (Yerui Sun)

This commit is contained in:
tedyu 2015-09-29 14:12:24 -07:00
parent dbbef06135
commit 6534583652
3 changed files with 122 additions and 49 deletions

View File

@ -854,27 +854,35 @@ public class DefaultMemStore implements MemStore {
* specified key, then seek to the first KeyValue of previous row
*/
@Override
public synchronized boolean seekToPreviousRow(Cell key) {
Cell firstKeyOnRow = CellUtil.createFirstOnRow(key);
SortedSet<Cell> cellHead = cellSetAtCreation.headSet(firstKeyOnRow);
Cell cellSetBeforeRow = cellHead.isEmpty() ? null : cellHead.last();
SortedSet<Cell> snapshotHead = snapshotAtCreation
.headSet(firstKeyOnRow);
Cell snapshotBeforeRow = snapshotHead.isEmpty() ? null : snapshotHead
.last();
Cell lastCellBeforeRow = getHighest(cellSetBeforeRow, snapshotBeforeRow);
if (lastCellBeforeRow == null) {
theNext = null;
return false;
}
Cell firstKeyOnPreviousRow = CellUtil.createFirstOnRow(lastCellBeforeRow);
this.stopSkippingCellsIfNextRow = true;
seek(firstKeyOnPreviousRow);
this.stopSkippingCellsIfNextRow = false;
if (peek() == null
|| comparator.compareRows(peek(), firstKeyOnPreviousRow) > 0) {
return seekToPreviousRow(lastCellBeforeRow);
}
public synchronized boolean seekToPreviousRow(Cell originalKey) {
boolean keepSeeking = false;
Cell key = originalKey;
do {
Cell firstKeyOnRow = CellUtil.createFirstOnRow(key);
SortedSet<Cell> cellHead = cellSetAtCreation.headSet(firstKeyOnRow);
Cell cellSetBeforeRow = cellHead.isEmpty() ? null : cellHead.last();
SortedSet<Cell> snapshotHead = snapshotAtCreation
.headSet(firstKeyOnRow);
Cell snapshotBeforeRow = snapshotHead.isEmpty() ? null : snapshotHead
.last();
Cell lastCellBeforeRow = getHighest(cellSetBeforeRow, snapshotBeforeRow);
if (lastCellBeforeRow == null) {
theNext = null;
return false;
}
Cell firstKeyOnPreviousRow = CellUtil.createFirstOnRow(lastCellBeforeRow);
this.stopSkippingCellsIfNextRow = true;
seek(firstKeyOnPreviousRow);
this.stopSkippingCellsIfNextRow = false;
if (peek() == null
|| comparator.compareRows(peek(), firstKeyOnPreviousRow) > 0) {
keepSeeking = true;
key = firstKeyOnPreviousRow;
continue;
} else {
keepSeeking = false;
}
} while (keepSeeking);
return true;
}

View File

@ -428,37 +428,44 @@ public class StoreFileScanner implements KeyValueScanner {
}
@Override
public boolean seekToPreviousRow(Cell key) throws IOException {
public boolean seekToPreviousRow(Cell originalKey) throws IOException {
try {
try {
Cell seekKey = CellUtil.createFirstOnRow(key);
if (seekCount != null) seekCount.incrementAndGet();
if (!hfs.seekBefore(seekKey)) {
this.cur = null;
return false;
}
Cell curCell = hfs.getCell();
Cell firstKeyOfPreviousRow = CellUtil.createFirstOnRow(curCell);
boolean keepSeeking = false;
Cell key = originalKey;
do {
Cell seekKey = CellUtil.createFirstOnRow(key);
if (seekCount != null) seekCount.incrementAndGet();
if (!hfs.seekBefore(seekKey)) {
this.cur = null;
return false;
}
Cell curCell = hfs.getCell();
Cell firstKeyOfPreviousRow = CellUtil.createFirstOnRow(curCell);
if (seekCount != null) seekCount.incrementAndGet();
if (!seekAtOrAfter(hfs, firstKeyOfPreviousRow)) {
this.cur = null;
return false;
}
setCurrentCell(hfs.getCell());
this.stopSkippingKVsIfNextRow = true;
boolean resultOfSkipKVs;
try {
resultOfSkipKVs = skipKVsNewerThanReadpoint();
} finally {
this.stopSkippingKVsIfNextRow = false;
}
if (!resultOfSkipKVs
|| getComparator().compareRows(cur, firstKeyOfPreviousRow) > 0) {
return seekToPreviousRow(firstKeyOfPreviousRow);
}
if (seekCount != null) seekCount.incrementAndGet();
if (!seekAtOrAfter(hfs, firstKeyOfPreviousRow)) {
this.cur = null;
return false;
}
setCurrentCell(hfs.getCell());
this.stopSkippingKVsIfNextRow = true;
boolean resultOfSkipKVs;
try {
resultOfSkipKVs = skipKVsNewerThanReadpoint();
} finally {
this.stopSkippingKVsIfNextRow = false;
}
if (!resultOfSkipKVs
|| getComparator().compareRows(cur, firstKeyOfPreviousRow) > 0) {
keepSeeking = true;
key = firstKeyOfPreviousRow;
continue;
} else {
keepSeeking = false;
}
} while (keepSeeking);
return true;
} finally {
realSeekDone = true;
@ -467,7 +474,7 @@ public class StoreFileScanner implements KeyValueScanner {
throw e;
} catch (IOException ioe) {
throw new IOException("Could not seekToPreviousRow " + this + " to key "
+ key, ioe);
+ originalKey, ioe);
}
}

View File

@ -5774,6 +5774,64 @@ public class TestHRegion {
}
}
/**
* Test for HBASE-14497: Reverse Scan threw StackOverflow caused by readPt checking
*/
@Test (timeout = 60000)
public void testReverseScanner_StackOverflow() throws IOException {
byte[] cf1 = Bytes.toBytes("CF1");
byte[][] families = {cf1};
byte[] col = Bytes.toBytes("C");
String method = this.getName();
HBaseConfiguration conf = new HBaseConfiguration();
this.region = initHRegion(tableName, method, conf, families);
try {
// setup with one storefile and one memstore, to create scanner and get an earlier readPt
Put put = new Put(Bytes.toBytes("19998"));
put.add(cf1, col, Bytes.toBytes("val"));
region.put(put);
region.flushcache(true, true);
Put put2 = new Put(Bytes.toBytes("19997"));
put2.add(cf1, col, Bytes.toBytes("val"));
region.put(put2);
Scan scan = new Scan(Bytes.toBytes("19998"));
scan.setReversed(true);
InternalScanner scanner = region.getScanner(scan);
// create one storefile contains many rows will be skipped
// to check StoreFileScanner.seekToPreviousRow
for (int i = 10000; i < 20000; i++) {
Put p = new Put(Bytes.toBytes(""+i));
p.add(cf1, col, Bytes.toBytes(""+i));
region.put(p);
}
region.flushcache(true, true);
// create one memstore contains many rows will be skipped
// to check MemStoreScanner.seekToPreviousRow
for (int i = 10000; i < 20000; i++) {
Put p = new Put(Bytes.toBytes(""+i));
p.add(cf1, col, Bytes.toBytes(""+i));
region.put(p);
}
List<Cell> currRow = new ArrayList<>();
boolean hasNext;
do {
hasNext = scanner.next(currRow);
} while (hasNext);
assertEquals(2, currRow.size());
assertEquals("19998", Bytes.toString(currRow.get(0).getRowArray(),
currRow.get(0).getRowOffset(), currRow.get(0).getRowLength()));
assertEquals("19997", Bytes.toString(currRow.get(1).getRowArray(),
currRow.get(1).getRowOffset(), currRow.get(1).getRowLength()));
} finally {
HBaseTestingUtility.closeRegionAndWAL(this.region);
this.region = null;
}
}
@Test (timeout=60000)
public void testSplitRegionWithReverseScan() throws IOException {
TableName tableName = TableName.valueOf("testSplitRegionWithReverseScan");