HBASE-18471 The DeleteFamily cell is skipped when StoreScanner seeks to next column

This commit is contained in:
Chia-Ping Tsai 2017-08-19 02:14:54 +08:00
parent c013bf8a7a
commit bb2b6b8662
3 changed files with 167 additions and 0 deletions

View File

@ -3135,4 +3135,57 @@ public final class CellUtil {
return Type.DeleteFamily.getCode();
}
}
/**
* @return An new cell is located following input cell. If both of type and timestamp are
* minimum, the input cell will be returned directly.
*/
@InterfaceAudience.Private
public 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(Cell cell, long ts, byte type) {
if (cell instanceof ByteBufferCell) {
return new LastOnRowColByteBufferCell(((ByteBufferCell) cell).getRowByteBuffer(),
((ByteBufferCell) cell).getRowPosition(), cell.getRowLength(),
((ByteBufferCell) cell).getFamilyByteBuffer(),
((ByteBufferCell) cell).getFamilyPosition(), cell.getFamilyLength(),
((ByteBufferCell) cell).getQualifierByteBuffer(),
((ByteBufferCell) cell).getQualifierPosition(), cell.getQualifierLength()) {
@Override
public long getTimestamp() {
return ts;
}
@Override
public byte getTypeByte() {
return type;
}
};
}
return new LastOnRowColCell(cell.getRowArray(), cell.getRowOffset(), cell.getRowLength(),
cell.getFamilyArray(), cell.getFamilyOffset(), cell.getFamilyLength(),
cell.getQualifierArray(), cell.getQualifierOffset(), cell.getQualifierLength()) {
@Override
public long getTimestamp() {
return ts;
}
@Override
public byte getTypeByte() {
return type;
}
};
}
}

View File

@ -290,6 +290,19 @@ public abstract class ScanQueryMatcher implements ShipperListener {
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 = CellUtil.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 CellUtil.createLastOnRowCol(cell);

View File

@ -34,6 +34,7 @@ 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.Coprocessor;
import org.apache.hadoop.hbase.coprocessor.ObserverContext;
import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
@ -159,6 +160,106 @@ 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(name.getMethodName());
TableDescriptor desc = TableDescriptorBuilder.newBuilder(tableName)
.addColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY))
.build();
TEST_UTIL.getAdmin().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.getAdmin().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(name.getMethodName());
TableDescriptor desc = TableDescriptorBuilder.newBuilder(tableName)
.addColumnFamily(ColumnFamilyDescriptorBuilder.of(FAMILY))
.build();
TEST_UTIL.getAdmin().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.getAdmin().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 {