HBASE-18471 The DeleteFamily cell is skipped when StoreScanner seeks to next column
This commit is contained in:
parent
c013bf8a7a
commit
bb2b6b8662
|
@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue