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 org.apache.hadoop.classification.InterfaceAudience;
23  import org.apache.hadoop.hbase.KeyValue;
24  import org.apache.hadoop.hbase.util.Bytes;
25  
26  /**
27   * This class is responsible for the tracking and enforcement of Deletes
28   * during the course of a Scan operation.
29   *
30   * It only has to enforce Delete and DeleteColumn, since the
31   * DeleteFamily is handled at a higher level.
32   *
33   * <p>
34   * This class is utilized through three methods:
35   * <ul><li>{@link #add} when encountering a Delete or DeleteColumn
36   * <li>{@link #isDeleted} when checking if a Put KeyValue has been deleted
37   * <li>{@link #update} when reaching the end of a StoreFile or row for scans
38   * <p>
39   * This class is NOT thread-safe as queries are never multi-threaded
40   */
41  @InterfaceAudience.Private
42  public class ScanDeleteTracker implements DeleteTracker {
43  
44    private boolean hasFamilyStamp = false;
45    private long familyStamp = 0L;
46    private byte [] deleteBuffer = null;
47    private int deleteOffset = 0;
48    private int deleteLength = 0;
49    private byte deleteType = 0;
50    private long deleteTimestamp = 0L;
51  
52    /**
53     * Constructor for ScanDeleteTracker
54     */
55    public ScanDeleteTracker() {
56      super();
57    }
58  
59    /**
60     * Add the specified KeyValue to the list of deletes to check against for
61     * this row operation.
62     * <p>
63     * This is called when a Delete is encountered in a StoreFile.
64     * @param buffer KeyValue buffer
65     * @param qualifierOffset column qualifier offset
66     * @param qualifierLength column qualifier length
67     * @param timestamp timestamp
68     * @param type delete type as byte
69     */
70    @Override
71    public void add(byte[] buffer, int qualifierOffset, int qualifierLength,
72        long timestamp, byte type) {
73      if (!hasFamilyStamp || timestamp > familyStamp) {
74        if (type == KeyValue.Type.DeleteFamily.getCode()) {
75          hasFamilyStamp = true;
76          familyStamp = timestamp;
77          return;
78        }
79  
80        if (deleteBuffer != null && type < deleteType) {
81          // same column, so ignore less specific delete
82          if (Bytes.equals(deleteBuffer, deleteOffset, deleteLength,
83              buffer, qualifierOffset, qualifierLength)){
84            return;
85          }
86        }
87        // new column, or more general delete type
88        deleteBuffer = buffer;
89        deleteOffset = qualifierOffset;
90        deleteLength = qualifierLength;
91        deleteType = type;
92        deleteTimestamp = timestamp;
93      }
94      // missing else is never called.
95    }
96  
97    /**
98     * Check if the specified KeyValue buffer has been deleted by a previously
99     * seen delete.
100    *
101    * @param buffer KeyValue buffer
102    * @param qualifierOffset column qualifier offset
103    * @param qualifierLength column qualifier length
104    * @param timestamp timestamp
105    * @return deleteResult
106    */
107   @Override
108   public DeleteResult isDeleted(byte [] buffer, int qualifierOffset,
109       int qualifierLength, long timestamp) {
110     if (hasFamilyStamp && timestamp <= familyStamp) {
111       return DeleteResult.FAMILY_DELETED;
112     }
113 
114     if (deleteBuffer != null) {
115       int ret = Bytes.compareTo(deleteBuffer, deleteOffset, deleteLength,
116           buffer, qualifierOffset, qualifierLength);
117 
118       if (ret == 0) {
119         if (deleteType == KeyValue.Type.DeleteColumn.getCode()) {
120           return DeleteResult.COLUMN_DELETED;
121         }
122         // Delete (aka DeleteVersion)
123         // If the timestamp is the same, keep this one
124         if (timestamp == deleteTimestamp) {
125           return DeleteResult.VERSION_DELETED;
126         }
127         // use assert or not?
128         assert timestamp < deleteTimestamp;
129 
130         // different timestamp, let's clear the buffer.
131         deleteBuffer = null;
132       } else if(ret < 0){
133         // Next column case.
134         deleteBuffer = null;
135       } else {
136         throw new IllegalStateException("isDelete failed: deleteBuffer="
137             + Bytes.toStringBinary(deleteBuffer, deleteOffset, deleteLength)
138             + ", qualifier="
139             + Bytes.toStringBinary(buffer, qualifierOffset, qualifierLength)
140             + ", timestamp=" + timestamp + ", comparison result: " + ret);
141       }
142     }
143 
144     return DeleteResult.NOT_DELETED;
145   }
146 
147   @Override
148   public boolean isEmpty() {
149     return deleteBuffer == null && !hasFamilyStamp;
150   }
151 
152   @Override
153   // called between every row.
154   public void reset() {
155     hasFamilyStamp = false;
156     familyStamp = 0L;
157     deleteBuffer = null;
158   }
159 
160   @Override
161   // should not be called at all even (!)
162   public void update() {
163     this.reset();
164   }
165 }