diff --git a/CHANGES.txt b/CHANGES.txt index d6acd580870..989a13f45f4 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -469,6 +469,8 @@ Release 0.21.0 - Unreleased HBASE-2871 Make "start|stop" commands symmetric for Master & Cluster (Nicolas Spiegelberg via Stack) HBASE-2901 HBASE-2461 broke build + HBASE-2823 Entire Row Deletes not stored in Row+Col Bloom + (Alexander Georgiev via Stack) IMPROVEMENTS HBASE-1760 Cleanup TODOs in HTable diff --git a/src/main/java/org/apache/hadoop/hbase/regionserver/StoreFile.java b/src/main/java/org/apache/hadoop/hbase/regionserver/StoreFile.java index 757a50c44f9..107d641c6c0 100644 --- a/src/main/java/org/apache/hadoop/hbase/regionserver/StoreFile.java +++ b/src/main/java/org/apache/hadoop/hbase/regionserver/StoreFile.java @@ -694,6 +694,13 @@ public class StoreFile { if (bloomType != BloomType.NONE && conf != null) { float err = conf.getFloat(IO_STOREFILE_BLOOM_ERROR_RATE, (float)0.01); + // Since in row+col blooms we have 2 calls to shouldSeek() instead of 1 + // and the false positives are adding up, we should keep the error rate + // twice as low in order to maintain the number of false positives as + // desired by the user + if (bloomType == BloomType.ROWCOL) { + err /= 2; + } int maxFold = conf.getInt(IO_STOREFILE_BLOOM_MAX_FOLD, 7); this.bloomFilter = new ByteBloomFilter(maxKeys, err, @@ -800,7 +807,6 @@ public class StoreFile { byte [] result = new byte[rl + ql]; System.arraycopy(kv.getBuffer(), ro, result, 0, rl); System.arraycopy(kv.getBuffer(), qo, result, rl, ql); - this.bloomFilter.add(result); break; default: @@ -943,7 +949,17 @@ public class StoreFile { try { ByteBuffer bloom = reader.getMetaBlock(BLOOM_FILTER_DATA_KEY, true); if (bloom != null) { - return this.bloomFilter.contains(key, bloom); + if (this.bloomFilterType == BloomType.ROWCOL) { + // Since a Row Delete is essentially a DeleteFamily applied to all + // columns, a file might be skipped if using row+col Bloom filter. + // In order to ensure this file is included an additional check is + // required looking only for a row bloom. + return this.bloomFilter.contains(key, bloom) || + this.bloomFilter.contains(row, bloom); + } + else { + return this.bloomFilter.contains(key, bloom); + } } } catch (IOException e) { LOG.error("Error reading bloom filter data -- proceeding without", diff --git a/src/test/java/org/apache/hadoop/hbase/regionserver/TestHRegion.java b/src/test/java/org/apache/hadoop/hbase/regionserver/TestHRegion.java index e9bdb4221bd..c425953ff04 100644 --- a/src/test/java/org/apache/hadoop/hbase/regionserver/TestHRegion.java +++ b/src/test/java/org/apache/hadoop/hbase/regionserver/TestHRegion.java @@ -2723,6 +2723,47 @@ public class TestHRegion extends HBaseTestCase { checkOneCell(kvs[3], FAMILY, 0, 0, 1); } + /** + * Testcase to cover bug-fix for HBASE-2823 + * Ensures correct delete when issuing delete row + * on columns with bloom filter set to row+col (BloomType.ROWCOL) + */ + public void testDeleteRowWithBloomFilter() throws IOException { + byte [] tableName = Bytes.toBytes("testDeleteRowWithBloomFilter"); + byte [] familyName = Bytes.toBytes("familyName"); + + // Create Table + HColumnDescriptor hcd = new HColumnDescriptor(familyName, Integer.MAX_VALUE, + HColumnDescriptor.DEFAULT_COMPRESSION, false, true, + HColumnDescriptor.DEFAULT_TTL, "rowcol"); + + HTableDescriptor htd = new HTableDescriptor(tableName); + htd.addFamily(hcd); + HRegionInfo info = new HRegionInfo(htd, null, null, false); + Path path = new Path(DIR + "TestDeleteRowWithBloomFilter"); + region = HRegion.createHRegion(info, path, conf); + + // Insert some data + byte row[] = Bytes.toBytes("row1"); + byte col[] = Bytes.toBytes("col1"); + + Put put = new Put(row); + put.add(familyName, col, 1, Bytes.toBytes("SomeRandomValue")); + region.put(put); + region.flushcache(); + + Delete del = new Delete(row); + region.delete(del, null, true); + region.flushcache(); + + // Get remaining rows (should have none) + Get get = new Get(row); + get.addColumn(familyName, col); + + KeyValue[] keyValues = region.get(get, null).raw(); + assertTrue(keyValues.length == 0); + } + private void putData(int startRow, int numRows, byte [] qf, byte [] ...families) throws IOException {