Skip to content
This repository was archived by the owner on Nov 17, 2024. It is now read-only.

Commit 594a9d0

Browse files
committed
Merge pull request #68 from caskdata/feature/tephra-104_delete-ts
TEPHRA-104 Use cell timestamps when generating family delete markers
2 parents 2357fce + 7f3257f commit 594a9d0

9 files changed

Lines changed: 256 additions & 8 deletions

File tree

tephra-hbase-compat-0.96/src/main/java/co/cask/tephra/hbase96/TransactionAwareHTable.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,9 @@ private Delete transactionalizeAction(Delete delete) throws IOException {
558558
if (conflictLevel == TxConstants.ConflictDetection.ROW ||
559559
conflictLevel == TxConstants.ConflictDetection.NONE) {
560560
// no need to identify individual columns deleted
561-
txDelete.deleteFamily(family);
561+
// Older versions of HBase 0.96 lack HBASE-10964, so family deletes do not correctly
562+
// inherit the common Delete timestamp, so must explicitly set the timestamp here.
563+
txDelete.deleteFamily(family, transactionTimestamp);
562564
addToChangeSet(deleteRow, null, null);
563565
} else {
564566
Result result = get(new Get(delete.getRow()).addFamily(family));

tephra-hbase-compat-0.96/src/main/java/co/cask/tephra/hbase96/coprocessor/TransactionProcessor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,8 @@ public void preDelete(ObserverContext<RegionCoprocessorEnvironment> e, Delete de
176176
for (byte[] family : delete.getFamilyCellMap().keySet()) {
177177
List<Cell> familyCells = delete.getFamilyCellMap().get(family);
178178
if (isFamilyDelete(familyCells)) {
179-
deleteMarkers.add(family, TxConstants.FAMILY_DELETE_QUALIFIER, HConstants.EMPTY_BYTE_ARRAY);
179+
deleteMarkers.add(family, TxConstants.FAMILY_DELETE_QUALIFIER, familyCells.get(0).getTimestamp(),
180+
HConstants.EMPTY_BYTE_ARRAY);
180181
} else {
181182
int cellSize = familyCells.size();
182183
for (int i = 0; i < cellSize; i++) {

tephra-hbase-compat-0.96/src/test/java/co/cask/tephra/hbase96/coprocessor/TransactionProcessorTest.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,66 @@ public void testDeleteMarkerCleanup() throws Exception {
366366
}
367367
}
368368

369+
/**
370+
* Test that we correctly preserve the timestamp set for column family delete markers. This is not
371+
* directly required for the TransactionAwareHTable usage, but is the right thing to do and ensures
372+
* that we make it easy to interoperate with other systems.
373+
*/
374+
@Test
375+
public void testFamilyDeleteTimestamp() throws Exception {
376+
String tableName = "TestFamilyDeleteTimestamp";
377+
byte[] family1Bytes = Bytes.toBytes("f1");
378+
byte[] columnBytes = Bytes.toBytes("c");
379+
byte[] rowBytes = Bytes.toBytes("row");
380+
byte[] valBytes = Bytes.toBytes("val");
381+
HRegion region = createRegion(tableName, family1Bytes, 0);
382+
try {
383+
region.initialize();
384+
385+
long now = System.currentTimeMillis() * TxConstants.MAX_TX_PER_MS;
386+
Put p = new Put(rowBytes);
387+
p.add(family1Bytes, columnBytes, now - 10, valBytes);
388+
region.put(p);
389+
390+
// issue a family delete with an explicit timestamp
391+
Delete delete = new Delete(rowBytes, now);
392+
delete.deleteFamily(family1Bytes, now - 5);
393+
region.delete(delete);
394+
395+
// test that the delete marker preserved the timestamp
396+
Scan scan = new Scan();
397+
scan.setMaxVersions();
398+
RegionScanner scanner = region.getScanner(scan);
399+
List<Cell> results = Lists.newArrayList();
400+
scanner.next(results);
401+
assertEquals(2, results.size());
402+
// delete marker should appear first
403+
Cell cell = results.get(0);
404+
assertArrayEquals(new byte[0], cell.getQualifier());
405+
assertArrayEquals(new byte[0], cell.getValue());
406+
assertEquals(now - 5, cell.getTimestamp());
407+
// since this is an unfiltered scan against the region, the original put should be next
408+
cell = results.get(1);
409+
assertArrayEquals(valBytes, cell.getValue());
410+
assertEquals(now - 10, cell.getTimestamp());
411+
scanner.close();
412+
413+
414+
// with a filtered scan the original put should disappear
415+
scan = new Scan();
416+
scan.setMaxVersions();
417+
scan.setFilter(new TransactionVisibilityFilter(
418+
TxUtils.createDummyTransaction(txSnapshot), new TreeMap<byte[], Long>(), false, ScanType.USER_SCAN));
419+
scanner = region.getScanner(scan);
420+
results = Lists.newArrayList();
421+
scanner.next(results);
422+
assertEquals(0, results.size());
423+
scanner.close();
424+
} finally {
425+
region.close();
426+
}
427+
}
428+
369429
private HRegion createRegion(String tableName, byte[] family, long ttl) throws IOException {
370430
HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(tableName));
371431
HColumnDescriptor cfd = new HColumnDescriptor(family);

tephra-hbase-compat-0.98/src/main/java/co/cask/tephra/hbase98/coprocessor/TransactionProcessor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,8 @@ public void preDelete(ObserverContext<RegionCoprocessorEnvironment> e, Delete de
176176
for (byte[] family : delete.getFamilyCellMap().keySet()) {
177177
List<Cell> familyCells = delete.getFamilyCellMap().get(family);
178178
if (isFamilyDelete(familyCells)) {
179-
deleteMarkers.add(family, TxConstants.FAMILY_DELETE_QUALIFIER, HConstants.EMPTY_BYTE_ARRAY);
179+
deleteMarkers.add(family, TxConstants.FAMILY_DELETE_QUALIFIER, familyCells.get(0).getTimestamp(),
180+
HConstants.EMPTY_BYTE_ARRAY);
180181
} else {
181182
int cellSize = familyCells.size();
182183
for (int i = 0; i < cellSize; i++) {

tephra-hbase-compat-0.98/src/test/java/co/cask/tephra/hbase98/coprocessor/TransactionProcessorTest.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,66 @@ public void testDeleteMarkerCleanup() throws Exception {
372372
}
373373
}
374374

375+
/**
376+
* Test that we correctly preserve the timestamp set for column family delete markers. This is not
377+
* directly required for the TransactionAwareHTable usage, but is the right thing to do and ensures
378+
* that we make it easy to interoperate with other systems.
379+
*/
380+
@Test
381+
public void testFamilyDeleteTimestamp() throws Exception {
382+
String tableName = "TestFamilyDeleteTimestamp";
383+
byte[] family1Bytes = Bytes.toBytes("f1");
384+
byte[] columnBytes = Bytes.toBytes("c");
385+
byte[] rowBytes = Bytes.toBytes("row");
386+
byte[] valBytes = Bytes.toBytes("val");
387+
HRegion region = createRegion(tableName, family1Bytes, 0);
388+
try {
389+
region.initialize();
390+
391+
long now = System.currentTimeMillis() * TxConstants.MAX_TX_PER_MS;
392+
Put p = new Put(rowBytes);
393+
p.add(family1Bytes, columnBytes, now - 10, valBytes);
394+
region.put(p);
395+
396+
// issue a family delete with an explicit timestamp
397+
Delete delete = new Delete(rowBytes, now);
398+
delete.deleteFamily(family1Bytes, now - 5);
399+
region.delete(delete);
400+
401+
// test that the delete marker preserved the timestamp
402+
Scan scan = new Scan();
403+
scan.setMaxVersions();
404+
RegionScanner scanner = region.getScanner(scan);
405+
List<Cell> results = Lists.newArrayList();
406+
scanner.next(results);
407+
assertEquals(2, results.size());
408+
// delete marker should appear first
409+
Cell cell = results.get(0);
410+
assertArrayEquals(new byte[0], cell.getQualifier());
411+
assertArrayEquals(new byte[0], cell.getValue());
412+
assertEquals(now - 5, cell.getTimestamp());
413+
// since this is an unfiltered scan against the region, the original put should be next
414+
cell = results.get(1);
415+
assertArrayEquals(valBytes, cell.getValue());
416+
assertEquals(now - 10, cell.getTimestamp());
417+
scanner.close();
418+
419+
420+
// with a filtered scan the original put should disappear
421+
scan = new Scan();
422+
scan.setMaxVersions();
423+
scan.setFilter(new TransactionVisibilityFilter(
424+
TxUtils.createDummyTransaction(txSnapshot), new TreeMap<byte[], Long>(), false, ScanType.USER_SCAN));
425+
scanner = region.getScanner(scan);
426+
results = Lists.newArrayList();
427+
scanner.next(results);
428+
assertEquals(0, results.size());
429+
scanner.close();
430+
} finally {
431+
region.close();
432+
}
433+
}
434+
375435
private HRegion createRegion(String tableName, byte[] family, long ttl) throws IOException {
376436
HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(tableName));
377437
HColumnDescriptor cfd = new HColumnDescriptor(family);

tephra-hbase-compat-1.0-cdh/src/main/java/co/cask/tephra/hbase10cdh/coprocessor/TransactionProcessor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,8 @@ public void preDelete(ObserverContext<RegionCoprocessorEnvironment> e, Delete de
176176
for (byte[] family : delete.getFamilyCellMap().keySet()) {
177177
List<Cell> familyCells = delete.getFamilyCellMap().get(family);
178178
if (isFamilyDelete(familyCells)) {
179-
deleteMarkers.add(family, TxConstants.FAMILY_DELETE_QUALIFIER, HConstants.EMPTY_BYTE_ARRAY);
179+
deleteMarkers.add(family, TxConstants.FAMILY_DELETE_QUALIFIER, familyCells.get(0).getTimestamp(),
180+
HConstants.EMPTY_BYTE_ARRAY);
180181
} else {
181182
int cellSize = familyCells.size();
182183
for (int i = 0; i < cellSize; i++) {

tephra-hbase-compat-1.0-cdh/src/test/java/co/cask/tephra/hbase10cdh/coprocessor/TransactionProcessorTest.java

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,66 @@ public void testDeleteMarkerCleanup() throws Exception {
351351
}
352352
}
353353

354+
/**
355+
* Test that we correctly preserve the timestamp set for column family delete markers. This is not
356+
* directly required for the TransactionAwareHTable usage, but is the right thing to do and ensures
357+
* that we make it easy to interoperate with other systems.
358+
*/
359+
@Test
360+
public void testFamilyDeleteTimestamp() throws Exception {
361+
String tableName = "TestFamilyDeleteTimestamp";
362+
byte[] family1Bytes = Bytes.toBytes("f1");
363+
byte[] columnBytes = Bytes.toBytes("c");
364+
byte[] rowBytes = Bytes.toBytes("row");
365+
byte[] valBytes = Bytes.toBytes("val");
366+
HRegion region = createRegion(tableName, family1Bytes, 0);
367+
try {
368+
region.initialize();
369+
370+
long now = System.currentTimeMillis() * TxConstants.MAX_TX_PER_MS;
371+
Put p = new Put(rowBytes);
372+
p.add(family1Bytes, columnBytes, now - 10, valBytes);
373+
region.put(p);
374+
375+
// issue a family delete with an explicit timestamp
376+
Delete delete = new Delete(rowBytes, now);
377+
delete.deleteFamily(family1Bytes, now - 5);
378+
region.delete(delete);
379+
380+
// test that the delete marker preserved the timestamp
381+
Scan scan = new Scan();
382+
scan.setMaxVersions();
383+
RegionScanner scanner = region.getScanner(scan);
384+
List<Cell> results = Lists.newArrayList();
385+
scanner.next(results);
386+
assertEquals(2, results.size());
387+
// delete marker should appear first
388+
Cell cell = results.get(0);
389+
assertArrayEquals(new byte[0], cell.getQualifier());
390+
assertArrayEquals(new byte[0], cell.getValue());
391+
assertEquals(now - 5, cell.getTimestamp());
392+
// since this is an unfiltered scan against the region, the original put should be next
393+
cell = results.get(1);
394+
assertArrayEquals(valBytes, cell.getValue());
395+
assertEquals(now - 10, cell.getTimestamp());
396+
scanner.close();
397+
398+
399+
// with a filtered scan the original put should disappear
400+
scan = new Scan();
401+
scan.setMaxVersions();
402+
scan.setFilter(new TransactionVisibilityFilter(
403+
TxUtils.createDummyTransaction(txSnapshot), new TreeMap<byte[], Long>(), false, ScanType.USER_SCAN));
404+
scanner = region.getScanner(scan);
405+
results = Lists.newArrayList();
406+
scanner.next(results);
407+
assertEquals(0, results.size());
408+
scanner.close();
409+
} finally {
410+
region.close();
411+
}
412+
}
413+
354414
private HRegion createRegion(String tableName, byte[] family, long ttl) throws IOException {
355415
HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(tableName));
356416
HColumnDescriptor cfd = new HColumnDescriptor(family);

tephra-hbase-compat-1.0/src/main/java/co/cask/tephra/hbase10/coprocessor/TransactionProcessor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,8 @@ public void preDelete(ObserverContext<RegionCoprocessorEnvironment> e, Delete de
176176
for (byte[] family : delete.getFamilyCellMap().keySet()) {
177177
List<Cell> familyCells = delete.getFamilyCellMap().get(family);
178178
if (isFamilyDelete(familyCells)) {
179-
deleteMarkers.add(family, TxConstants.FAMILY_DELETE_QUALIFIER, HConstants.EMPTY_BYTE_ARRAY);
179+
deleteMarkers.add(family, TxConstants.FAMILY_DELETE_QUALIFIER, familyCells.get(0).getTimestamp(),
180+
HConstants.EMPTY_BYTE_ARRAY);
180181
} else {
181182
int cellSize = familyCells.size();
182183
for (int i = 0; i < cellSize; i++) {

tephra-hbase-compat-1.0/src/test/java/co/cask/tephra/hbase10/coprocessor/TransactionProcessorTest.java

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
import org.apache.hadoop.hbase.TableName;
4646
import org.apache.hadoop.hbase.client.Delete;
4747
import org.apache.hadoop.hbase.client.Put;
48+
import org.apache.hadoop.hbase.client.Result;
49+
import org.apache.hadoop.hbase.client.ResultScanner;
4850
import org.apache.hadoop.hbase.client.Scan;
4951
import org.apache.hadoop.hbase.regionserver.HRegion;
5052
import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
@@ -170,19 +172,19 @@ public void testDataJanitorRegionScanner() throws Exception {
170172
// first returned value should be "4" with version "4"
171173
results.clear();
172174
assertTrue(regionScanner.next(results));
173-
assertKeyValueMatches(results, 4, new long[] {V[4]});
175+
assertKeyValueMatches(results, 4, new long[]{V[4]});
174176

175177
results.clear();
176178
assertTrue(regionScanner.next(results));
177179
assertKeyValueMatches(results, 5, new long[] {V[4]});
178180

179181
results.clear();
180182
assertTrue(regionScanner.next(results));
181-
assertKeyValueMatches(results, 6, new long[] {V[6], V[4]});
183+
assertKeyValueMatches(results, 6, new long[]{V[6], V[4]});
182184

183185
results.clear();
184186
assertTrue(regionScanner.next(results));
185-
assertKeyValueMatches(results, 7, new long[] {V[6], V[4]});
187+
assertKeyValueMatches(results, 7, new long[]{V[6], V[4]});
186188

187189
results.clear();
188190
assertFalse(regionScanner.next(results));
@@ -352,6 +354,66 @@ public void testDeleteMarkerCleanup() throws Exception {
352354
}
353355
}
354356

357+
/**
358+
* Test that we correctly preserve the timestamp set for column family delete markers. This is not
359+
* directly required for the TransactionAwareHTable usage, but is the right thing to do and ensures
360+
* that we make it easy to interoperate with other systems.
361+
*/
362+
@Test
363+
public void testFamilyDeleteTimestamp() throws Exception {
364+
String tableName = "TestFamilyDeleteTimestamp";
365+
byte[] family1Bytes = Bytes.toBytes("f1");
366+
byte[] columnBytes = Bytes.toBytes("c");
367+
byte[] rowBytes = Bytes.toBytes("row");
368+
byte[] valBytes = Bytes.toBytes("val");
369+
HRegion region = createRegion(tableName, family1Bytes, 0);
370+
try {
371+
region.initialize();
372+
373+
long now = System.currentTimeMillis() * TxConstants.MAX_TX_PER_MS;
374+
Put p = new Put(rowBytes);
375+
p.add(family1Bytes, columnBytes, now - 10, valBytes);
376+
region.put(p);
377+
378+
// issue a family delete with an explicit timestamp
379+
Delete delete = new Delete(rowBytes, now);
380+
delete.deleteFamily(family1Bytes, now - 5);
381+
region.delete(delete);
382+
383+
// test that the delete marker preserved the timestamp
384+
Scan scan = new Scan();
385+
scan.setMaxVersions();
386+
RegionScanner scanner = region.getScanner(scan);
387+
List<Cell> results = Lists.newArrayList();
388+
scanner.next(results);
389+
assertEquals(2, results.size());
390+
// delete marker should appear first
391+
Cell cell = results.get(0);
392+
assertArrayEquals(new byte[0], cell.getQualifier());
393+
assertArrayEquals(new byte[0], cell.getValue());
394+
assertEquals(now - 5, cell.getTimestamp());
395+
// since this is an unfiltered scan against the region, the original put should be next
396+
cell = results.get(1);
397+
assertArrayEquals(valBytes, cell.getValue());
398+
assertEquals(now - 10, cell.getTimestamp());
399+
scanner.close();
400+
401+
402+
// with a filtered scan the original put should disappear
403+
scan = new Scan();
404+
scan.setMaxVersions();
405+
scan.setFilter(new TransactionVisibilityFilter(
406+
TxUtils.createDummyTransaction(txSnapshot), new TreeMap<byte[], Long>(), false, ScanType.USER_SCAN));
407+
scanner = region.getScanner(scan);
408+
results = Lists.newArrayList();
409+
scanner.next(results);
410+
assertEquals(0, results.size());
411+
scanner.close();
412+
} finally {
413+
region.close();
414+
}
415+
}
416+
355417
private HRegion createRegion(String tableName, byte[] family, long ttl) throws IOException {
356418
HTableDescriptor htd = new HTableDescriptor(TableName.valueOf(tableName));
357419
HColumnDescriptor cfd = new HColumnDescriptor(family);

0 commit comments

Comments
 (0)