1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.regionserver.compactions;
19
20 import java.io.IOException;
21 import java.io.InterruptedIOException;
22 import java.security.PrivilegedExceptionAction;
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.concurrent.atomic.AtomicInteger;
28
29 import org.apache.commons.logging.Log;
30 import org.apache.commons.logging.LogFactory;
31 import org.apache.hadoop.conf.Configuration;
32 import org.apache.hadoop.hbase.Cell;
33 import org.apache.hadoop.hbase.CellUtil;
34 import org.apache.hadoop.hbase.HConstants;
35 import org.apache.hadoop.hbase.KeyValue;
36 import org.apache.hadoop.hbase.KeyValueUtil;
37 import org.apache.hadoop.hbase.classification.InterfaceAudience;
38 import org.apache.hadoop.hbase.client.Scan;
39 import org.apache.hadoop.hbase.io.compress.Compression;
40 import org.apache.hadoop.hbase.io.hfile.HFile.FileInfo;
41 import org.apache.hadoop.hbase.io.hfile.HFileWriterV2;
42 import org.apache.hadoop.hbase.regionserver.HStore;
43 import org.apache.hadoop.hbase.regionserver.InternalScanner;
44 import org.apache.hadoop.hbase.regionserver.ScanType;
45 import org.apache.hadoop.hbase.regionserver.ScannerContext;
46 import org.apache.hadoop.hbase.regionserver.Store;
47 import org.apache.hadoop.hbase.regionserver.StoreFile;
48 import org.apache.hadoop.hbase.regionserver.StoreFileScanner;
49 import org.apache.hadoop.hbase.regionserver.StoreScanner;
50 import org.apache.hadoop.hbase.regionserver.TimeRangeTracker;
51 import org.apache.hadoop.hbase.security.User;
52 import org.apache.hadoop.hbase.util.Bytes;
53 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
54 import org.apache.hadoop.hbase.util.Writables;
55 import org.apache.hadoop.util.StringUtils.TraditionalBinaryPrefix;
56
57
58
59
60
61 @InterfaceAudience.Private
62 public abstract class Compactor {
63 private static final Log LOG = LogFactory.getLog(Compactor.class);
64 protected CompactionProgress progress;
65 protected Configuration conf;
66 protected Store store;
67
68 protected int compactionKVMax;
69 protected Compression.Algorithm compactionCompression;
70
71
72 protected int keepSeqIdPeriod;
73
74
75 Compactor(final Configuration conf, final Store store) {
76 this.conf = conf;
77 this.store = store;
78 this.compactionKVMax =
79 this.conf.getInt(HConstants.COMPACTION_KV_MAX, HConstants.COMPACTION_KV_MAX_DEFAULT);
80 this.compactionCompression = (this.store.getFamily() == null) ?
81 Compression.Algorithm.NONE : this.store.getFamily().getCompactionCompression();
82 this.keepSeqIdPeriod = Math.max(this.conf.getInt(HConstants.KEEP_SEQID_PERIOD,
83 HConstants.MIN_KEEP_SEQID_PERIOD), HConstants.MIN_KEEP_SEQID_PERIOD);
84 }
85
86 public interface CellSink {
87 void append(Cell cell) throws IOException;
88 }
89
90 public CompactionProgress getProgress() {
91 return this.progress;
92 }
93
94
95 protected static class FileDetails {
96
97 public long maxKeyCount = 0;
98
99 public long earliestPutTs = HConstants.LATEST_TIMESTAMP;
100
101 public long latestPutTs = HConstants.LATEST_TIMESTAMP;
102
103 public long maxSeqId = 0;
104
105 public long maxMVCCReadpoint = 0;
106
107 public int maxTagsLength = 0;
108
109 public long minSeqIdToKeep = 0;
110 }
111
112
113
114
115
116
117
118 protected FileDetails getFileDetails(
119 Collection<StoreFile> filesToCompact, boolean allFiles) throws IOException {
120 FileDetails fd = new FileDetails();
121 long oldestHFileTimeStampToKeepMVCC = System.currentTimeMillis() -
122 (1000L * 60 * 60 * 24 * this.keepSeqIdPeriod);
123
124 for (StoreFile file : filesToCompact) {
125 if(allFiles && (file.getModificationTimeStamp() < oldestHFileTimeStampToKeepMVCC)) {
126
127
128 if(fd.minSeqIdToKeep < file.getMaxMemstoreTS()) {
129 fd.minSeqIdToKeep = file.getMaxMemstoreTS();
130 }
131 }
132 long seqNum = file.getMaxSequenceId();
133 fd.maxSeqId = Math.max(fd.maxSeqId, seqNum);
134 StoreFile.Reader r = file.getReader();
135 if (r == null) {
136 LOG.warn("Null reader for " + file.getPath());
137 continue;
138 }
139
140
141
142 long keyCount = r.getEntries();
143 fd.maxKeyCount += keyCount;
144
145 Map<byte[], byte[]> fileInfo = r.loadFileInfo();
146 byte tmp[] = null;
147
148
149 if (r.isBulkLoaded()) {
150 fd.maxMVCCReadpoint = Math.max(fd.maxMVCCReadpoint, r.getSequenceID());
151 }
152 else {
153 tmp = fileInfo.get(HFileWriterV2.MAX_MEMSTORE_TS_KEY);
154 if (tmp != null) {
155 fd.maxMVCCReadpoint = Math.max(fd.maxMVCCReadpoint, Bytes.toLong(tmp));
156 }
157 }
158 tmp = fileInfo.get(FileInfo.MAX_TAGS_LEN);
159 if (tmp != null) {
160 fd.maxTagsLength = Math.max(fd.maxTagsLength, Bytes.toInt(tmp));
161 }
162
163
164 long earliestPutTs = 0;
165 if (allFiles) {
166 tmp = fileInfo.get(StoreFile.EARLIEST_PUT_TS);
167 if (tmp == null) {
168
169
170 fd.earliestPutTs = earliestPutTs = HConstants.OLDEST_TIMESTAMP;
171 } else {
172 earliestPutTs = Bytes.toLong(tmp);
173 fd.earliestPutTs = Math.min(fd.earliestPutTs, earliestPutTs);
174 }
175 }
176 tmp = fileInfo.get(StoreFile.TIMERANGE_KEY);
177 TimeRangeTracker trt = new TimeRangeTracker();
178 if (tmp == null) {
179 fd.latestPutTs = HConstants.LATEST_TIMESTAMP;
180 } else {
181 Writables.copyWritable(tmp, trt);
182 fd.latestPutTs = trt.getMaximumTimestamp();
183 }
184 if (LOG.isDebugEnabled()) {
185 LOG.debug("Compacting " + file +
186 ", keycount=" + keyCount +
187 ", bloomtype=" + r.getBloomFilterType().toString() +
188 ", size=" + TraditionalBinaryPrefix.long2String(r.length(), "", 1) +
189 ", encoding=" + r.getHFileReader().getDataBlockEncoding() +
190 ", seqNum=" + seqNum +
191 (allFiles ? ", earliestPutTs=" + earliestPutTs: ""));
192 }
193 }
194 return fd;
195 }
196
197
198
199
200
201
202 protected List<StoreFileScanner> createFileScanners(
203 final Collection<StoreFile> filesToCompact,
204 long smallestReadPoint,
205 boolean useDropBehind) throws IOException {
206 return StoreFileScanner.getScannersForStoreFiles(filesToCompact,
207
208
209
210
211 smallestReadPoint);
212 }
213
214 protected long getSmallestReadPoint() {
215 return store.getSmallestReadPoint();
216 }
217
218
219
220
221
222
223
224
225
226 protected InternalScanner preCreateCoprocScanner(final CompactionRequest request,
227 ScanType scanType, long earliestPutTs, List<StoreFileScanner> scanners) throws IOException {
228 return preCreateCoprocScanner(request, scanType, earliestPutTs, scanners, null);
229 }
230
231 protected InternalScanner preCreateCoprocScanner(final CompactionRequest request,
232 final ScanType scanType, final long earliestPutTs, final List<StoreFileScanner> scanners,
233 User user) throws IOException {
234 if (store.getCoprocessorHost() == null) return null;
235 if (user == null) {
236 return store.getCoprocessorHost().preCompactScannerOpen(store, scanners, scanType,
237 earliestPutTs, request);
238 } else {
239 try {
240 return user.getUGI().doAs(new PrivilegedExceptionAction<InternalScanner>() {
241 @Override
242 public InternalScanner run() throws Exception {
243 return store.getCoprocessorHost().preCompactScannerOpen(store, scanners,
244 scanType, earliestPutTs, request);
245 }
246 });
247 } catch (InterruptedException ie) {
248 InterruptedIOException iioe = new InterruptedIOException();
249 iioe.initCause(ie);
250 throw iioe;
251 }
252 }
253 }
254
255
256
257
258
259
260
261
262 protected InternalScanner postCreateCoprocScanner(final CompactionRequest request,
263 final ScanType scanType, final InternalScanner scanner, User user) throws IOException {
264 if (store.getCoprocessorHost() == null) return scanner;
265 if (user == null) {
266 return store.getCoprocessorHost().preCompact(store, scanner, scanType, request);
267 } else {
268 try {
269 return user.getUGI().doAs(new PrivilegedExceptionAction<InternalScanner>() {
270 @Override
271 public InternalScanner run() throws Exception {
272 return store.getCoprocessorHost().preCompact(store, scanner, scanType, request);
273 }
274 });
275 } catch (InterruptedException ie) {
276 InterruptedIOException iioe = new InterruptedIOException();
277 iioe.initCause(ie);
278 throw iioe;
279 }
280 }
281 }
282
283
284
285
286
287 private static final AtomicInteger NAME_COUNTER = new AtomicInteger(0);
288
289 private String generateCompactionName() {
290 int counter;
291 for (;;) {
292 counter = NAME_COUNTER.get();
293 int next = counter == Integer.MAX_VALUE ? 0 : counter + 1;
294 if (NAME_COUNTER.compareAndSet(counter, next)) {
295 break;
296 }
297 }
298 return store.getRegionInfo().getRegionNameAsString() + "#"
299 + store.getFamily().getNameAsString() + "#" + counter;
300 }
301
302
303
304
305
306
307
308
309
310
311 protected boolean performCompaction(FileDetails fd, InternalScanner scanner, CellSink writer,
312 long smallestReadPoint, boolean cleanSeqId,
313 CompactionThroughputController throughputController, boolean major) throws IOException {
314 long bytesWritten = 0;
315 long bytesWrittenProgress = 0;
316
317
318
319 List<Cell> cells = new ArrayList<Cell>();
320 long closeCheckInterval = HStore.getCloseCheckInterval();
321 long lastMillis = 0;
322 if (LOG.isDebugEnabled()) {
323 lastMillis = EnvironmentEdgeManager.currentTime();
324 }
325 String compactionName = generateCompactionName();
326 long now = 0;
327 boolean hasMore;
328 ScannerContext scannerContext =
329 ScannerContext.newBuilder().setBatchLimit(compactionKVMax).build();
330
331 throughputController.start(compactionName);
332 try {
333 do {
334 hasMore = scanner.next(cells, scannerContext);
335 if (LOG.isDebugEnabled()) {
336 now = EnvironmentEdgeManager.currentTime();
337 }
338
339 for (Cell c : cells) {
340 if (cleanSeqId && c.getSequenceId() <= smallestReadPoint) {
341 CellUtil.setSequenceId(c, 0);
342 }
343 writer.append(c);
344 int len = KeyValueUtil.length(c);
345 ++progress.currentCompactedKVs;
346 progress.totalCompactedSize += len;
347 if (LOG.isDebugEnabled()) {
348 bytesWrittenProgress += len;
349 }
350 throughputController.control(compactionName, len);
351
352 if (closeCheckInterval > 0) {
353 bytesWritten += len;
354 if (bytesWritten > closeCheckInterval) {
355 bytesWritten = 0;
356 if (!store.areWritesEnabled()) {
357 progress.cancel();
358 return false;
359 }
360 }
361 }
362 }
363
364
365 if (LOG.isDebugEnabled()) {
366 if ((now - lastMillis) >= 60 * 1000) {
367 LOG.debug("Compaction progress: "
368 + compactionName
369 + " "
370 + progress
371 + String.format(", rate=%.2f kB/sec", (bytesWrittenProgress / 1024.0)
372 / ((now - lastMillis) / 1000.0)) + ", throughputController is "
373 + throughputController);
374 lastMillis = now;
375 bytesWrittenProgress = 0;
376 }
377 }
378 cells.clear();
379 } while (hasMore);
380 } catch (InterruptedException e) {
381 progress.cancel();
382 throw new InterruptedIOException("Interrupted while control throughput of compacting "
383 + compactionName);
384 } finally {
385 throughputController.finish(compactionName);
386 }
387 progress.complete();
388 return true;
389 }
390
391
392
393
394
395
396
397
398
399 protected InternalScanner createScanner(Store store, List<StoreFileScanner> scanners,
400 ScanType scanType, long smallestReadPoint, long earliestPutTs) throws IOException {
401 Scan scan = new Scan();
402 scan.setMaxVersions(store.getFamily().getMaxVersions());
403 return new StoreScanner(store, store.getScanInfo(), scan, scanners,
404 scanType, smallestReadPoint, earliestPutTs);
405 }
406
407
408
409
410
411
412
413
414
415
416 protected InternalScanner createScanner(Store store, List<StoreFileScanner> scanners,
417 long smallestReadPoint, long earliestPutTs, byte[] dropDeletesFromRow,
418 byte[] dropDeletesToRow) throws IOException {
419 Scan scan = new Scan();
420 scan.setMaxVersions(store.getFamily().getMaxVersions());
421 return new StoreScanner(store, store.getScanInfo(), scan, scanners, smallestReadPoint,
422 earliestPutTs, dropDeletesFromRow, dropDeletesToRow);
423 }
424
425
426
427
428
429
430
431 protected void resetSeqId(long smallestReadPoint, boolean cleanSeqId, KeyValue kv) {
432 if (cleanSeqId && kv.getSequenceId() <= smallestReadPoint) {
433 kv.setSequenceId(0);
434 }
435 }
436
437
438
439
440
441
442
443
444 protected void appendMetadataAndCloseWriter(StoreFile.Writer writer, FileDetails fd,
445 boolean isMajor) throws IOException {
446 writer.appendMetadata(fd.maxSeqId, isMajor);
447 writer.close();
448 }
449 }