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</li>
40   * <li>{@link #isDeleted} when checking if a Put KeyValue has been deleted</li>
41   * <li>{@link #update} when reaching the end of a StoreFile or row for scans</li>
42   * </ul>
43   * <p>
44   * This class is NOT thread-safe as queries are never multi-threaded
45   */
46  @InterfaceAudience.Private
47  public class ScanDeleteTracker implements DeleteTracker {
48  
49    protected boolean hasFamilyStamp = false;
50    protected long familyStamp = 0L;
51    protected SortedSet<Long> familyVersionStamps = new TreeSet<Long>();
52    protected byte [] deleteBuffer = null;
53    protected int deleteOffset = 0;
54    protected int deleteLength = 0;
55    protected byte deleteType = 0;
56    protected long deleteTimestamp = 0L;
57  
58    /**
59     * Constructor for ScanDeleteTracker
60     */
61    public ScanDeleteTracker() {
62      super();
63    }
64  
65    /**
66     * Add the specified KeyValue to the list of deletes to check against for
67     * this row operation.
68     * <p>
69     * This is called when a Delete is encountered.
70     * @param cell - the delete cell
71     */
72    @Override
73    public void add(Cell cell) {
74      long timestamp = cell.getTimestamp();
75      int qualifierOffset = cell.getQualifierOffset();
76      int qualifierLength = cell.getQualifierLength();
77      byte type = cell.getTypeByte();
78      if (!hasFamilyStamp || timestamp > familyStamp) {
79        if (type == KeyValue.Type.DeleteFamily.getCode()) {
80          hasFamilyStamp = true;
81          familyStamp = timestamp;
82          return;
83        } else if (type == KeyValue.Type.DeleteFamilyVersion.getCode()) {
84          familyVersionStamps.add(timestamp);
85          return;
86        }
87  
88        if (deleteBuffer != null && type < deleteType) {
89          // same column, so ignore less specific delete
90          if (Bytes.equals(deleteBuffer, deleteOffset, deleteLength,
91              cell.getQualifierArray(), qualifierOffset, qualifierLength)){
92            return;
93          }
94        }
95        // new column, or more general delete type
96        deleteBuffer = cell.getQualifierArray();
97        deleteOffset = qualifierOffset;
98        deleteLength = qualifierLength;
99        deleteType = type;
100       deleteTimestamp = timestamp;
101     }
102     // missing else is never called.
103   }
104 
105   /**
106    * Check if the specified KeyValue buffer has been deleted by a previously
107    * seen delete.
108    *
109    * @param cell - current cell to check if deleted by a previously seen delete
110    * @return deleteResult
111    */
112   @Override
113   public DeleteResult isDeleted(Cell cell) {
114     long timestamp = cell.getTimestamp();
115     int qualifierOffset = cell.getQualifierOffset();
116     int qualifierLength = cell.getQualifierLength();
117     if (hasFamilyStamp && timestamp <= familyStamp) {
118       return DeleteResult.FAMILY_DELETED;
119     }
120 
121     if (familyVersionStamps.contains(Long.valueOf(timestamp))) {
122         return DeleteResult.FAMILY_VERSION_DELETED;
123     }
124 
125     if (deleteBuffer != null) {
126       int ret = Bytes.compareTo(deleteBuffer, deleteOffset, deleteLength,
127           cell.getQualifierArray(), qualifierOffset, qualifierLength);
128 
129       if (ret == 0) {
130         if (deleteType == KeyValue.Type.DeleteColumn.getCode()) {
131           return DeleteResult.COLUMN_DELETED;
132         }
133         // Delete (aka DeleteVersion)
134         // If the timestamp is the same, keep this one
135         if (timestamp == deleteTimestamp) {
136           return DeleteResult.VERSION_DELETED;
137         }
138         // use assert or not?
139         assert timestamp < deleteTimestamp;
140 
141         // different timestamp, let's clear the buffer.
142         deleteBuffer = null;
143       } else if(ret < 0){
144         // Next column case.
145         deleteBuffer = null;
146       } else {
147         throw new IllegalStateException("isDelete failed: deleteBuffer="
148             + Bytes.toStringBinary(deleteBuffer, deleteOffset, deleteLength)
149             + ", qualifier="
150             + Bytes.toStringBinary(cell.getQualifierArray(), qualifierOffset, qualifierLength)
151             + ", timestamp=" + timestamp + ", comparison result: " + ret);
152       }
153     }
154 
155     return DeleteResult.NOT_DELETED;
156   }
157 
158   @Override
159   public boolean isEmpty() {
160     return deleteBuffer == null && !hasFamilyStamp &&
161            familyVersionStamps.isEmpty();
162   }
163 
164   @Override
165   // called between every row.
166   public void reset() {
167     hasFamilyStamp = false;
168     familyStamp = 0L;
169     familyVersionStamps.clear();
170     deleteBuffer = null;
171   }
172 
173   @Override
174   // should not be called at all even (!)
175   public void update() {
176     this.reset();
177   }
178 }