HBASE-18471 The DeleteFamily cell is skipped when StoreScanner seeks to next column
This commit is contained in:
parent
10d65ee37f
commit
f61f09cdf2
|
@ -21,6 +21,7 @@ import java.io.IOException;
|
|||
|
||||
import org.apache.hadoop.hbase.Cell;
|
||||
import org.apache.hadoop.hbase.HConstants;
|
||||
import org.apache.hadoop.hbase.KeyValue;
|
||||
import org.apache.hadoop.hbase.KeyValue.KVComparator;
|
||||
import org.apache.hadoop.hbase.KeyValue.Type;
|
||||
import org.apache.hadoop.hbase.KeyValueUtil;
|
||||
|
@ -249,6 +250,19 @@ public abstract class ScanQueryMatcher {
|
|||
public abstract boolean moreRowsMayExistAfter(Cell cell);
|
||||
|
||||
public Cell getKeyForNextColumn(Cell cell) {
|
||||
// We aren't sure whether any DeleteFamily cells exist, so we can't skip to next column.
|
||||
// TODO: Current way disable us to seek to next column quickly. Is there any better solution?
|
||||
// see HBASE-18471 for more details
|
||||
// see TestFromClientSide3#testScanAfterDeletingSpecifiedRow
|
||||
// see TestFromClientSide3#testScanAfterDeletingSpecifiedRowV2
|
||||
if (cell.getQualifierLength() == 0) {
|
||||
Cell nextKey = createNextOnRowCol(cell);
|
||||
if (nextKey != cell) {
|
||||
return nextKey;
|
||||
}
|
||||
// The cell is at the end of row/family/qualifier, so it is impossible to find any DeleteFamily cells.
|
||||
// Let us seek to next column.
|
||||
}
|
||||
ColumnCount nextColumn = columns.getColumnHint();
|
||||
if (nextColumn == null) {
|
||||
return KeyValueUtil.createLastOnRow(cell.getRowArray(), cell.getRowOffset(),
|
||||
|
@ -322,4 +336,95 @@ public abstract class ScanQueryMatcher {
|
|||
}
|
||||
return matchCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return An new cell is located following input cell. If both of type and timestamp are
|
||||
* minimum, the input cell will be returned directly.
|
||||
*/
|
||||
private static Cell createNextOnRowCol(Cell cell) {
|
||||
long ts = cell.getTimestamp();
|
||||
byte type = cell.getTypeByte();
|
||||
if (type != Type.Minimum.getCode()) {
|
||||
type = KeyValue.Type.values()[KeyValue.Type.codeToType(type).ordinal() - 1].getCode();
|
||||
} else if (ts != HConstants.OLDEST_TIMESTAMP) {
|
||||
ts = ts - 1;
|
||||
type = Type.Maximum.getCode();
|
||||
} else {
|
||||
return cell;
|
||||
}
|
||||
return createNextOnRowCol(cell, ts, type);
|
||||
}
|
||||
|
||||
private static Cell createNextOnRowCol(final Cell cell, final long ts, final byte type) {
|
||||
return new Cell() {
|
||||
@Override
|
||||
public byte[] getRowArray() { return cell.getRowArray(); }
|
||||
|
||||
@Override
|
||||
public int getRowOffset() { return cell.getRowOffset(); }
|
||||
|
||||
@Override
|
||||
public short getRowLength() { return cell.getRowLength(); }
|
||||
|
||||
@Override
|
||||
public byte[] getFamilyArray() { return cell.getFamilyArray(); }
|
||||
|
||||
@Override
|
||||
public int getFamilyOffset() { return cell.getFamilyOffset(); }
|
||||
|
||||
@Override
|
||||
public byte getFamilyLength() { return cell.getFamilyLength(); }
|
||||
|
||||
@Override
|
||||
public byte[] getQualifierArray() { return cell.getQualifierArray(); }
|
||||
|
||||
@Override
|
||||
public int getQualifierOffset() { return cell.getQualifierOffset(); }
|
||||
|
||||
@Override
|
||||
public int getQualifierLength() { return cell.getQualifierLength(); }
|
||||
|
||||
@Override
|
||||
public long getTimestamp() { return ts; }
|
||||
|
||||
@Override
|
||||
public byte getTypeByte() {return type; }
|
||||
|
||||
@Override
|
||||
public long getMvccVersion() { return cell.getMvccVersion(); }
|
||||
|
||||
@Override
|
||||
public long getSequenceId() { return cell.getSequenceId(); }
|
||||
|
||||
@Override
|
||||
public byte[] getValueArray() { return cell.getValueArray(); }
|
||||
|
||||
@Override
|
||||
public int getValueOffset() { return cell.getValueOffset(); }
|
||||
|
||||
@Override
|
||||
public int getValueLength() { return cell.getValueLength(); }
|
||||
|
||||
@Override
|
||||
public byte[] getTagsArray() { return cell.getTagsArray(); }
|
||||
|
||||
@Override
|
||||
public int getTagsOffset() { return cell.getTagsOffset(); }
|
||||
|
||||
@Override
|
||||
public int getTagsLength() { return cell.getTagsLength(); }
|
||||
|
||||
@Override
|
||||
public byte[] getValue() { return cell.getValue(); }
|
||||
|
||||
@Override
|
||||
public byte[] getFamily() { return cell.getFamily(); }
|
||||
|
||||
@Override
|
||||
public byte[] getQualifier() { return cell.getQualifier(); }
|
||||
|
||||
@Override
|
||||
public byte[] getRow() {return cell.getRow(); }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,8 @@ import static junit.framework.Assert.assertFalse;
|
|||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.hbase.Cell;
|
||||
import org.apache.hadoop.hbase.CellUtil;
|
||||
import org.apache.hadoop.hbase.HBaseTestingUtility;
|
||||
import org.apache.hadoop.hbase.HColumnDescriptor;
|
||||
import org.apache.hadoop.hbase.HConstants;
|
||||
|
@ -159,6 +161,104 @@ public class TestFromClientSide3 {
|
|||
}
|
||||
}
|
||||
|
||||
private static List<Cell> toList(ResultScanner scanner) {
|
||||
try {
|
||||
List<Cell> cells = new ArrayList<>();
|
||||
for (Result r : scanner) {
|
||||
cells.addAll(r.listCells());
|
||||
}
|
||||
return cells;
|
||||
} finally {
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScanAfterDeletingSpecifiedRow() throws IOException {
|
||||
TableName tableName = TableName.valueOf("testScanAfterDeletingSpecifiedRow");
|
||||
HTableDescriptor desc = new HTableDescriptor(tableName);
|
||||
desc.addFamily(new HColumnDescriptor(FAMILY));
|
||||
TEST_UTIL.getHBaseAdmin().createTable(desc);
|
||||
byte[] row = Bytes.toBytes("SpecifiedRow");
|
||||
byte[] value0 = Bytes.toBytes("value_0");
|
||||
byte[] value1 = Bytes.toBytes("value_1");
|
||||
try (Table t = TEST_UTIL.getConnection().getTable(tableName)) {
|
||||
Put put = new Put(row);
|
||||
put.addColumn(FAMILY, QUALIFIER, VALUE);
|
||||
t.put(put);
|
||||
Delete d = new Delete(row);
|
||||
t.delete(d);
|
||||
put = new Put(row);
|
||||
put.addColumn(FAMILY, null, value0);
|
||||
t.put(put);
|
||||
put = new Put(row);
|
||||
put.addColumn(FAMILY, null, value1);
|
||||
t.put(put);
|
||||
List<Cell> cells = toList(t.getScanner(new Scan()));
|
||||
assertEquals(1, cells.size());
|
||||
assertEquals("value_1", Bytes.toString(CellUtil.cloneValue(cells.get(0))));
|
||||
|
||||
cells = toList(t.getScanner(new Scan().addFamily(FAMILY)));
|
||||
assertEquals(1, cells.size());
|
||||
assertEquals("value_1", Bytes.toString(CellUtil.cloneValue(cells.get(0))));
|
||||
|
||||
cells = toList(t.getScanner(new Scan().addColumn(FAMILY, QUALIFIER)));
|
||||
assertEquals(0, cells.size());
|
||||
|
||||
TEST_UTIL.getHBaseAdmin().flush(tableName);
|
||||
cells = toList(t.getScanner(new Scan()));
|
||||
assertEquals(1, cells.size());
|
||||
assertEquals("value_1", Bytes.toString(CellUtil.cloneValue(cells.get(0))));
|
||||
|
||||
cells = toList(t.getScanner(new Scan().addFamily(FAMILY)));
|
||||
assertEquals(1, cells.size());
|
||||
assertEquals("value_1", Bytes.toString(CellUtil.cloneValue(cells.get(0))));
|
||||
|
||||
cells = toList(t.getScanner(new Scan().addColumn(FAMILY, QUALIFIER)));
|
||||
assertEquals(0, cells.size());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testScanAfterDeletingSpecifiedRowV2() throws IOException {
|
||||
TableName tableName = TableName.valueOf("testScanAfterDeletingSpecifiedRowV2");
|
||||
HTableDescriptor desc = new HTableDescriptor(tableName);
|
||||
desc.addFamily(new HColumnDescriptor(FAMILY));
|
||||
TEST_UTIL.getHBaseAdmin().createTable(desc);
|
||||
byte[] row = Bytes.toBytes("SpecifiedRow");
|
||||
byte[] qual0 = Bytes.toBytes("qual0");
|
||||
byte[] qual1 = Bytes.toBytes("qual1");
|
||||
try (Table t = TEST_UTIL.getConnection().getTable(tableName)) {
|
||||
Delete d = new Delete(row);
|
||||
t.delete(d);
|
||||
|
||||
Put put = new Put(row);
|
||||
put.addColumn(FAMILY, null, VALUE);
|
||||
t.put(put);
|
||||
|
||||
put = new Put(row);
|
||||
put.addColumn(FAMILY, qual1, qual1);
|
||||
t.put(put);
|
||||
|
||||
put = new Put(row);
|
||||
put.addColumn(FAMILY, qual0, qual0);
|
||||
t.put(put);
|
||||
|
||||
Result r = t.get(new Get(row));
|
||||
assertEquals(3, r.size());
|
||||
assertEquals("testValue", Bytes.toString(CellUtil.cloneValue(r.rawCells()[0])));
|
||||
assertEquals("qual0", Bytes.toString(CellUtil.cloneValue(r.rawCells()[1])));
|
||||
assertEquals("qual1", Bytes.toString(CellUtil.cloneValue(r.rawCells()[2])));
|
||||
|
||||
TEST_UTIL.getHBaseAdmin().flush(tableName);
|
||||
r = t.get(new Get(row));
|
||||
assertEquals(3, r.size());
|
||||
assertEquals("testValue", Bytes.toString(CellUtil.cloneValue(r.rawCells()[0])));
|
||||
assertEquals("qual0", Bytes.toString(CellUtil.cloneValue(r.rawCells()[1])));
|
||||
assertEquals("qual1", Bytes.toString(CellUtil.cloneValue(r.rawCells()[2])));
|
||||
}
|
||||
}
|
||||
|
||||
// override the config settings at the CF level and ensure priority
|
||||
@Test(timeout = 60000)
|
||||
public void testAdvancedConfigOverride() throws Exception {
|
||||
|
|
Loading…
Reference in New Issue