From 0f33c00f1f463fadf86d848a4387ffab3fe2be0e Mon Sep 17 00:00:00 2001 From: Zhihong Yu Date: Mon, 8 Jul 2013 14:52:00 +0000 Subject: [PATCH] HBASE-8753 Provide new delete flag which can delete all cells under a column-family which have designated timestamp (Honghua) git-svn-id: https://svn.apache.org/repos/asf/hbase/trunk@1500780 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/hadoop/hbase/client/Delete.java | 19 +++ .../hadoop/hbase/protobuf/ProtobufUtil.java | 6 +- .../org/apache/hadoop/hbase/KeyValue.java | 8 + .../protobuf/generated/ClientProtos.java | 115 ++++++------- hbase-protocol/src/main/protobuf/Client.proto | 1 + .../hbase/regionserver/DeleteTracker.java | 1 + .../hbase/regionserver/ScanDeleteTracker.java | 15 +- .../hbase/regionserver/ScanQueryMatcher.java | 1 + .../hadoop/hbase/regionserver/StoreFile.java | 2 +- .../hbase/regionserver/StoreFileScanner.java | 2 +- .../hbase/client/TestFromClientSide.java | 154 ++++++++++++++++++ .../regionserver/TestScanDeleteTracker.java | 29 ++++ 12 files changed, 293 insertions(+), 60 deletions(-) diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Delete.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Delete.java index f7562af1977..f23fdbc1eb9 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Delete.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/client/Delete.java @@ -211,6 +211,25 @@ public class Delete extends Mutation implements Comparable { return this; } + /** + * Delete all columns of the specified family with a timestamp equal to + * the specified timestamp. + * @param family family name + * @param timestamp version timestamp + * @return this for invocation chaining + */ + public Delete deleteFamilyVersion(byte [] family, long timestamp) { + List list = familyMap.get(family); + if(list == null) { + list = new ArrayList(); + } + ((List)list).add(new KeyValue(row, family, null, timestamp, + KeyValue.Type.DeleteFamilyVersion)); + familyMap.put(family, list); + return this; + } + + /** * Delete all versions of the specified column. * @param family family name diff --git a/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/ProtobufUtil.java b/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/ProtobufUtil.java index 75d41dd6c4d..619dcd6039b 100644 --- a/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/ProtobufUtil.java +++ b/hbase-client/src/main/java/org/apache/hadoop/hbase/protobuf/ProtobufUtil.java @@ -548,6 +548,8 @@ public final class ProtobufUtil { delete.deleteColumn(family, qualifier, ts); } else if (deleteType == DeleteType.DELETE_MULTIPLE_VERSIONS) { delete.deleteColumns(family, qualifier, ts); + } else if (deleteType == DeleteType.DELETE_FAMILY_VERSION) { + delete.deleteFamilyVersion(family, ts); } else { delete.deleteFamily(family, ts); } @@ -1187,7 +1189,9 @@ public final class ProtobufUtil { return DeleteType.DELETE_MULTIPLE_VERSIONS; case DeleteFamily: return DeleteType.DELETE_FAMILY; - default: + case DeleteFamilyVersion: + return DeleteType.DELETE_FAMILY_VERSION; + default: throw new IOException("Unknown delete type: " + type); } } diff --git a/hbase-common/src/main/java/org/apache/hadoop/hbase/KeyValue.java b/hbase-common/src/main/java/org/apache/hadoop/hbase/KeyValue.java index e32163fe3fd..7eaf75ea814 100644 --- a/hbase-common/src/main/java/org/apache/hadoop/hbase/KeyValue.java +++ b/hbase-common/src/main/java/org/apache/hadoop/hbase/KeyValue.java @@ -205,6 +205,7 @@ public class KeyValue implements Cell, HeapSize, Cloneable { Put((byte)4), Delete((byte)8), + DeleteFamilyVersion((byte)10), DeleteColumn((byte)12), DeleteFamily((byte)14), @@ -1371,6 +1372,13 @@ public class KeyValue implements Cell, HeapSize, Cloneable { return getType() == Type.DeleteFamily.getCode(); } + /** + * @return True if this KV is a delete family-version type. + */ + public boolean isDeleteFamilyVersion() { + return getType() == Type.DeleteFamilyVersion.getCode(); + } + /** * * @return True if this KV is a delete family or column type. diff --git a/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/ClientProtos.java b/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/ClientProtos.java index c376e9accbd..b996e82746b 100644 --- a/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/ClientProtos.java +++ b/hbase-protocol/src/main/java/org/apache/hadoop/hbase/protobuf/generated/ClientProtos.java @@ -6600,11 +6600,13 @@ public final class ClientProtos { DELETE_ONE_VERSION(0, 0), DELETE_MULTIPLE_VERSIONS(1, 1), DELETE_FAMILY(2, 2), + DELETE_FAMILY_VERSION(3, 3), ; public static final int DELETE_ONE_VERSION_VALUE = 0; public static final int DELETE_MULTIPLE_VERSIONS_VALUE = 1; public static final int DELETE_FAMILY_VALUE = 2; + public static final int DELETE_FAMILY_VERSION_VALUE = 3; public final int getNumber() { return value; } @@ -6614,6 +6616,7 @@ public final class ClientProtos { case 0: return DELETE_ONE_VERSION; case 1: return DELETE_MULTIPLE_VERSIONS; case 2: return DELETE_FAMILY; + case 3: return DELETE_FAMILY_VERSION; default: return null; } } @@ -6644,7 +6647,7 @@ public final class ClientProtos { } private static final DeleteType[] VALUES = { - DELETE_ONE_VERSION, DELETE_MULTIPLE_VERSIONS, DELETE_FAMILY, + DELETE_ONE_VERSION, DELETE_MULTIPLE_VERSIONS, DELETE_FAMILY, DELETE_FAMILY_VERSION, }; public static DeleteType valueOf( @@ -21576,7 +21579,7 @@ public final class ClientProtos { "\177\n\tCondition\022\013\n\003row\030\001 \002(\014\022\016\n\006family\030\002 \002(", "\014\022\021\n\tqualifier\030\003 \002(\014\022!\n\013compareType\030\004 \002(" + "\0162\014.CompareType\022\037\n\ncomparator\030\005 \002(\0132\013.Co" + - "mparator\"\365\005\n\rMutationProto\022\013\n\003row\030\001 \001(\014\022" + + "mparator\"\220\006\n\rMutationProto\022\013\n\003row\030\001 \001(\014\022" + "/\n\nmutateType\030\002 \001(\0162\033.MutationProto.Muta" + "tionType\022/\n\013columnValue\030\003 \003(\0132\032.Mutation" + "Proto.ColumnValue\022\021\n\ttimestamp\030\004 \001(\004\022!\n\t" + @@ -21593,61 +21596,61 @@ public final class ClientProtos { "\014\n\010SKIP_WAL\020\001\022\r\n\tASYNC_WAL\020\002\022\014\n\010SYNC_WAL" + "\020\003\022\r\n\tFSYNC_WAL\020\004\">\n\014MutationType\022\n\n\006APP" + "END\020\000\022\r\n\tINCREMENT\020\001\022\007\n\003PUT\020\002\022\n\n\006DELETE\020" + - "\003\"U\n\nDeleteType\022\026\n\022DELETE_ONE_VERSION\020\000\022", + "\003\"p\n\nDeleteType\022\026\n\022DELETE_ONE_VERSION\020\000\022", "\034\n\030DELETE_MULTIPLE_VERSIONS\020\001\022\021\n\rDELETE_" + - "FAMILY\020\002\"r\n\rMutateRequest\022 \n\006region\030\001 \002(" + - "\0132\020.RegionSpecifier\022 \n\010mutation\030\002 \002(\0132\016." + - "MutationProto\022\035\n\tcondition\030\003 \001(\0132\n.Condi" + - "tion\"<\n\016MutateResponse\022\027\n\006result\030\001 \001(\0132\007" + - ".Result\022\021\n\tprocessed\030\002 \001(\010\"\362\002\n\004Scan\022\027\n\006c" + - "olumn\030\001 \003(\0132\007.Column\022!\n\tattribute\030\002 \003(\0132" + - "\016.NameBytesPair\022\020\n\010startRow\030\003 \001(\014\022\017\n\007sto" + - "pRow\030\004 \001(\014\022\027\n\006filter\030\005 \001(\0132\007.Filter\022\035\n\tt" + - "imeRange\030\006 \001(\0132\n.TimeRange\022\026\n\013maxVersion", - "s\030\007 \001(\r:\0011\022\031\n\013cacheBlocks\030\010 \001(\010:\004true\022\021\n" + - "\tbatchSize\030\t \001(\r\022\025\n\rmaxResultSize\030\n \001(\004\022" + - "\022\n\nstoreLimit\030\013 \001(\r\022\023\n\013storeOffset\030\014 \001(\r" + - "\022\"\n\032loadColumnFamiliesOnDemand\030\r \001(\010\022\024\n\014" + - "cachingCount\030\016 \001(\r\022\023\n\013prefetching\030\017 \001(\010\"" + - "\230\001\n\013ScanRequest\022 \n\006region\030\001 \001(\0132\020.Region" + - "Specifier\022\023\n\004scan\030\002 \001(\0132\005.Scan\022\021\n\tscanne" + - "rId\030\003 \001(\004\022\024\n\014numberOfRows\030\004 \001(\r\022\024\n\014close" + - "Scanner\030\005 \001(\010\022\023\n\013nextCallSeq\030\006 \001(\004\"l\n\014Sc" + - "anResponse\022\'\n\016resultCellMeta\030\001 \001(\0132\017.Res", - "ultCellMeta\022\021\n\tscannerId\030\002 \001(\004\022\023\n\013moreRe" + - "sults\030\003 \001(\010\022\013\n\003ttl\030\004 \001(\r\"%\n\016ResultCellMe" + - "ta\022\023\n\013cellsLength\030\001 \003(\r\"\260\001\n\024BulkLoadHFil" + - "eRequest\022 \n\006region\030\001 \002(\0132\020.RegionSpecifi" + - "er\0224\n\nfamilyPath\030\002 \003(\0132 .BulkLoadHFileRe" + - "quest.FamilyPath\022\024\n\014assignSeqNum\030\003 \001(\010\032*" + - "\n\nFamilyPath\022\016\n\006family\030\001 \002(\014\022\014\n\004path\030\002 \002" + - "(\t\"\'\n\025BulkLoadHFileResponse\022\016\n\006loaded\030\001 " + - "\002(\010\"_\n\026CoprocessorServiceCall\022\013\n\003row\030\001 \002" + - "(\014\022\023\n\013serviceName\030\002 \002(\t\022\022\n\nmethodName\030\003 ", - "\002(\t\022\017\n\007request\030\004 \002(\014\"d\n\031CoprocessorServi" + - "ceRequest\022 \n\006region\030\001 \002(\0132\020.RegionSpecif" + - "ier\022%\n\004call\030\002 \002(\0132\027.CoprocessorServiceCa" + - "ll\"]\n\032CoprocessorServiceResponse\022 \n\006regi" + - "on\030\001 \002(\0132\020.RegionSpecifier\022\035\n\005value\030\002 \002(" + - "\0132\016.NameBytesPair\"B\n\013MultiAction\022 \n\010muta" + - "tion\030\001 \001(\0132\016.MutationProto\022\021\n\003get\030\002 \001(\0132" + - "\004.Get\"I\n\014ActionResult\022\026\n\005value\030\001 \001(\0132\007.R" + - "esult\022!\n\texception\030\002 \001(\0132\016.NameBytesPair" + - "\"^\n\014MultiRequest\022 \n\006region\030\001 \002(\0132\020.Regio", - "nSpecifier\022\034\n\006action\030\002 \003(\0132\014.MultiAction" + - "\022\016\n\006atomic\030\003 \001(\010\".\n\rMultiResponse\022\035\n\006res" + - "ult\030\001 \003(\0132\r.ActionResult2\342\002\n\rClientServi" + - "ce\022 \n\003get\022\013.GetRequest\032\014.GetResponse\022/\n\010" + - "multiGet\022\020.MultiGetRequest\032\021.MultiGetRes" + - "ponse\022)\n\006mutate\022\016.MutateRequest\032\017.Mutate" + - "Response\022#\n\004scan\022\014.ScanRequest\032\r.ScanRes" + - "ponse\022>\n\rbulkLoadHFile\022\025.BulkLoadHFileRe" + - "quest\032\026.BulkLoadHFileResponse\022F\n\013execSer" + - "vice\022\032.CoprocessorServiceRequest\032\033.Copro", - "cessorServiceResponse\022&\n\005multi\022\r.MultiRe" + - "quest\032\016.MultiResponseBB\n*org.apache.hado" + - "op.hbase.protobuf.generatedB\014ClientProto" + - "sH\001\210\001\001\240\001\001" + "FAMILY\020\002\022\031\n\025DELETE_FAMILY_VERSION\020\003\"r\n\rM" + + "utateRequest\022 \n\006region\030\001 \002(\0132\020.RegionSpe" + + "cifier\022 \n\010mutation\030\002 \002(\0132\016.MutationProto" + + "\022\035\n\tcondition\030\003 \001(\0132\n.Condition\"<\n\016Mutat" + + "eResponse\022\027\n\006result\030\001 \001(\0132\007.Result\022\021\n\tpr" + + "ocessed\030\002 \001(\010\"\362\002\n\004Scan\022\027\n\006column\030\001 \003(\0132\007" + + ".Column\022!\n\tattribute\030\002 \003(\0132\016.NameBytesPa" + + "ir\022\020\n\010startRow\030\003 \001(\014\022\017\n\007stopRow\030\004 \001(\014\022\027\n" + + "\006filter\030\005 \001(\0132\007.Filter\022\035\n\ttimeRange\030\006 \001(", + "\0132\n.TimeRange\022\026\n\013maxVersions\030\007 \001(\r:\0011\022\031\n" + + "\013cacheBlocks\030\010 \001(\010:\004true\022\021\n\tbatchSize\030\t " + + "\001(\r\022\025\n\rmaxResultSize\030\n \001(\004\022\022\n\nstoreLimit" + + "\030\013 \001(\r\022\023\n\013storeOffset\030\014 \001(\r\022\"\n\032loadColum" + + "nFamiliesOnDemand\030\r \001(\010\022\024\n\014cachingCount\030" + + "\016 \001(\r\022\023\n\013prefetching\030\017 \001(\010\"\230\001\n\013ScanReque" + + "st\022 \n\006region\030\001 \001(\0132\020.RegionSpecifier\022\023\n\004" + + "scan\030\002 \001(\0132\005.Scan\022\021\n\tscannerId\030\003 \001(\004\022\024\n\014" + + "numberOfRows\030\004 \001(\r\022\024\n\014closeScanner\030\005 \001(\010" + + "\022\023\n\013nextCallSeq\030\006 \001(\004\"l\n\014ScanResponse\022\'\n", + "\016resultCellMeta\030\001 \001(\0132\017.ResultCellMeta\022\021" + + "\n\tscannerId\030\002 \001(\004\022\023\n\013moreResults\030\003 \001(\010\022\013" + + "\n\003ttl\030\004 \001(\r\"%\n\016ResultCellMeta\022\023\n\013cellsLe" + + "ngth\030\001 \003(\r\"\260\001\n\024BulkLoadHFileRequest\022 \n\006r" + + "egion\030\001 \002(\0132\020.RegionSpecifier\0224\n\nfamilyP" + + "ath\030\002 \003(\0132 .BulkLoadHFileRequest.FamilyP" + + "ath\022\024\n\014assignSeqNum\030\003 \001(\010\032*\n\nFamilyPath\022" + + "\016\n\006family\030\001 \002(\014\022\014\n\004path\030\002 \002(\t\"\'\n\025BulkLoa" + + "dHFileResponse\022\016\n\006loaded\030\001 \002(\010\"_\n\026Coproc" + + "essorServiceCall\022\013\n\003row\030\001 \002(\014\022\023\n\013service", + "Name\030\002 \002(\t\022\022\n\nmethodName\030\003 \002(\t\022\017\n\007reques" + + "t\030\004 \002(\014\"d\n\031CoprocessorServiceRequest\022 \n\006" + + "region\030\001 \002(\0132\020.RegionSpecifier\022%\n\004call\030\002" + + " \002(\0132\027.CoprocessorServiceCall\"]\n\032Coproce" + + "ssorServiceResponse\022 \n\006region\030\001 \002(\0132\020.Re" + + "gionSpecifier\022\035\n\005value\030\002 \002(\0132\016.NameBytes" + + "Pair\"B\n\013MultiAction\022 \n\010mutation\030\001 \001(\0132\016." + + "MutationProto\022\021\n\003get\030\002 \001(\0132\004.Get\"I\n\014Acti" + + "onResult\022\026\n\005value\030\001 \001(\0132\007.Result\022!\n\texce" + + "ption\030\002 \001(\0132\016.NameBytesPair\"^\n\014MultiRequ", + "est\022 \n\006region\030\001 \002(\0132\020.RegionSpecifier\022\034\n" + + "\006action\030\002 \003(\0132\014.MultiAction\022\016\n\006atomic\030\003 " + + "\001(\010\".\n\rMultiResponse\022\035\n\006result\030\001 \003(\0132\r.A" + + "ctionResult2\342\002\n\rClientService\022 \n\003get\022\013.G" + + "etRequest\032\014.GetResponse\022/\n\010multiGet\022\020.Mu" + + "ltiGetRequest\032\021.MultiGetResponse\022)\n\006muta" + + "te\022\016.MutateRequest\032\017.MutateResponse\022#\n\004s" + + "can\022\014.ScanRequest\032\r.ScanResponse\022>\n\rbulk" + + "LoadHFile\022\025.BulkLoadHFileRequest\032\026.BulkL" + + "oadHFileResponse\022F\n\013execService\022\032.Coproc", + "essorServiceRequest\032\033.CoprocessorService" + + "Response\022&\n\005multi\022\r.MultiRequest\032\016.Multi" + + "ResponseBB\n*org.apache.hadoop.hbase.prot" + + "obuf.generatedB\014ClientProtosH\001\210\001\001\240\001\001" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { diff --git a/hbase-protocol/src/main/protobuf/Client.proto b/hbase-protocol/src/main/protobuf/Client.proto index dcc96888c96..e89d184cbdc 100644 --- a/hbase-protocol/src/main/protobuf/Client.proto +++ b/hbase-protocol/src/main/protobuf/Client.proto @@ -175,6 +175,7 @@ message MutationProto { DELETE_ONE_VERSION = 0; DELETE_MULTIPLE_VERSIONS = 1; DELETE_FAMILY = 2; + DELETE_FAMILY_VERSION = 3; } message ColumnValue { diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DeleteTracker.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DeleteTracker.java index 7ccd560388d..d4b8934ec35 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DeleteTracker.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/DeleteTracker.java @@ -104,6 +104,7 @@ public interface DeleteTracker { */ public static enum DeleteResult { FAMILY_DELETED, // The KeyValue is deleted by a delete family. + FAMILY_VERSION_DELETED, // The KeyValue is deleted by a delete family version. COLUMN_DELETED, // The KeyValue is deleted by a delete column. VERSION_DELETED, // The KeyValue is deleted by a version delete. NOT_DELETED diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/ScanDeleteTracker.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/ScanDeleteTracker.java index 97fe9415643..7d2170ac6b3 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/ScanDeleteTracker.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/ScanDeleteTracker.java @@ -19,6 +19,9 @@ package org.apache.hadoop.hbase.regionserver; +import java.util.SortedSet; +import java.util.TreeSet; + import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.hbase.KeyValue; import org.apache.hadoop.hbase.util.Bytes; @@ -43,6 +46,7 @@ public class ScanDeleteTracker implements DeleteTracker { private boolean hasFamilyStamp = false; private long familyStamp = 0L; + private SortedSet familyVersionStamps = new TreeSet(); private byte [] deleteBuffer = null; private int deleteOffset = 0; private int deleteLength = 0; @@ -75,6 +79,9 @@ public class ScanDeleteTracker implements DeleteTracker { hasFamilyStamp = true; familyStamp = timestamp; return; + } else if (type == KeyValue.Type.DeleteFamilyVersion.getCode()) { + familyVersionStamps.add(timestamp); + return; } if (deleteBuffer != null && type < deleteType) { @@ -111,6 +118,10 @@ public class ScanDeleteTracker implements DeleteTracker { return DeleteResult.FAMILY_DELETED; } + if (familyVersionStamps.contains(Long.valueOf(timestamp))) { + return DeleteResult.FAMILY_VERSION_DELETED; + } + if (deleteBuffer != null) { int ret = Bytes.compareTo(deleteBuffer, deleteOffset, deleteLength, buffer, qualifierOffset, qualifierLength); @@ -146,7 +157,8 @@ public class ScanDeleteTracker implements DeleteTracker { @Override public boolean isEmpty() { - return deleteBuffer == null && !hasFamilyStamp; + return deleteBuffer == null && !hasFamilyStamp && + familyVersionStamps.isEmpty(); } @Override @@ -154,6 +166,7 @@ public class ScanDeleteTracker implements DeleteTracker { public void reset() { hasFamilyStamp = false; familyStamp = 0L; + familyVersionStamps.clear(); deleteBuffer = null; } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/ScanQueryMatcher.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/ScanQueryMatcher.java index f43c72b9bbd..87f9b32d4fd 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/ScanQueryMatcher.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/ScanQueryMatcher.java @@ -352,6 +352,7 @@ public class ScanQueryMatcher { case COLUMN_DELETED: return columns.getNextRowOrNextColumn(bytes, offset, qualLength); case VERSION_DELETED: + case FAMILY_VERSION_DELETED: return MatchCode.SKIP; case NOT_DELETED: break; diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreFile.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreFile.java index 6afa5f34a5a..ebfcd862001 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreFile.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreFile.java @@ -950,7 +950,7 @@ public class StoreFile { private void appendDeleteFamilyBloomFilter(final KeyValue kv) throws IOException { - if (!kv.isDeleteFamily()) { + if (!kv.isDeleteFamily() && !kv.isDeleteFamilyVersion()) { return; } diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreFileScanner.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreFileScanner.java index 34fed09f030..9b29ad90d9d 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreFileScanner.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/StoreFileScanner.java @@ -292,7 +292,7 @@ public class StoreFileScanner implements KeyValueScanner { kv.getRowOffset(), kv.getRowLength(), kv.getBuffer(), kv.getQualifierOffset(), kv.getQualifierLength()); } else if (this.matcher != null && !matcher.hasNullColumnInQuery() && - kv.isDeleteFamily()) { + (kv.isDeleteFamily() || kv.isDeleteFamilyVersion())) { // if there is no such delete family kv in the store file, // then no need to seek. haveToSeek = reader.passesDeleteFamilyBloomFilter(kv.getBuffer(), diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java index f2ef4d1328b..cb35ca341e3 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/client/TestFromClientSide.java @@ -1722,6 +1722,160 @@ public class TestFromClientSide { } + @Test + public void testDeleteFamilyVersion() throws Exception { + HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration()); + byte [] TABLE = Bytes.toBytes("testDeleteFamilyVersion"); + + byte [][] QUALIFIERS = makeNAscii(QUALIFIER, 1); + byte [][] VALUES = makeN(VALUE, 5); + long [] ts = {1000, 2000, 3000, 4000, 5000}; + + HTable ht = TEST_UTIL.createTable(TABLE, FAMILY, 5); + + Put put = new Put(ROW); + for (int q = 0; q < 1; q++) + for (int t = 0; t < 5; t++) + put.add(FAMILY, QUALIFIERS[q], ts[t], VALUES[t]); + ht.put(put); + admin.flush(TABLE); + + Delete delete = new Delete(ROW); + delete.deleteFamilyVersion(FAMILY, ts[1]); // delete version '2000' + delete.deleteFamilyVersion(FAMILY, ts[3]); // delete version '4000' + ht.delete(delete); + admin.flush(TABLE); + + for (int i = 0; i < 1; i++) { + Get get = new Get(ROW); + get.addColumn(FAMILY, QUALIFIERS[i]); + get.setMaxVersions(Integer.MAX_VALUE); + Result result = ht.get(get); + // verify version '1000'/'3000'/'5000' remains for all columns + assertNResult(result, ROW, FAMILY, QUALIFIERS[i], + new long [] {ts[0], ts[2], ts[4]}, + new byte[][] {VALUES[0], VALUES[2], VALUES[4]}, + 0, 2); + } + ht.close(); + admin.close(); + } + + @Test + public void testDeleteFamilyVersionWithOtherDeletes() throws Exception { + byte [] TABLE = Bytes.toBytes("testDeleteFamilyVersionWithOtherDeletes"); + + byte [][] QUALIFIERS = makeNAscii(QUALIFIER, 5); + byte [][] VALUES = makeN(VALUE, 5); + long [] ts = {1000, 2000, 3000, 4000, 5000}; + + HBaseAdmin admin = new HBaseAdmin(TEST_UTIL.getConfiguration()); + HTable ht = TEST_UTIL.createTable(TABLE, FAMILY, 5); + Put put = null; + Result result = null; + Get get = null; + Delete delete = null; + + // 1. put on ROW + put = new Put(ROW); + for (int q = 0; q < 5; q++) + for (int t = 0; t < 5; t++) + put.add(FAMILY, QUALIFIERS[q], ts[t], VALUES[t]); + ht.put(put); + admin.flush(TABLE); + + // 2. put on ROWS[0] + byte [] ROW2 = Bytes.toBytes("myRowForTest"); + put = new Put(ROW2); + for (int q = 0; q < 5; q++) + for (int t = 0; t < 5; t++) + put.add(FAMILY, QUALIFIERS[q], ts[t], VALUES[t]); + ht.put(put); + admin.flush(TABLE); + + // 3. delete on ROW + delete = new Delete(ROW); + // delete version <= 2000 of all columns + // note: deleteFamily must be the first since it will mask + // the subsequent other type deletes! + delete.deleteFamily(FAMILY, ts[1]); + // delete version '4000' of all columns + delete.deleteFamilyVersion(FAMILY, ts[3]); + // delete version <= 3000 of column 0 + delete.deleteColumns(FAMILY, QUALIFIERS[0], ts[2]); + // delete version <= 5000 of column 2 + delete.deleteColumns(FAMILY, QUALIFIERS[2], ts[4]); + // delete version 5000 of column 4 + delete.deleteColumn(FAMILY, QUALIFIERS[4], ts[4]); + ht.delete(delete); + admin.flush(TABLE); + + // 4. delete on ROWS[0] + delete = new Delete(ROW2); + delete.deleteFamilyVersion(FAMILY, ts[1]); // delete version '2000' + delete.deleteFamilyVersion(FAMILY, ts[3]); // delete version '4000' + ht.delete(delete); + admin.flush(TABLE); + + // 5. check ROW + get = new Get(ROW); + get.addColumn(FAMILY, QUALIFIERS[0]); + get.setMaxVersions(Integer.MAX_VALUE); + result = ht.get(get); + assertNResult(result, ROW, FAMILY, QUALIFIERS[0], + new long [] {ts[4]}, + new byte[][] {VALUES[4]}, + 0, 0); + + get = new Get(ROW); + get.addColumn(FAMILY, QUALIFIERS[1]); + get.setMaxVersions(Integer.MAX_VALUE); + result = ht.get(get); + assertNResult(result, ROW, FAMILY, QUALIFIERS[1], + new long [] {ts[2], ts[4]}, + new byte[][] {VALUES[2], VALUES[4]}, + 0, 1); + + get = new Get(ROW); + get.addColumn(FAMILY, QUALIFIERS[2]); + get.setMaxVersions(Integer.MAX_VALUE); + result = ht.get(get); + assertEquals(0, result.size()); + + get = new Get(ROW); + get.addColumn(FAMILY, QUALIFIERS[3]); + get.setMaxVersions(Integer.MAX_VALUE); + result = ht.get(get); + assertNResult(result, ROW, FAMILY, QUALIFIERS[3], + new long [] {ts[2], ts[4]}, + new byte[][] {VALUES[2], VALUES[4]}, + 0, 1); + + get = new Get(ROW); + get.addColumn(FAMILY, QUALIFIERS[4]); + get.setMaxVersions(Integer.MAX_VALUE); + result = ht.get(get); + assertNResult(result, ROW, FAMILY, QUALIFIERS[4], + new long [] {ts[2]}, + new byte[][] {VALUES[2]}, + 0, 0); + + // 6. check ROWS[0] + for (int i = 0; i < 5; i++) { + get = new Get(ROW2); + get.addColumn(FAMILY, QUALIFIERS[i]); + get.setMaxVersions(Integer.MAX_VALUE); + result = ht.get(get); + // verify version '1000'/'3000'/'5000' remains for all columns + assertNResult(result, ROW2, FAMILY, QUALIFIERS[i], + new long [] {ts[0], ts[2], ts[4]}, + new byte[][] {VALUES[0], VALUES[2], VALUES[4]}, + 0, 2); + } + ht.close(); + admin.close(); + } + @Test public void testDeletes() throws Exception { byte [] TABLE = Bytes.toBytes("testDeletes"); diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestScanDeleteTracker.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestScanDeleteTracker.java index 04d626a593d..ce05c314407 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestScanDeleteTracker.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/regionserver/TestScanDeleteTracker.java @@ -67,6 +67,35 @@ public class TestScanDeleteTracker extends HBaseTestCase { assertEquals(DeleteResult.FAMILY_DELETED, ret); } + public void testDeletedBy_DeleteFamilyVersion() { + byte [] qualifier1 = Bytes.toBytes("qualifier1"); + byte [] qualifier2 = Bytes.toBytes("qualifier2"); + byte [] qualifier3 = Bytes.toBytes("qualifier3"); + byte [] qualifier4 = Bytes.toBytes("qualifier4"); + deleteType = KeyValue.Type.DeleteFamilyVersion.getCode(); + + sdt.add(null, 0, 0, timestamp, deleteType); + + DeleteResult ret = sdt.isDeleted(qualifier1, 0, qualifier1.length, timestamp); + assertEquals(DeleteResult.FAMILY_VERSION_DELETED, ret); + ret = sdt.isDeleted(qualifier2, 0, qualifier2.length, timestamp); + assertEquals(DeleteResult.FAMILY_VERSION_DELETED, ret); + ret = sdt.isDeleted(qualifier3, 0, qualifier3.length, timestamp); + assertEquals(DeleteResult.FAMILY_VERSION_DELETED, ret); + ret = sdt.isDeleted(qualifier4, 0, qualifier4.length, timestamp); + assertEquals(DeleteResult.FAMILY_VERSION_DELETED, ret); + + ret = sdt.isDeleted(qualifier1, 0, qualifier1.length, timestamp + 3); + assertEquals(DeleteResult.NOT_DELETED, ret); + ret = sdt.isDeleted(qualifier2, 0, qualifier2.length, timestamp - 2); + assertEquals(DeleteResult.NOT_DELETED, ret); + ret = sdt.isDeleted(qualifier3, 0, qualifier3.length, timestamp - 5); + assertEquals(DeleteResult.NOT_DELETED, ret); + ret = sdt.isDeleted(qualifier4, 0, qualifier4.length, timestamp + 8); + assertEquals(DeleteResult.NOT_DELETED, ret); + } + + public void testDelete_DeleteColumn() { byte [] qualifier = Bytes.toBytes("qualifier"); deleteType = KeyValue.Type.Delete.getCode();