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 }