HBASE-11813 CellScanner#advance may overflow stack
This commit is contained in:
parent
465d7d85a6
commit
2c5a2e854b
|
@ -201,13 +201,14 @@ public final class CellUtil {
|
|||
|
||||
@Override
|
||||
public boolean advance() throws IOException {
|
||||
while (true) {
|
||||
if (this.cellScanner == null) {
|
||||
if (!this.iterator.hasNext()) return false;
|
||||
this.cellScanner = this.iterator.next().cellScanner();
|
||||
}
|
||||
if (this.cellScanner.advance()) return true;
|
||||
this.cellScanner = null;
|
||||
return advance();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -275,11 +276,9 @@ public final class CellUtil {
|
|||
* inside Put, etc., keeping Cells organized by family.
|
||||
* @return CellScanner interface over <code>cellIterable</code>
|
||||
*/
|
||||
public static CellScanner createCellScanner(final NavigableMap<byte [],
|
||||
List<Cell>> map) {
|
||||
public static CellScanner createCellScanner(final NavigableMap<byte [], List<Cell>> map) {
|
||||
return new CellScanner() {
|
||||
private final Iterator<Entry<byte[], List<Cell>>> entries =
|
||||
map.entrySet().iterator();
|
||||
private final Iterator<Entry<byte[], List<Cell>>> entries = map.entrySet().iterator();
|
||||
private Iterator<Cell> currentIterator = null;
|
||||
private Cell currentCell;
|
||||
|
||||
|
@ -290,6 +289,7 @@ public final class CellUtil {
|
|||
|
||||
@Override
|
||||
public boolean advance() {
|
||||
while(true) {
|
||||
if (this.currentIterator == null) {
|
||||
if (!this.entries.hasNext()) return false;
|
||||
this.currentIterator = this.entries.next().getValue().iterator();
|
||||
|
@ -300,7 +300,7 @@ public final class CellUtil {
|
|||
}
|
||||
this.currentCell = null;
|
||||
this.currentIterator = null;
|
||||
return advance();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -18,6 +18,12 @@
|
|||
|
||||
package org.apache.hadoop.hbase;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.NavigableMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.apache.hadoop.hbase.util.Bytes;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
@ -25,6 +31,250 @@ import org.junit.experimental.categories.Category;
|
|||
|
||||
@Category(SmallTests.class)
|
||||
public class TestCellUtil {
|
||||
/**
|
||||
* CellScannable used in test. Returns a {@link TestCellScanner}
|
||||
*/
|
||||
private class TestCellScannable implements CellScannable {
|
||||
private final int cellsCount;
|
||||
TestCellScannable(final int cellsCount) {
|
||||
this.cellsCount = cellsCount;
|
||||
}
|
||||
@Override
|
||||
public CellScanner cellScanner() {
|
||||
return new TestCellScanner(this.cellsCount);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* CellScanner used in test.
|
||||
*/
|
||||
private class TestCellScanner implements CellScanner {
|
||||
private int count = 0;
|
||||
private Cell current = null;
|
||||
private final int cellsCount;
|
||||
|
||||
TestCellScanner(final int cellsCount) {
|
||||
this.cellsCount = cellsCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cell current() {
|
||||
return this.current;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean advance() throws IOException {
|
||||
if (this.count < cellsCount) {
|
||||
this.current = new TestCell(this.count);
|
||||
this.count++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cell used in test. Has row only.
|
||||
*/
|
||||
private class TestCell implements Cell {
|
||||
private final byte [] row;
|
||||
|
||||
TestCell(final int i) {
|
||||
this.row = Bytes.toBytes(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getRowArray() {
|
||||
return this.row;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowOffset() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getRowLength() {
|
||||
return (short)this.row.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getFamilyArray() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getFamilyOffset() {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getFamilyLength() {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getQualifierArray() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getQualifierOffset() {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getQualifierLength() {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getTimestamp() {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getTypeByte() {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getMvccVersion() {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getValueArray() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getValueOffset() {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getValueLength() {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getTagsArray() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTagsOffset() {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getValue() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getFamily() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getQualifier() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getRow() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getSequenceId() {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTagsLength() {
|
||||
// TODO Auto-generated method stub
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Was overflowing if 100k or so lists of cellscanners to return.
|
||||
* @throws IOException
|
||||
*/
|
||||
@Test
|
||||
public void testCreateCellScannerOverflow() throws IOException {
|
||||
consume(doCreateCellScanner(1, 1), 1 * 1);
|
||||
consume(doCreateCellScanner(3, 0), 3 * 0);
|
||||
consume(doCreateCellScanner(3, 3), 3 * 3);
|
||||
consume(doCreateCellScanner(0, 1), 0 * 1);
|
||||
// Do big number. See HBASE-11813 for why.
|
||||
final int hundredK = 100000;
|
||||
consume(doCreateCellScanner(hundredK, 0), hundredK * 0);
|
||||
consume(doCreateCellArray(1), 1);
|
||||
consume(doCreateCellArray(0), 0);
|
||||
consume(doCreateCellArray(3), 3);
|
||||
List<CellScannable> cells = new ArrayList<CellScannable>(hundredK);
|
||||
for (int i = 0; i < hundredK; i++) {
|
||||
cells.add(new TestCellScannable(1));
|
||||
}
|
||||
consume(CellUtil.createCellScanner(cells), hundredK * 1);
|
||||
NavigableMap<byte [], List<Cell>> m = new TreeMap<byte [], List<Cell>>(Bytes.BYTES_COMPARATOR);
|
||||
List<Cell> cellArray = new ArrayList<Cell>(hundredK);
|
||||
for (int i = 0; i < hundredK; i++) cellArray.add(new TestCell(i));
|
||||
m.put(new byte [] {'f'}, cellArray);
|
||||
consume(CellUtil.createCellScanner(m), hundredK * 1);
|
||||
}
|
||||
|
||||
private CellScanner doCreateCellArray(final int itemsPerList) {
|
||||
Cell [] cells = new Cell [itemsPerList];
|
||||
for (int i = 0; i < itemsPerList; i++) {
|
||||
cells[i] = new TestCell(i);
|
||||
}
|
||||
return CellUtil.createCellScanner(cells);
|
||||
}
|
||||
|
||||
private CellScanner doCreateCellScanner(final int listsCount, final int itemsPerList)
|
||||
throws IOException {
|
||||
List<CellScannable> cells = new ArrayList<CellScannable>(listsCount);
|
||||
for (int i = 0; i < listsCount; i++) {
|
||||
CellScannable cs = new CellScannable() {
|
||||
@Override
|
||||
public CellScanner cellScanner() {
|
||||
return new TestCellScanner(itemsPerList);
|
||||
}
|
||||
};
|
||||
cells.add(cs);
|
||||
}
|
||||
return CellUtil.createCellScanner(cells);
|
||||
}
|
||||
|
||||
private void consume(final CellScanner scanner, final int expected) throws IOException {
|
||||
int count = 0;
|
||||
while (scanner.advance()) count++;
|
||||
Assert.assertEquals(expected, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverlappingKeys() {
|
||||
|
|
|
@ -139,7 +139,7 @@ public class CallRunner {
|
|||
RpcServer.LOG.warn(Thread.currentThread().getName()
|
||||
+ ": caught: " + StringUtils.stringifyException(e));
|
||||
} finally {
|
||||
// regardless if succesful or not we need to reset the callQueueSize
|
||||
// regardless if successful or not we need to reset the callQueueSize
|
||||
this.rpcServer.addCallSize(call.getSize() * -1);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,21 +126,21 @@ public class SimpleRpcScheduler extends RpcScheduler {
|
|||
// multiple read/write queues
|
||||
if (callQueueType.equals(CALL_QUEUE_TYPE_DEADLINE_CONF_VALUE)) {
|
||||
CallPriorityComparator callPriority = new CallPriorityComparator(conf, this.priority);
|
||||
callExecutor = new RWQueueRpcExecutor("default", handlerCount, numCallQueues,
|
||||
callExecutor = new RWQueueRpcExecutor("RW.default", handlerCount, numCallQueues,
|
||||
callqReadShare, callqScanShare, maxQueueLength,
|
||||
BoundedPriorityBlockingQueue.class, callPriority);
|
||||
} else {
|
||||
callExecutor = new RWQueueRpcExecutor("default", handlerCount, numCallQueues,
|
||||
callExecutor = new RWQueueRpcExecutor("RW.default", handlerCount, numCallQueues,
|
||||
callqReadShare, callqScanShare, maxQueueLength);
|
||||
}
|
||||
} else {
|
||||
// multiple queues
|
||||
if (callQueueType.equals(CALL_QUEUE_TYPE_DEADLINE_CONF_VALUE)) {
|
||||
CallPriorityComparator callPriority = new CallPriorityComparator(conf, this.priority);
|
||||
callExecutor = new BalancedQueueRpcExecutor("default", handlerCount, numCallQueues,
|
||||
callExecutor = new BalancedQueueRpcExecutor("B.default", handlerCount, numCallQueues,
|
||||
BoundedPriorityBlockingQueue.class, maxQueueLength, callPriority);
|
||||
} else {
|
||||
callExecutor = new BalancedQueueRpcExecutor("default", handlerCount,
|
||||
callExecutor = new BalancedQueueRpcExecutor("B.default", handlerCount,
|
||||
numCallQueues, maxQueueLength);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue