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.io.IOException;
22 import java.util.List;
23 import java.util.Random;
24
25 import org.apache.hadoop.conf.Configuration;
26 import org.apache.hadoop.fs.FileStatus;
27 import org.apache.hadoop.fs.FileSystem;
28 import org.apache.hadoop.fs.Path;
29 import org.apache.hadoop.hbase.Cell;
30 import org.apache.hadoop.hbase.CellUtil;
31 import org.apache.hadoop.hbase.HBaseTestingUtility;
32 import org.apache.hadoop.hbase.HColumnDescriptor;
33 import org.apache.hadoop.hbase.HRegionInfo;
34 import org.apache.hadoop.hbase.HTableDescriptor;
35 import org.apache.hadoop.hbase.TableName;
36 import org.apache.hadoop.hbase.client.*;
37 import org.apache.hadoop.hbase.io.hfile.CorruptHFileException;
38 import org.apache.hadoop.hbase.io.hfile.TestHFile;
39 import org.apache.hadoop.hbase.mob.MobConstants;
40 import org.apache.hadoop.hbase.mob.MobUtils;
41 import org.apache.hadoop.hbase.testclassification.MediumTests;
42 import org.apache.hadoop.hbase.util.Bytes;
43 import org.apache.hadoop.hbase.util.FSUtils;
44 import org.apache.hadoop.hbase.util.HFileArchiveUtil;
45 import org.junit.AfterClass;
46 import org.junit.Assert;
47 import org.junit.BeforeClass;
48 import org.junit.Test;
49 import org.junit.experimental.categories.Category;
50
51 @Category(MediumTests.class)
52 public class TestMobStoreScanner {
53
54 private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
55 private final static byte [] row1 = Bytes.toBytes("row1");
56 private final static byte [] row2 = Bytes.toBytes("row2");
57 private final static byte [] family = Bytes.toBytes("family");
58 private final static byte [] qf1 = Bytes.toBytes("qualifier1");
59 private final static byte [] qf2 = Bytes.toBytes("qualifier2");
60 protected final byte[] qf3 = Bytes.toBytes("qualifier3");
61 private static HTable table;
62 private static HBaseAdmin admin;
63 private static HColumnDescriptor hcd;
64 private static HTableDescriptor desc;
65 private static Random random = new Random();
66 private static long defaultThreshold = 10;
67 private FileSystem fs;
68 private Configuration conf;
69
70 @BeforeClass
71 public static void setUpBeforeClass() throws Exception {
72 TEST_UTIL.getConfiguration().setInt("hfile.format.version", 3);
73 TEST_UTIL.getConfiguration().setInt("hbase.master.info.port", 0);
74 TEST_UTIL.getConfiguration().setBoolean("hbase.regionserver.info.port.auto", true);
75 TEST_UTIL.getConfiguration().setInt("hbase.client.keyvalue.maxsize", 100 * 1024 * 1024);
76
77 TEST_UTIL.startMiniCluster(1);
78 }
79
80 @AfterClass
81 public static void tearDownAfterClass() throws Exception {
82 TEST_UTIL.shutdownMiniCluster();
83 }
84
85 public void setUp(long threshold, TableName tn) throws Exception {
86 conf = TEST_UTIL.getConfiguration();
87 fs = FileSystem.get(conf);
88 desc = new HTableDescriptor(tn);
89 hcd = new HColumnDescriptor(family);
90 hcd.setMobEnabled(true);
91 hcd.setMobThreshold(threshold);
92 hcd.setMaxVersions(4);
93 desc.addFamily(hcd);
94 admin = new HBaseAdmin(TEST_UTIL.getConfiguration());
95 admin.createTable(desc);
96 table = new HTable(TEST_UTIL.getConfiguration(), tn);
97 }
98
99
100
101
102
103
104
105 private static byte[] generateMobValue(int size) {
106 byte[] mobVal = new byte[size];
107 random.nextBytes(mobVal);
108 return mobVal;
109 }
110
111
112
113
114
115
116
117
118 public void setScan(Scan scan, boolean reversed, boolean mobScanRaw) {
119 scan.setReversed(reversed);
120 scan.setMaxVersions(4);
121 if(mobScanRaw) {
122 scan.setAttribute(MobConstants.MOB_SCAN_RAW, Bytes.toBytes(Boolean.TRUE));
123 }
124 }
125
126 @Test
127 public void testMobStoreScanner() throws Exception {
128 testGetFromFiles(false);
129 testGetFromMemStore(false);
130 testGetReferences(false);
131 testMobThreshold(false);
132 testGetFromArchive(false);
133 }
134
135 @Test
136 public void testReversedMobStoreScanner() throws Exception {
137 testGetFromFiles(true);
138 testGetFromMemStore(true);
139 testGetReferences(true);
140 testMobThreshold(true);
141 testGetFromArchive(true);
142 }
143
144 @Test(timeout=60000)
145 public void testGetMassive() throws Exception {
146 setUp(defaultThreshold, TableName.valueOf("testGetMassive"));
147
148
149
150 byte[] bigValue = new byte[25*1024*1024];
151
152 Put put = new Put(row1);
153 put.add(family, qf1, bigValue);
154 put.add(family, qf2, bigValue);
155 put.add(family, qf3, bigValue);
156 table.put(put);
157
158 Get g = new Get(row1);
159 Result r = table.get(g);
160
161 }
162
163 @Test
164 public void testReadPt() throws Exception {
165 TableName tn = TableName.valueOf("testReadPt");
166 setUp(0L, tn);
167 long ts = System.currentTimeMillis();
168 byte[] value1 = Bytes.toBytes("value1");
169 Put put1 = new Put(row1);
170 put1.addColumn(family, qf1, ts, value1);
171 table.put(put1);
172 Put put2 = new Put(row2);
173 byte[] value2 = Bytes.toBytes("value2");
174 put2.addColumn(family, qf1, ts, value2);
175 table.put(put2);
176
177 Scan scan = new Scan();
178 scan.setCaching(1);
179 ResultScanner rs = table.getScanner(scan);
180
181 Put put3 = new Put(row1);
182 byte[] value3 = Bytes.toBytes("value3");
183 put3.addColumn(family, qf1, ts, value3);
184 table.put(put3);
185 Put put4 = new Put(row2);
186 byte[] value4 = Bytes.toBytes("value4");
187 put4.addColumn(family, qf1, ts, value4);
188 table.put(put4);
189 Result result = rs.next();
190 Cell cell = result.getColumnLatestCell(family, qf1);
191 Assert.assertEquals("value1", Bytes.toString(cell.getValue()));
192
193 admin.flush(tn);
194 result = rs.next();
195 cell = result.getColumnLatestCell(family, qf1);
196 Assert.assertEquals("value2", Bytes.toString(cell.getValue()));
197 }
198
199 @Test
200 public void testReadFromCorruptMobFilesWithReadEmptyValueOnMobCellMiss() throws Exception {
201 TableName tn = TableName.valueOf("testReadFromCorruptMobFilesWithReadEmptyValueOnMobCellMiss");
202 setUp(0, tn);
203 createRecordAndCorruptMobFile(tn, row1, family, qf1, Bytes.toBytes("value1"));
204 Get get = new Get(row1);
205 get.setAttribute(MobConstants.EMPTY_VALUE_ON_MOBCELL_MISS, Bytes.toBytes(true));
206 Result result = table.get(get);
207 Cell cell = result.getColumnLatestCell(family, qf1);
208 Assert.assertEquals(0, CellUtil.cloneValue(cell).length);
209 }
210
211 @Test
212 public void testReadFromCorruptMobFiles() throws Exception {
213 TableName tn = TableName.valueOf("testReadFromCorruptMobFiles");
214 setUp(0, tn);
215 createRecordAndCorruptMobFile(tn, row1, family, qf1, Bytes.toBytes("value1"));
216 Get get = new Get(row1);
217 IOException ioe = null;
218 try {
219 table.get(get);
220 } catch (IOException e) {
221 ioe = e;
222 }
223 Assert.assertNotNull(ioe);
224 Assert.assertEquals(CorruptHFileException.class.getName(), ioe.getClass().getName());
225 }
226
227 private void createRecordAndCorruptMobFile(TableName tn, byte[] row, byte[] family, byte[] qf,
228 byte[] value) throws IOException {
229 Put put1 = new Put(row);
230 put1.addColumn(family, qf, value);
231 table.put(put1);
232 admin.flush(tn);
233 Path mobFile = getFlushedMobFile(conf, fs, tn, Bytes.toString(family));
234 Assert.assertNotNull(mobFile);
235
236 Path corruptFile = new Path(mobFile.getParent(), "dummy");
237 TestHFile.truncateFile(fs, mobFile, corruptFile);
238 fs.delete(mobFile, true);
239 fs.rename(corruptFile, mobFile);
240 }
241
242 private Path getFlushedMobFile(Configuration conf, FileSystem fs, TableName table, String family)
243 throws IOException {
244 Path regionDir = MobUtils.getMobRegionPath(conf, table);
245 Path famDir = new Path(regionDir, family);
246 FileStatus[] hfFss = fs.listStatus(famDir);
247 for (FileStatus hfs : hfFss) {
248 if (!hfs.isDirectory()) {
249 return hfs.getPath();
250 }
251 }
252 return null;
253 }
254
255 private void testGetFromFiles(boolean reversed) throws Exception {
256 TableName tn = TableName.valueOf("testGetFromFiles" + reversed);
257 setUp(defaultThreshold, tn);
258 long ts1 = System.currentTimeMillis();
259 long ts2 = ts1 + 1;
260 long ts3 = ts1 + 2;
261 byte [] value = generateMobValue((int)defaultThreshold+1);
262
263 Put put1 = new Put(row1);
264 put1.add(family, qf1, ts3, value);
265 put1.add(family, qf2, ts2, value);
266 put1.add(family, qf3, ts1, value);
267 table.put(put1);
268
269 table.flushCommits();
270 admin.flush(tn);
271
272 Scan scan = new Scan();
273 setScan(scan, reversed, false);
274
275 ResultScanner results = table.getScanner(scan);
276 int count = 0;
277 for (Result res : results) {
278 List<Cell> cells = res.listCells();
279 for(Cell cell : cells) {
280
281 Assert.assertEquals(Bytes.toString(value),
282 Bytes.toString(CellUtil.cloneValue(cell)));
283 count++;
284 }
285 }
286 results.close();
287 Assert.assertEquals(3, count);
288 }
289
290 private void testGetFromMemStore(boolean reversed) throws Exception {
291 setUp(defaultThreshold, TableName.valueOf("testGetFromMemStore" + reversed));
292 long ts1 = System.currentTimeMillis();
293 long ts2 = ts1 + 1;
294 long ts3 = ts1 + 2;
295 byte [] value = generateMobValue((int)defaultThreshold+1);;
296
297 Put put1 = new Put(row1);
298 put1.add(family, qf1, ts3, value);
299 put1.add(family, qf2, ts2, value);
300 put1.add(family, qf3, ts1, value);
301 table.put(put1);
302
303 Scan scan = new Scan();
304 setScan(scan, reversed, false);
305
306 ResultScanner results = table.getScanner(scan);
307 int count = 0;
308 for (Result res : results) {
309 List<Cell> cells = res.listCells();
310 for(Cell cell : cells) {
311
312 Assert.assertEquals(Bytes.toString(value),
313 Bytes.toString(CellUtil.cloneValue(cell)));
314 count++;
315 }
316 }
317 results.close();
318 Assert.assertEquals(3, count);
319 }
320
321 public void testGetReferences(boolean reversed) throws Exception {
322 TableName tn = TableName.valueOf("testGetReferences" + reversed);
323 setUp(defaultThreshold, tn);
324 long ts1 = System.currentTimeMillis();
325 long ts2 = ts1 + 1;
326 long ts3 = ts1 + 2;
327 byte [] value = generateMobValue((int)defaultThreshold+1);;
328
329 Put put1 = new Put(row1);
330 put1.add(family, qf1, ts3, value);
331 put1.add(family, qf2, ts2, value);
332 put1.add(family, qf3, ts1, value);
333 table.put(put1);
334
335 table.flushCommits();
336 admin.flush(tn);
337
338 Scan scan = new Scan();
339 setScan(scan, reversed, true);
340
341 ResultScanner results = table.getScanner(scan);
342 int count = 0;
343 for (Result res : results) {
344 List<Cell> cells = res.listCells();
345 for(Cell cell : cells) {
346
347 assertIsMobReference(cell, row1, family, value, tn);
348 count++;
349 }
350 }
351 results.close();
352 Assert.assertEquals(3, count);
353 }
354
355 private void testMobThreshold(boolean reversed) throws Exception {
356 TableName tn = TableName.valueOf("testMobThreshold" + reversed);
357 setUp(defaultThreshold, tn);
358 byte [] valueLess = generateMobValue((int)defaultThreshold-1);
359 byte [] valueEqual = generateMobValue((int)defaultThreshold);
360 byte [] valueGreater = generateMobValue((int)defaultThreshold+1);
361 long ts1 = System.currentTimeMillis();
362 long ts2 = ts1 + 1;
363 long ts3 = ts1 + 2;
364
365 Put put1 = new Put(row1);
366 put1.add(family, qf1, ts3, valueLess);
367 put1.add(family, qf2, ts2, valueEqual);
368 put1.add(family, qf3, ts1, valueGreater);
369 table.put(put1);
370
371 table.flushCommits();
372 admin.flush(tn);
373
374 Scan scan = new Scan();
375 setScan(scan, reversed, true);
376
377 Cell cellLess= null;
378 Cell cellEqual = null;
379 Cell cellGreater = null;
380 ResultScanner results = table.getScanner(scan);
381 int count = 0;
382 for (Result res : results) {
383 List<Cell> cells = res.listCells();
384 for(Cell cell : cells) {
385
386 String qf = Bytes.toString(CellUtil.cloneQualifier(cell));
387 if(qf.equals(Bytes.toString(qf1))) {
388 cellLess = cell;
389 }
390 if(qf.equals(Bytes.toString(qf2))) {
391 cellEqual = cell;
392 }
393 if(qf.equals(Bytes.toString(qf3))) {
394 cellGreater = cell;
395 }
396 count++;
397 }
398 }
399 Assert.assertEquals(3, count);
400 assertNotMobReference(cellLess, row1, family, valueLess);
401 assertNotMobReference(cellEqual, row1, family, valueEqual);
402 assertIsMobReference(cellGreater, row1, family, valueGreater, tn);
403 results.close();
404 }
405
406 private void testGetFromArchive(boolean reversed) throws Exception {
407 TableName tn = TableName.valueOf("testGetFromArchive" + reversed);
408 setUp(defaultThreshold, tn);
409 long ts1 = System.currentTimeMillis();
410 long ts2 = ts1 + 1;
411 long ts3 = ts1 + 2;
412 byte [] value = generateMobValue((int)defaultThreshold+1);;
413
414 Put put1 = new Put(row1);
415 put1.add(family, qf1, ts3, value);
416 put1.add(family, qf2, ts2, value);
417 put1.add(family, qf3, ts1, value);
418 table.put(put1);
419
420 table.flushCommits();
421 admin.flush(tn);
422
423
424 Path mobFamilyPath;
425 mobFamilyPath = new Path(MobUtils.getMobRegionPath(TEST_UTIL.getConfiguration(), tn),
426 hcd.getNameAsString());
427 FileSystem fs = FileSystem.get(TEST_UTIL.getConfiguration());
428 FileStatus[] files = fs.listStatus(mobFamilyPath);
429
430
431 Path rootDir = FSUtils.getRootDir(TEST_UTIL.getConfiguration());
432 Path tableDir = FSUtils.getTableDir(rootDir, tn);
433 HRegionInfo regionInfo = MobUtils.getMobRegionInfo(tn);
434 Path storeArchiveDir = HFileArchiveUtil.getStoreArchivePath(TEST_UTIL.getConfiguration(),
435 regionInfo, tableDir, family);
436
437
438 fs.mkdirs(storeArchiveDir);
439 int fileCount = 0;
440 for(FileStatus file : files) {
441 fileCount++;
442 Path filePath = file.getPath();
443 Path src = new Path(mobFamilyPath, filePath.getName());
444 Path dst = new Path(storeArchiveDir, filePath.getName());
445 fs.rename(src, dst);
446 }
447
448
449 FileStatus[] files1 = fs.listStatus(mobFamilyPath);
450 Assert.assertEquals(0, files1.length);
451 FileStatus[] files2 = fs.listStatus(storeArchiveDir);
452 Assert.assertEquals(fileCount, files2.length);
453
454
455 Scan scan = new Scan();
456 setScan(scan, reversed, false);
457 ResultScanner results = table.getScanner(scan);
458 int count = 0;
459 for (Result res : results) {
460 List<Cell> cells = res.listCells();
461 for(Cell cell : cells) {
462
463 Assert.assertEquals(Bytes.toString(value),
464 Bytes.toString(CellUtil.cloneValue(cell)));
465 count++;
466 }
467 }
468 results.close();
469 Assert.assertEquals(3, count);
470 }
471
472
473
474
475 private static void assertNotMobReference(Cell cell, byte[] row, byte[] family,
476 byte[] value) throws IOException {
477 Assert.assertEquals(Bytes.toString(row),
478 Bytes.toString(CellUtil.cloneRow(cell)));
479 Assert.assertEquals(Bytes.toString(family),
480 Bytes.toString(CellUtil.cloneFamily(cell)));
481 Assert.assertTrue(Bytes.toString(value).equals(
482 Bytes.toString(CellUtil.cloneValue(cell))));
483 }
484
485
486
487
488 private static void assertIsMobReference(Cell cell, byte[] row, byte[] family,
489 byte[] value, TableName tn) throws IOException {
490 Assert.assertEquals(Bytes.toString(row),
491 Bytes.toString(CellUtil.cloneRow(cell)));
492 Assert.assertEquals(Bytes.toString(family),
493 Bytes.toString(CellUtil.cloneFamily(cell)));
494 Assert.assertFalse(Bytes.toString(value).equals(
495 Bytes.toString(CellUtil.cloneValue(cell))));
496 byte[] referenceValue = CellUtil.cloneValue(cell);
497 String fileName = Bytes.toString(referenceValue, Bytes.SIZEOF_INT,
498 referenceValue.length - Bytes.SIZEOF_INT);
499 int valLen = Bytes.toInt(referenceValue, 0, Bytes.SIZEOF_INT);
500 Assert.assertEquals(value.length, valLen);
501 Path mobFamilyPath;
502 mobFamilyPath = new Path(MobUtils.getMobRegionPath(TEST_UTIL.getConfiguration(),
503 tn), hcd.getNameAsString());
504 Path targetPath = new Path(mobFamilyPath, fileName);
505 FileSystem fs = FileSystem.get(TEST_UTIL.getConfiguration());
506 Assert.assertTrue(fs.exists(targetPath));
507 }
508 }