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:37 +08:00
parent 10d65ee37f
commit f61f09cdf2
2 changed files with 205 additions and 0 deletions

View File

@ -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(); }
};
}
}

View File

@ -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 {