1   
2   
3   
4   
5   
6   
7   
8   
9   
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  package org.apache.hadoop.hbase.regionserver;
20  
21  import java.util.NavigableMap;
22  import java.util.NavigableSet;
23  import java.util.TreeMap;
24  import java.util.TreeSet;
25  
26  import org.apache.hadoop.hbase.classification.InterfaceAudience;
27  import org.apache.hadoop.hbase.Cell;
28  import org.apache.hadoop.hbase.CellUtil;
29  import org.apache.hadoop.hbase.HConstants;
30  import org.apache.hadoop.hbase.KeyValue;
31  import org.apache.hadoop.hbase.KeyValue.KVComparator;
32  import org.apache.hadoop.hbase.KeyValueUtil;
33  import org.apache.hadoop.hbase.util.Bytes;
34  
35  
36  
37  
38  
39  
40  
41  @InterfaceAudience.Private
42  class GetClosestRowBeforeTracker {
43    private final KeyValue targetkey;
44    
45    private final long now;
46    private final long oldestUnexpiredTs;
47    private Cell candidate = null;
48    private final KVComparator kvcomparator;
49    
50    private final boolean metaregion;
51    
52    private final int rowoffset;
53    private final int tablenamePlusDelimiterLength;
54  
55    
56    private final NavigableMap<KeyValue, NavigableSet<KeyValue>> deletes;
57  
58    
59  
60  
61  
62  
63  
64  
65    GetClosestRowBeforeTracker(final KVComparator c, final KeyValue kv,
66        final long ttl, final boolean metaregion) {
67      super();
68      this.metaregion = metaregion;
69      this.targetkey = kv;
70      
71      
72      this.rowoffset = kv.getRowOffset();
73      int l = -1;
74      if (metaregion) {
75        l = KeyValue.getDelimiter(kv.getRowArray(), rowoffset, kv.getRowLength(),
76          HConstants.DELIMITER) - this.rowoffset;
77      }
78      this.tablenamePlusDelimiterLength = metaregion? l + 1: -1;
79      this.now = System.currentTimeMillis();
80      this.oldestUnexpiredTs = now - ttl;
81      this.kvcomparator = c;
82      KeyValue.RowOnlyComparator rc = new KeyValue.RowOnlyComparator(this.kvcomparator);
83      this.deletes = new TreeMap<KeyValue, NavigableSet<KeyValue>>(rc);
84    }
85  
86    
87  
88  
89  
90    private void addDelete(final Cell kv) {
91      NavigableSet<KeyValue> rowdeletes = this.deletes.get(kv);
92      if (rowdeletes == null) {
93        rowdeletes = new TreeSet<KeyValue>(this.kvcomparator);
94        this.deletes.put(KeyValueUtil.ensureKeyValue(kv), rowdeletes);
95      }
96      rowdeletes.add(KeyValueUtil.ensureKeyValue(kv));
97    }
98  
99    
100 
101 
102 
103   private boolean addCandidate(final Cell kv) {
104     if (!isDeleted(kv) && isBetterCandidate(kv)) {
105       this.candidate = kv;
106       return true;
107     }
108     return false;
109   }
110 
111   boolean isBetterCandidate(final Cell contender) {
112     return this.candidate == null ||
113       (this.kvcomparator.compareRows(this.candidate, contender) < 0 &&
114         this.kvcomparator.compareRows(contender, this.targetkey) <= 0);
115   }
116 
117   
118 
119 
120 
121 
122 
123   private boolean isDeleted(final Cell kv) {
124     if (this.deletes.isEmpty()) return false;
125     NavigableSet<KeyValue> rowdeletes = this.deletes.get(kv);
126     if (rowdeletes == null || rowdeletes.isEmpty()) return false;
127     return isDeleted(kv, rowdeletes);
128   }
129 
130   
131 
132 
133 
134 
135 
136 
137   public boolean isDeleted(final Cell kv, final NavigableSet<KeyValue> ds) {
138     if (deletes == null || deletes.isEmpty()) return false;
139     for (KeyValue d: ds) {
140       long kvts = kv.getTimestamp();
141       long dts = d.getTimestamp();
142       if (CellUtil.isDeleteFamily(d)) {
143         if (kvts <= dts) return true;
144         continue;
145       }
146       
147       int ret = Bytes.compareTo(kv.getQualifierArray(), kv.getQualifierOffset(),
148           kv.getQualifierLength(),
149         d.getQualifierArray(), d.getQualifierOffset(), d.getQualifierLength());
150       if (ret <= -1) {
151         
152         continue;
153       } else if (ret >= 1) {
154         
155         break;
156       }
157       
158       if (kvts > dts) return false;
159 
160       
161       switch (KeyValue.Type.codeToType(d.getType())) {
162         case Delete: return kvts == dts;
163         case DeleteColumn: return true;
164         default: continue;
165       }
166     }
167     return false;
168   }
169 
170   
171 
172 
173 
174   public boolean isExpired(final Cell cell) {
175     return cell.getTimestamp() < this.oldestUnexpiredTs ||
176       HStore.isCellTTLExpired(cell, this.oldestUnexpiredTs, this.now);
177   }
178 
179   
180 
181 
182 
183 
184 
185 
186 
187   boolean handleDeletes(final Cell kv) {
188     addDelete(kv);
189     boolean deleted = false;
190     if (!hasCandidate()) return deleted;
191     if (isDeleted(this.candidate)) {
192       this.candidate = null;
193       deleted = true;
194     }
195     return deleted;
196   }
197 
198   
199 
200 
201 
202 
203   boolean handle(final Cell kv) {
204     if (KeyValueUtil.ensureKeyValue(kv).isDelete()) {
205       handleDeletes(kv);
206       return false;
207     }
208     return addCandidate(kv);
209   }
210 
211   
212 
213 
214   public boolean hasCandidate() {
215     return this.candidate != null;
216   }
217 
218   
219 
220 
221   public Cell getCandidate() {
222     return this.candidate;
223   }
224 
225   public KeyValue getTargetKey() {
226     return this.targetkey;
227   }
228 
229   
230 
231 
232 
233 
234   boolean isTooFar(final Cell kv, final Cell firstOnRow) {
235     return this.kvcomparator.compareRows(kv, firstOnRow) > 0;
236   }
237 
238   boolean isTargetTable(final Cell kv) {
239     if (!metaregion) return true;
240     
241     
242     return Bytes.compareTo(this.targetkey.getRowArray(), this.rowoffset,
243         this.tablenamePlusDelimiterLength,
244       kv.getRowArray(), kv.getRowOffset(), this.tablenamePlusDelimiterLength) == 0;
245   }
246 }