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.classification.InterfaceAudience;
26  import org.apache.hadoop.hbase.KeyValue;
27  import org.apache.hadoop.hbase.util.Bytes;
28  
29  /**
30   * This class is responsible for the tracking and enforcement of Deletes
31   * during the course of a Scan operation.
32   *
33   * It only has to enforce Delete and DeleteColumn, since the
34   * DeleteFamily is handled at a higher level.
35   *
36   * <p>
37   * This class is utilized through three methods:
38   * <ul><li>{@link #add} when encountering a Delete or DeleteColumn
39   * <li>{@link #isDeleted} when checking if a Put KeyValue has been deleted
40   * <li>{@link #update} when reaching the end of a StoreFile or row for scans
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    private boolean hasFamilyStamp = false;
48    private long familyStamp = 0L;
49    private SortedSet<Long> familyVersionStamps = new TreeSet<Long>();
50    private byte [] deleteBuffer = null;
51    private int deleteOffset = 0;
52    private int deleteLength = 0;
53    private byte deleteType = 0;
54    private long deleteTimestamp = 0L;
55  
56    /**
57     * Constructor for ScanDeleteTracker
58     */
59    public ScanDeleteTracker() {
60      super();
61    }
62  
63    /**
64     * Add the specified KeyValue to the list of deletes to check against for
65     * this row operation.
66     * <p>
67     * This is called when a Delete is encountered in a StoreFile.
68     * @param buffer KeyValue buffer
69     * @param qualifierOffset column qualifier offset
70     * @param qualifierLength column qualifier length
71     * @param timestamp timestamp
72     * @param type delete type as byte
73     */
74    @Override
75    public void add(byte[] buffer, int qualifierOffset, int qualifierLength,
76        long timestamp, byte type) {
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              buffer, qualifierOffset, qualifierLength)){
91            return;
92          }
93        }
94        // new column, or more general delete type
95        deleteBuffer = buffer;
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 buffer KeyValue buffer
109    * @param qualifierOffset column qualifier offset
110    * @param qualifierLength column qualifier length
111    * @param timestamp timestamp
112    * @return deleteResult
113    */
114   @Override
115   public DeleteResult isDeleted(byte [] buffer, int qualifierOffset,
116       int qualifierLength, long timestamp) {
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           buffer, 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(buffer, 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 }