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 static org.apache.hadoop.hbase.HBaseTestingUtility.START_KEY;
22 import static org.apache.hadoop.hbase.HBaseTestingUtility.fam1;
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertTrue;
25
26 import java.io.IOException;
27 import java.util.ArrayList;
28 import java.util.HashSet;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Random;
32 import java.util.Set;
33
34 import org.apache.commons.logging.Log;
35 import org.apache.commons.logging.LogFactory;
36 import org.apache.hadoop.conf.Configuration;
37 import org.apache.hadoop.fs.FileStatus;
38 import org.apache.hadoop.fs.FileSystem;
39 import org.apache.hadoop.fs.Path;
40 import org.apache.hadoop.hbase.Cell;
41 import org.apache.hadoop.hbase.CellUtil;
42 import org.apache.hadoop.hbase.HBaseTestCase.HRegionIncommon;
43 import org.apache.hadoop.hbase.HBaseTestingUtility;
44 import org.apache.hadoop.hbase.HColumnDescriptor;
45 import org.apache.hadoop.hbase.HConstants;
46 import org.apache.hadoop.hbase.HTableDescriptor;
47 import org.apache.hadoop.hbase.KeyValue;
48 import org.apache.hadoop.hbase.KeyValueUtil;
49 import org.apache.hadoop.hbase.testclassification.MediumTests;
50 import org.apache.hadoop.hbase.client.Delete;
51 import org.apache.hadoop.hbase.client.Durability;
52 import org.apache.hadoop.hbase.client.Put;
53 import org.apache.hadoop.hbase.client.Scan;
54 import org.apache.hadoop.hbase.io.hfile.CacheConfig;
55 import org.apache.hadoop.hbase.io.hfile.HFile;
56 import org.apache.hadoop.hbase.io.hfile.HFileContext;
57 import org.apache.hadoop.hbase.io.hfile.HFileContextBuilder;
58 import org.apache.hadoop.hbase.mob.MobConstants;
59 import org.apache.hadoop.hbase.mob.MobUtils;
60 import org.apache.hadoop.hbase.util.Bytes;
61 import org.apache.hadoop.hbase.util.FSUtils;
62 import org.apache.hadoop.hbase.util.Pair;
63 import org.junit.After;
64 import org.junit.AfterClass;
65 import org.junit.BeforeClass;
66 import org.junit.Rule;
67 import org.junit.Test;
68 import org.junit.experimental.categories.Category;
69 import org.junit.rules.TestName;
70
71
72
73
74 @Category(MediumTests.class)
75 public class TestMobCompaction {
76 @Rule
77 public TestName name = new TestName();
78 static final Log LOG = LogFactory.getLog(TestMobCompaction.class.getName());
79 private final static HBaseTestingUtility UTIL = new HBaseTestingUtility();
80 private Configuration conf = null;
81
82 private HRegion region = null;
83 private HTableDescriptor htd = null;
84 private HColumnDescriptor hcd = null;
85 private long mobCellThreshold = 1000;
86
87 private FileSystem fs;
88
89 private static final byte[] COLUMN_FAMILY = fam1;
90 private final byte[] STARTROW = Bytes.toBytes(START_KEY);
91 private int compactionThreshold;
92
93 @BeforeClass
94 public static void setUpBeforeClass() throws Exception {
95 UTIL.getConfiguration().setInt("hfile.format.version", 3);
96 UTIL.getConfiguration().setInt("hbase.master.info.port", 0);
97 UTIL.getConfiguration().setBoolean("hbase.regionserver.info.port.auto", true);
98 UTIL.startMiniCluster(1);
99 }
100
101 @AfterClass
102 public static void tearDownAfterClass() throws Exception {
103 UTIL.shutdownMiniCluster();
104 }
105
106 private void init(Configuration conf, long mobThreshold) throws Exception {
107 this.conf = conf;
108 this.mobCellThreshold = mobThreshold;
109 HBaseTestingUtility UTIL = new HBaseTestingUtility(conf);
110
111 compactionThreshold = conf.getInt("hbase.hstore.compactionThreshold", 3);
112 htd = UTIL.createTableDescriptor(name.getMethodName());
113 hcd = htd.getFamily(COLUMN_FAMILY);
114 hcd.setMobEnabled(true);
115 hcd.setMobThreshold(mobThreshold);
116 hcd.setMaxVersions(1);
117
118 region = UTIL.createLocalHRegion(htd, null, null);
119 fs = FileSystem.get(conf);
120 }
121
122 @After
123 public void tearDown() throws Exception {
124 region.close();
125 fs.delete(UTIL.getDataTestDir(), true);
126 }
127
128
129
130
131 @Test
132 public void testSmallerValue() throws Exception {
133 init(UTIL.getConfiguration(), 500);
134 byte[] dummyData = makeDummyData(300);
135 HRegionIncommon loader = new HRegionIncommon(region);
136
137 for (int i = 0; i < compactionThreshold; i++) {
138 Put p = createPut(i, dummyData);
139 loader.put(p);
140 loader.flushcache();
141 }
142 assertEquals("Before compaction: store files", compactionThreshold, countStoreFiles());
143 assertEquals("Before compaction: mob file count", 0, countMobFiles());
144 assertEquals("Before compaction: rows", compactionThreshold, countRows());
145 assertEquals("Before compaction: mob rows", 0, countMobRows());
146
147 region.compactStores();
148
149 assertEquals("After compaction: store files", 1, countStoreFiles());
150 assertEquals("After compaction: mob file count", 0, countMobFiles());
151 assertEquals("After compaction: referenced mob file count", 0, countReferencedMobFiles());
152 assertEquals("After compaction: rows", compactionThreshold, countRows());
153 assertEquals("After compaction: mob rows", 0, countMobRows());
154 }
155
156
157
158
159 @Test
160 public void testLargerValue() throws Exception {
161 init(UTIL.getConfiguration(), 200);
162 byte[] dummyData = makeDummyData(300);
163 HRegionIncommon loader = new HRegionIncommon(region);
164 for (int i = 0; i < compactionThreshold; i++) {
165 Put p = createPut(i, dummyData);
166 loader.put(p);
167 loader.flushcache();
168 }
169 assertEquals("Before compaction: store files", compactionThreshold, countStoreFiles());
170 assertEquals("Before compaction: mob file count", compactionThreshold, countMobFiles());
171 assertEquals("Before compaction: rows", compactionThreshold, countRows());
172 assertEquals("Before compaction: mob rows", compactionThreshold, countMobRows());
173 assertEquals("Before compaction: number of mob cells", compactionThreshold,
174 countMobCellsInMetadata());
175
176 region.getTableDesc().getFamily(COLUMN_FAMILY).setMobThreshold(500);
177 region.initialize();
178 region.compactStores();
179
180 assertEquals("After compaction: store files", 1, countStoreFiles());
181 assertEquals("After compaction: mob file count", compactionThreshold, countMobFiles());
182 assertEquals("After compaction: referenced mob file count", 0, countReferencedMobFiles());
183 assertEquals("After compaction: rows", compactionThreshold, countRows());
184 assertEquals("After compaction: mob rows", 0, countMobRows());
185 }
186
187
188
189
190
191 @Test
192 public void testMobCompactionWithBulkload() throws Exception {
193
194 init(UTIL.getConfiguration(), 300);
195 byte[] dummyData = makeDummyData(600);
196
197 Path hbaseRootDir = FSUtils.getRootDir(conf);
198 Path basedir = new Path(hbaseRootDir, htd.getNameAsString());
199 List<Pair<byte[], String>> hfiles = new ArrayList<Pair<byte[], String>>(1);
200 for (int i = 0; i < compactionThreshold; i++) {
201 Path hpath = new Path(basedir, "hfile" + i);
202 hfiles.add(Pair.newPair(COLUMN_FAMILY, hpath.toString()));
203 createHFile(hpath, i, dummyData);
204 }
205
206
207
208 boolean result = region.bulkLoadHFiles(hfiles, true, null);
209 assertTrue("Bulkload result:", result);
210 assertEquals("Before compaction: store files", compactionThreshold, countStoreFiles());
211 assertEquals("Before compaction: mob file count", 0, countMobFiles());
212 assertEquals("Before compaction: rows", compactionThreshold, countRows());
213 assertEquals("Before compaction: mob rows", 0, countMobRows());
214 assertEquals("Before compaction: referenced mob file count", 0, countReferencedMobFiles());
215
216 region.compactStores();
217
218 assertEquals("After compaction: store files", 1, countStoreFiles());
219 assertEquals("After compaction: mob file count:", 1, countMobFiles());
220 assertEquals("After compaction: rows", compactionThreshold, countRows());
221 assertEquals("After compaction: mob rows", compactionThreshold, countMobRows());
222 assertEquals("After compaction: referenced mob file count", 1, countReferencedMobFiles());
223 assertEquals("After compaction: number of mob cells", compactionThreshold,
224 countMobCellsInMetadata());
225 }
226
227 @Test
228 public void testMajorCompactionAfterDelete() throws Exception {
229 init(UTIL.getConfiguration(), 100);
230 byte[] dummyData = makeDummyData(200);
231 HRegionIncommon loader = new HRegionIncommon(region);
232
233 int numHfiles = compactionThreshold - 1;
234 byte[] deleteRow = Bytes.add(STARTROW, Bytes.toBytes(0));
235 for (int i = 0; i < numHfiles; i++) {
236 Put p = createPut(i, dummyData);
237 loader.put(p);
238 loader.flushcache();
239 }
240 assertEquals("Before compaction: store files", numHfiles, countStoreFiles());
241 assertEquals("Before compaction: mob file count", numHfiles, countMobFiles());
242 assertEquals("Before compaction: rows", numHfiles, countRows());
243 assertEquals("Before compaction: mob rows", numHfiles, countMobRows());
244 assertEquals("Before compaction: number of mob cells", numHfiles, countMobCellsInMetadata());
245
246 Delete delete = new Delete(deleteRow);
247 delete.deleteFamily(COLUMN_FAMILY);
248 region.delete(delete);
249 loader.flushcache();
250
251 assertEquals("Before compaction: store files", numHfiles + 1, countStoreFiles());
252 assertEquals("Before compaction: mob files", numHfiles, countMobFiles());
253 region.compact(true);
254 assertEquals("After compaction: store files", 1, countStoreFiles());
255
256 assertEquals("After compaction: mob files", numHfiles + 1, countMobFiles());
257
258 Scan scan = new Scan();
259 scan.setRaw(true);
260 InternalScanner scanner = region.getScanner(scan);
261 List<Cell> results = new ArrayList<Cell>();
262 scanner.next(results);
263 int deleteCount = 0;
264 while (!results.isEmpty()) {
265 for (Cell c : results) {
266 if (c.getTypeByte() == KeyValue.Type.DeleteFamily.getCode()) {
267 deleteCount++;
268 assertTrue(Bytes.equals(CellUtil.cloneRow(c), deleteRow));
269 }
270 }
271 results.clear();
272 scanner.next(results);
273 }
274
275 assertEquals(0, deleteCount);
276 scanner.close();
277
278 assertEquals("The cells in mob files", numHfiles - 1, countMobCellsInMobFiles(1));
279 }
280
281 private int countStoreFiles() throws IOException {
282 Store store = region.getStore(COLUMN_FAMILY);
283 return store.getStorefilesCount();
284 }
285
286 private int countMobFiles() throws IOException {
287 Path mobDirPath = new Path(MobUtils.getMobRegionPath(conf, htd.getTableName()),
288 hcd.getNameAsString());
289 if (fs.exists(mobDirPath)) {
290 FileStatus[] files = UTIL.getTestFileSystem().listStatus(mobDirPath);
291 return files.length;
292 }
293 return 0;
294 }
295
296 private long countMobCellsInMetadata() throws IOException {
297 long mobCellsCount = 0;
298 Path mobDirPath = new Path(MobUtils.getMobRegionPath(conf, htd.getTableName()),
299 hcd.getNameAsString());
300 Configuration copyOfConf = new Configuration(conf);
301 copyOfConf.setFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY, 0f);
302 CacheConfig cacheConfig = new CacheConfig(copyOfConf);
303 if (fs.exists(mobDirPath)) {
304 FileStatus[] files = UTIL.getTestFileSystem().listStatus(mobDirPath);
305 for (FileStatus file : files) {
306 StoreFile sf = new StoreFile(fs, file.getPath(), conf, cacheConfig, BloomType.NONE);
307 Map<byte[], byte[]> fileInfo = sf.createReader().loadFileInfo();
308 byte[] count = fileInfo.get(StoreFile.MOB_CELLS_COUNT);
309 assertTrue(count != null);
310 mobCellsCount += Bytes.toLong(count);
311 }
312 }
313 return mobCellsCount;
314 }
315
316 private Put createPut(int rowIdx, byte[] dummyData) throws IOException {
317 Put p = new Put(Bytes.add(STARTROW, Bytes.toBytes(rowIdx)));
318 p.setDurability(Durability.SKIP_WAL);
319 p.add(COLUMN_FAMILY, Bytes.toBytes("colX"), dummyData);
320 return p;
321 }
322
323
324
325
326 private void createHFile(Path path, int rowIdx, byte[] dummyData) throws IOException {
327 HFileContext meta = new HFileContextBuilder().build();
328 HFile.Writer writer = HFile.getWriterFactory(conf, new CacheConfig(conf)).withPath(fs, path)
329 .withFileContext(meta).create();
330 long now = System.currentTimeMillis();
331 try {
332 KeyValue kv = new KeyValue(Bytes.add(STARTROW, Bytes.toBytes(rowIdx)), COLUMN_FAMILY,
333 Bytes.toBytes("colX"), now, dummyData);
334 writer.append(kv);
335 } finally {
336 writer.appendFileInfo(StoreFile.BULKLOAD_TIME_KEY, Bytes.toBytes(System.currentTimeMillis()));
337 writer.close();
338 }
339 }
340
341 private int countMobRows() throws IOException {
342 Scan scan = new Scan();
343
344 scan.setAttribute(MobConstants.MOB_SCAN_RAW, Bytes.toBytes(Boolean.TRUE));
345 InternalScanner scanner = region.getScanner(scan);
346
347 int scannedCount = 0;
348 List<Cell> results = new ArrayList<Cell>();
349 boolean hasMore = true;
350 while (hasMore) {
351 hasMore = scanner.next(results);
352 for (Cell c : results) {
353 if (MobUtils.isMobReferenceCell(c)) {
354 scannedCount++;
355 }
356 }
357 results.clear();
358 }
359 scanner.close();
360
361 return scannedCount;
362 }
363
364 private int countRows() throws IOException {
365 Scan scan = new Scan();
366
367 InternalScanner scanner = region.getScanner(scan);
368
369 int scannedCount = 0;
370 List<Cell> results = new ArrayList<Cell>();
371 boolean hasMore = true;
372 while (hasMore) {
373 hasMore = scanner.next(results);
374 scannedCount += results.size();
375 results.clear();
376 }
377 scanner.close();
378
379 return scannedCount;
380 }
381
382 private byte[] makeDummyData(int size) {
383 byte[] dummyData = new byte[size];
384 new Random().nextBytes(dummyData);
385 return dummyData;
386 }
387
388 private int countReferencedMobFiles() throws IOException {
389 Scan scan = new Scan();
390
391 scan.setAttribute(MobConstants.MOB_SCAN_RAW, Bytes.toBytes(Boolean.TRUE));
392 InternalScanner scanner = region.getScanner(scan);
393
394 List<Cell> kvs = new ArrayList<Cell>();
395 boolean hasMore = true;
396 String fileName;
397 Set<String> files = new HashSet<String>();
398 do {
399 kvs.clear();
400 hasMore = scanner.next(kvs);
401 for (Cell c : kvs) {
402 KeyValue kv = KeyValueUtil.ensureKeyValue(c);
403 if (!MobUtils.isMobReferenceCell(kv)) {
404 continue;
405 }
406 if (!MobUtils.hasValidMobRefCellValue(kv)) {
407 continue;
408 }
409 int size = MobUtils.getMobValueLength(kv);
410 if (size <= mobCellThreshold) {
411 continue;
412 }
413 fileName = MobUtils.getMobFileName(kv);
414 if (fileName.isEmpty()) {
415 continue;
416 }
417 files.add(fileName);
418 Path familyPath = MobUtils.getMobFamilyPath(conf, htd.getTableName(),
419 hcd.getNameAsString());
420 assertTrue(fs.exists(new Path(familyPath, fileName)));
421 }
422 } while (hasMore);
423
424 scanner.close();
425
426 return files.size();
427 }
428
429 private int countMobCellsInMobFiles(int expectedNumDelfiles) throws IOException {
430 Configuration copyOfConf = new Configuration(conf);
431 copyOfConf.setFloat(HConstants.HFILE_BLOCK_CACHE_SIZE_KEY, 0f);
432 CacheConfig cacheConfig = new CacheConfig(copyOfConf);
433 Path mobDirPath = new Path(MobUtils.getMobRegionPath(conf, htd.getTableName()),
434 hcd.getNameAsString());
435 List<StoreFile> sfs = new ArrayList<StoreFile>();
436 int numDelfiles = 0;
437 int size = 0;
438 if (fs.exists(mobDirPath)) {
439 for (FileStatus f : fs.listStatus(mobDirPath)) {
440 StoreFile sf = new StoreFile(fs, f.getPath(), conf, cacheConfig, BloomType.NONE);
441 sfs.add(sf);
442 if (StoreFileInfo.isDelFile(sf.getPath())) {
443 numDelfiles++;
444 }
445 }
446 List scanners = StoreFileScanner.getScannersForStoreFiles(sfs, false, true, false, false,
447 HConstants.LATEST_TIMESTAMP);
448 Scan scan = new Scan();
449 scan.setMaxVersions(hcd.getMaxVersions());
450 long timeToPurgeDeletes = Math.max(conf.getLong("hbase.hstore.time.to.purge.deletes", 0), 0);
451 long ttl = HStore.determineTTLFromFamily(hcd);
452 ScanInfo scanInfo = new ScanInfo(conf, hcd, ttl, timeToPurgeDeletes, KeyValue.COMPARATOR);
453 StoreScanner scanner = new StoreScanner(scan, scanInfo, ScanType.COMPACT_DROP_DELETES, null,
454 scanners, 0L, HConstants.LATEST_TIMESTAMP);
455 List<Cell> results = new ArrayList<>();
456 boolean hasMore = true;
457 while (hasMore) {
458 hasMore = scanner.next(results);
459 size += results.size();
460 results.clear();
461 }
462 }
463
464 assertEquals(expectedNumDelfiles, numDelfiles);
465 return size;
466 }
467 }