View Javadoc

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