View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  package org.apache.hadoop.hbase.regionserver;
21  
22  import java.util.SortedSet;
23  import java.util.TreeSet;
24  
25  import org.apache.hadoop.hbase.classification.InterfaceAudience;
26  import org.apache.hadoop.hbase.Cell;
27  import org.apache.hadoop.hbase.KeyValue;
28  import org.apache.hadoop.hbase.util.Bytes;
29  
30  /**
31   * This class is responsible for the tracking and enforcement of Deletes
32   * during the course of a Scan operation.
33   *
34   * It only has to enforce Delete and DeleteColumn, since the
35   * DeleteFamily is handled at a higher level.
36   *
37   * <p>
38   * This class is utilized through three methods:
39   * <ul><li>{@link #add} when encountering a Delete or DeleteColumn
40   * <li>{@link #isDeleted} when checking if a Put KeyValue has been deleted
41   * <li>{@link #update} when reaching the end of a StoreFile or row for scans
42   * <p>
43   * This class is NOT thread-safe as queries are never multi-threaded
44   */
45  @InterfaceAudience.Private
46  public class ScanDeleteTracker implements DeleteTracker {
47  
48    protected boolean hasFamilyStamp = false;
49    protected long familyStamp = 0L;
50    protected SortedSet<Long> familyVersionStamps = new TreeSet<Long>();
51    protected byte [] deleteBuffer = null;
52    protected int deleteOffset = 0;
53    protected int deleteLength = 0;
54    protected byte deleteType = 0;
55    protected long deleteTimestamp = 0L;
56  
57    /**
58     * Constructor for ScanDeleteTracker
59     */
60    public ScanDeleteTracker() {
61      super();
62    }
63  
64    /**
65     * Add the specified KeyValue to the list of deletes to check against for
66     * this row operation.
67     * <p>
68     * This is called when a Delete is encountered.
69     * @param cell - the delete cell
70     */
71    @Override
72    public void add(Cell cell) {
73      long timestamp = cell.getTimestamp();
74      int qualifierOffset = cell.getQualifierOffset();
75      int qualifierLength = cell.getQualifierLength();
76      byte type = cell.getTypeByte();
77      if (!hasFamilyStamp || timestamp > familyStamp) {
78        if (type == KeyValue.Type.DeleteFamily.getCode()) {
79          hasFamilyStamp = true;
80          familyStamp = timestamp;
81          return;
82        } else if (type == KeyValue.Type.DeleteFamilyVersion.getCode()) {
83          familyVersionStamps.add(timestamp);
84          return;
85        }
86  
87        if (deleteBuffer != null && type < deleteType) {
88          // same column, so ignore less specific delete
89          if (Bytes.equals(deleteBuffer, deleteOffset, deleteLength,
90              cell.getQualifierArray(), qualifierOffset, qualifierLength)){
91            return;
92          }
93        }
94        // new column, or more general delete type
95        deleteBuffer = cell.getQualifierArray();
96        deleteOffset = qualifierOffset;
97        deleteLength = qualifierLength;
98        deleteType = type;
99        deleteTimestamp = timestamp;
100     }
101     // missing else is never called.
102   }
103 
104   /**
105    * Check if the specified KeyValue buffer has been deleted by a previously
106    * seen delete.
107    *
108    * @param cell - current cell to check if deleted by a previously seen delete
109    * @return deleteResult
110    */
111   @Override
112   public DeleteResult isDeleted(Cell cell) {
113     long timestamp = cell.getTimestamp();
114     int qualifierOffset = cell.getQualifierOffset();
115     int qualifierLength = cell.getQualifierLength();
116     if (hasFamilyStamp && timestamp <= familyStamp) {
117       return DeleteResult.FAMILY_DELETED;
118     }
119 
120     if (familyVersionStamps.contains(Long.valueOf(timestamp))) {
121         return DeleteResult.FAMILY_VERSION_DELETED;
122     }
123 
124     if (deleteBuffer != null) {
125       int ret = Bytes.compareTo(deleteBuffer, deleteOffset, deleteLength,
126           cell.getQualifierArray(), qualifierOffset, qualifierLength);
127 
128       if (ret == 0) {
129         if (deleteType == KeyValue.Type.DeleteColumn.getCode()) {
130           return DeleteResult.COLUMN_DELETED;
131         }
132         // Delete (aka DeleteVersion)
133         // If the timestamp is the same, keep this one
134         if (timestamp == deleteTimestamp) {
135           return DeleteResult.VERSION_DELETED;
136         }
137         // use assert or not?
138         assert timestamp < deleteTimestamp;
139 
140         // different timestamp, let's clear the buffer.
141         deleteBuffer = null;
142       } else if(ret < 0){
143         // Next column case.
144         deleteBuffer = null;
145       } else {
146         throw new IllegalStateException("isDelete failed: deleteBuffer="
147             + Bytes.toStringBinary(deleteBuffer, deleteOffset, deleteLength)
148             + ", qualifier="
149             + Bytes.toStringBinary(cell.getQualifierArray(), qualifierOffset, qualifierLength)
150             + ", timestamp=" + timestamp + ", comparison result: " + ret);
151       }
152     }
153 
154     return DeleteResult.NOT_DELETED;
155   }
156 
157   @Override
158   public boolean isEmpty() {
159     return deleteBuffer == null && !hasFamilyStamp &&
160            familyVersionStamps.isEmpty();
161   }
162 
163   @Override
164   // called between every row.
165   public void reset() {
166     hasFamilyStamp = false;
167     familyStamp = 0L;
168     familyVersionStamps.clear();
169     deleteBuffer = null;
170   }
171 
172   @Override
173   // should not be called at all even (!)
174   public void update() {
175     this.reset();
176   }
177 }