1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.regionserver;
19
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertNull;
22 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assert.fail;
24
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.concurrent.Callable;
29
30 import org.apache.commons.lang.exception.ExceptionUtils;
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.apache.commons.logging.impl.Log4JLogger;
34 import org.apache.hadoop.conf.Configuration;
35 import org.apache.hadoop.fs.FileSystem;
36 import org.apache.hadoop.fs.Path;
37 import org.apache.hadoop.hbase.Cell;
38 import org.apache.hadoop.hbase.CellUtil;
39 import org.apache.hadoop.hbase.CoordinatedStateManager;
40 import org.apache.hadoop.hbase.HBaseTestingUtility;
41 import org.apache.hadoop.hbase.HConstants;
42 import org.apache.hadoop.hbase.HRegionInfo;
43 import org.apache.hadoop.hbase.HTableDescriptor;
44 import org.apache.hadoop.hbase.HTestConst;
45 import org.apache.hadoop.hbase.KeyValue;
46 import org.apache.hadoop.hbase.KeyValue.KVComparator;
47 import org.apache.hadoop.hbase.TableName;
48 import org.apache.hadoop.hbase.client.Put;
49 import org.apache.hadoop.hbase.client.Result;
50 import org.apache.hadoop.hbase.client.ResultScanner;
51 import org.apache.hadoop.hbase.client.Scan;
52 import org.apache.hadoop.hbase.client.ScannerCallable;
53 import org.apache.hadoop.hbase.client.Table;
54 import org.apache.hadoop.hbase.filter.Filter;
55 import org.apache.hadoop.hbase.filter.FilterBase;
56 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ScanRequest;
57 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.ScanResponse;
58 import org.apache.hadoop.hbase.regionserver.HRegion.RegionScannerImpl;
59 import org.apache.hadoop.hbase.testclassification.MediumTests;
60 import org.apache.hadoop.hbase.util.Bytes;
61 import org.apache.hadoop.hbase.wal.WAL;
62 import org.apache.log4j.Level;
63 import org.junit.After;
64 import org.junit.AfterClass;
65 import org.junit.Before;
66 import org.junit.BeforeClass;
67 import org.junit.Test;
68 import org.junit.experimental.categories.Category;
69
70 import com.google.protobuf.RpcController;
71 import com.google.protobuf.ServiceException;
72
73
74
75
76
77
78
79
80
81 @Category(MediumTests.class)
82 public class TestScannerHeartbeatMessages {
83 private static final Log LOG = LogFactory.getLog(TestScannerHeartbeatMessages.class);
84
85 private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
86
87 private static Table TABLE = null;
88
89
90
91
92 private static TableName TABLE_NAME = TableName.valueOf("testScannerHeartbeatMessagesTable");
93
94 private static int NUM_ROWS = 5;
95 private static byte[] ROW = Bytes.toBytes("testRow");
96 private static byte[][] ROWS = HTestConst.makeNAscii(ROW, NUM_ROWS);
97
98 private static int NUM_FAMILIES = 4;
99 private static byte[] FAMILY = Bytes.toBytes("testFamily");
100 private static byte[][] FAMILIES = HTestConst.makeNAscii(FAMILY, NUM_FAMILIES);
101
102 private static int NUM_QUALIFIERS = 3;
103 private static byte[] QUALIFIER = Bytes.toBytes("testQualifier");
104 private static byte[][] QUALIFIERS = HTestConst.makeNAscii(QUALIFIER, NUM_QUALIFIERS);
105
106 private static int VALUE_SIZE = 128;
107 private static byte[] VALUE = Bytes.createMaxByteArray(VALUE_SIZE);
108
109
110
111 private static int SERVER_TIMEOUT = 60000;
112
113
114
115
116 private static int CLIENT_TIMEOUT = 1000;
117
118
119
120
121
122 private static int DEFAULT_ROW_SLEEP_TIME = 200;
123
124
125 private static int DEFAULT_CF_SLEEP_TIME = 200;
126
127 @BeforeClass
128 public static void setUpBeforeClass() throws Exception {
129 ((Log4JLogger) ScannerCallable.LOG).getLogger().setLevel(Level.ALL);
130 ((Log4JLogger) HeartbeatRPCServices.LOG).getLogger().setLevel(Level.ALL);
131 Configuration conf = TEST_UTIL.getConfiguration();
132
133 conf.setStrings(HConstants.REGION_IMPL, HeartbeatHRegion.class.getName());
134 conf.setStrings(HConstants.REGION_SERVER_IMPL, HeartbeatHRegionServer.class.getName());
135 conf.setInt(HConstants.HBASE_CLIENT_SCANNER_TIMEOUT_PERIOD, SERVER_TIMEOUT);
136 conf.setInt(HConstants.HBASE_RPC_TIMEOUT_KEY, SERVER_TIMEOUT);
137 conf.setInt(HConstants.HBASE_CLIENT_PAUSE, 1);
138
139
140 conf.setLong(StoreScanner.HBASE_CELLS_SCANNED_PER_HEARTBEAT_CHECK, 1);
141 TEST_UTIL.startMiniCluster(1);
142
143 TABLE = createTestTable(TABLE_NAME, ROWS, FAMILIES, QUALIFIERS, VALUE);
144 }
145
146 static Table createTestTable(TableName name, byte[][] rows, byte[][] families,
147 byte[][] qualifiers, byte[] cellValue) throws IOException {
148 Table ht = TEST_UTIL.createTable(name, families);
149 List<Put> puts = createPuts(rows, families, qualifiers, cellValue);
150 ht.put(puts);
151 ht.getConfiguration().setInt(HConstants.HBASE_CLIENT_SCANNER_TIMEOUT_PERIOD, CLIENT_TIMEOUT);
152 return ht;
153 }
154
155
156
157
158
159
160
161
162
163
164 static ArrayList<Put> createPuts(byte[][] rows, byte[][] families, byte[][] qualifiers,
165 byte[] value) throws IOException {
166 Put put;
167 ArrayList<Put> puts = new ArrayList<>();
168
169 for (int row = 0; row < rows.length; row++) {
170 put = new Put(rows[row]);
171 for (int fam = 0; fam < families.length; fam++) {
172 for (int qual = 0; qual < qualifiers.length; qual++) {
173 KeyValue kv = new KeyValue(rows[row], families[fam], qualifiers[qual], qual, value);
174 put.add(kv);
175 }
176 }
177 puts.add(put);
178 }
179
180 return puts;
181 }
182
183 @AfterClass
184 public static void tearDownAfterClass() throws Exception {
185 TEST_UTIL.deleteTable(TABLE_NAME);
186 TEST_UTIL.shutdownMiniCluster();
187 }
188
189 @Before
190 public void setupBeforeTest() throws Exception {
191 disableSleeping();
192 }
193
194 @After
195 public void teardownAfterTest() throws Exception {
196 disableSleeping();
197 }
198
199
200
201
202
203
204
205 @Test
206 public void testScannerHeartbeatMessages() throws Exception {
207 testImportanceOfHeartbeats(testHeartbeatBetweenRows());
208 testImportanceOfHeartbeats(testHeartbeatBetweenColumnFamilies());
209 testImportanceOfHeartbeats(testHeartbeatWithSparseFilter());
210 }
211
212
213
214
215
216
217
218
219 public void testImportanceOfHeartbeats(Callable<Void> testCallable) throws InterruptedException {
220 HeartbeatRPCServices.heartbeatsEnabled = true;
221
222 try {
223 testCallable.call();
224 } catch (Exception e) {
225 fail("Heartbeat messages are enabled, exceptions should NOT be thrown. Exception trace:"
226 + ExceptionUtils.getStackTrace(e));
227 }
228
229 HeartbeatRPCServices.heartbeatsEnabled = false;
230 try {
231 testCallable.call();
232 } catch (Exception e) {
233 return;
234 } finally {
235 HeartbeatRPCServices.heartbeatsEnabled = true;
236 }
237 fail("Heartbeats messages are disabled, an exception should be thrown. If an exception "
238 + " is not thrown, the test case is not testing the importance of heartbeat messages");
239 }
240
241
242
243
244
245
246 public Callable<Void> testHeartbeatBetweenRows() throws Exception {
247 return new Callable<Void>() {
248
249 @Override
250 public Void call() throws Exception {
251
252
253 Scan scan = new Scan();
254 scan.setMaxResultSize(Long.MAX_VALUE);
255 scan.setCaching(Integer.MAX_VALUE);
256
257 testEquivalenceOfScanWithHeartbeats(scan, DEFAULT_ROW_SLEEP_TIME, -1, false);
258 return null;
259 }
260 };
261 }
262
263
264
265
266
267 public Callable<Void> testHeartbeatBetweenColumnFamilies() throws Exception {
268 return new Callable<Void>() {
269 @Override
270 public Void call() throws Exception {
271
272
273 Scan baseScan = new Scan();
274 baseScan.setMaxResultSize(Long.MAX_VALUE);
275 baseScan.setCaching(Integer.MAX_VALUE);
276
277
278
279 Scan scanCopy = new Scan(baseScan);
280 testEquivalenceOfScanWithHeartbeats(scanCopy, -1, DEFAULT_CF_SLEEP_TIME, false);
281 scanCopy = new Scan(baseScan);
282 testEquivalenceOfScanWithHeartbeats(scanCopy, -1, DEFAULT_CF_SLEEP_TIME, true);
283 return null;
284 }
285 };
286 }
287
288 public static class SparseFilter extends FilterBase{
289
290 @Override
291 public ReturnCode filterKeyValue(Cell v) throws IOException {
292 try {
293 Thread.sleep(CLIENT_TIMEOUT/2 + 10);
294 } catch (InterruptedException e) {
295 Thread.currentThread().interrupt();
296 }
297 return Bytes.equals(CellUtil.cloneRow(v), ROWS[NUM_ROWS - 1]) ?
298 ReturnCode.INCLUDE :
299 ReturnCode.SKIP;
300 }
301
302 public static Filter parseFrom(final byte [] pbBytes){
303 return new SparseFilter();
304 }
305 }
306
307
308
309
310
311 public Callable<Void> testHeartbeatWithSparseFilter() throws Exception {
312 return new Callable<Void>() {
313 @Override
314 public Void call() throws Exception {
315 Scan scan = new Scan();
316 scan.setMaxResultSize(Long.MAX_VALUE);
317 scan.setCaching(Integer.MAX_VALUE);
318 scan.setFilter(new SparseFilter());
319 ResultScanner scanner = TABLE.getScanner(scan);
320 int num = 0;
321 while (scanner.next() != null) {
322 num++;
323 }
324 assertEquals(1, num);
325 scanner.close();
326
327 scan = new Scan();
328 scan.setMaxResultSize(Long.MAX_VALUE);
329 scan.setCaching(Integer.MAX_VALUE);
330 scan.setFilter(new SparseFilter());
331 scan.setAllowPartialResults(true);
332 scanner = TABLE.getScanner(scan);
333 num = 0;
334 while (scanner.next() != null) {
335 num++;
336 }
337 assertEquals(NUM_FAMILIES * NUM_QUALIFIERS, num);
338 scanner.close();
339
340 return null;
341 }
342 };
343 }
344
345
346
347
348
349
350
351
352
353
354
355 public void testEquivalenceOfScanWithHeartbeats(final Scan scan, int rowSleepTime,
356 int cfSleepTime, boolean sleepBeforeCf) throws Exception {
357 disableSleeping();
358 final ResultScanner scanner = TABLE.getScanner(scan);
359 final ResultScanner scannerWithHeartbeats = TABLE.getScanner(scan);
360
361 Result r1 = null;
362 Result r2 = null;
363
364 while ((r1 = scanner.next()) != null) {
365
366 configureSleepTime(rowSleepTime, cfSleepTime, sleepBeforeCf);
367 r2 = scannerWithHeartbeats.next();
368 disableSleeping();
369
370 assertTrue(r2 != null);
371 try {
372 Result.compareResults(r1, r2);
373 } catch (Exception e) {
374 fail(e.getMessage());
375 }
376 }
377
378 assertTrue(scannerWithHeartbeats.next() == null);
379 scanner.close();
380 scannerWithHeartbeats.close();
381 }
382
383
384
385
386
387
388
389 private static void configureSleepTime(int rowSleepTime, int cfSleepTime, boolean sleepBeforeCf) {
390 HeartbeatHRegion.sleepBetweenRows = rowSleepTime > 0;
391 HeartbeatHRegion.rowSleepTime = rowSleepTime;
392
393 HeartbeatHRegion.sleepBetweenColumnFamilies = cfSleepTime > 0;
394 HeartbeatHRegion.columnFamilySleepTime = cfSleepTime;
395 HeartbeatHRegion.sleepBeforeColumnFamily = sleepBeforeCf;
396 }
397
398
399
400
401 private static void disableSleeping() {
402 HeartbeatHRegion.sleepBetweenRows = false;
403 HeartbeatHRegion.sleepBetweenColumnFamilies = false;
404 }
405
406
407
408
409
410 private static class HeartbeatHRegionServer extends HRegionServer {
411 public HeartbeatHRegionServer(Configuration conf) throws IOException, InterruptedException {
412 super(conf);
413 }
414
415 public HeartbeatHRegionServer(Configuration conf, CoordinatedStateManager csm)
416 throws IOException, InterruptedException {
417 super(conf, csm);
418 }
419
420 @Override
421 protected RSRpcServices createRpcServices() throws IOException {
422 return new HeartbeatRPCServices(this);
423 }
424 }
425
426
427
428
429 private static class HeartbeatRPCServices extends RSRpcServices {
430 private static boolean heartbeatsEnabled = true;
431
432 public HeartbeatRPCServices(HRegionServer rs) throws IOException {
433 super(rs);
434 }
435
436 @Override
437 public ScanResponse scan(RpcController controller, ScanRequest request)
438 throws ServiceException {
439 ScanRequest.Builder builder = ScanRequest.newBuilder(request);
440 builder.setClientHandlesHeartbeats(heartbeatsEnabled);
441 return super.scan(controller, builder.build());
442 }
443 }
444
445
446
447
448
449
450 private static class HeartbeatHRegion extends HRegion {
451
452 private static int rowSleepTime = DEFAULT_ROW_SLEEP_TIME;
453 private static boolean sleepBetweenRows = false;
454
455
456
457
458
459
460 private static boolean sleepBeforeColumnFamily = false;
461 private static int columnFamilySleepTime = DEFAULT_CF_SLEEP_TIME;
462 private static boolean sleepBetweenColumnFamilies = false;
463
464 public HeartbeatHRegion(Path tableDir, WAL wal, FileSystem fs, Configuration confParam,
465 HRegionInfo regionInfo, HTableDescriptor htd, RegionServerServices rsServices) {
466 super(tableDir, wal, fs, confParam, regionInfo, htd, rsServices);
467 }
468
469 public HeartbeatHRegion(HRegionFileSystem fs, WAL wal, Configuration confParam,
470 HTableDescriptor htd, RegionServerServices rsServices) {
471 super(fs, wal, confParam, htd, rsServices);
472 }
473
474 private static void columnFamilySleep() {
475 if (HeartbeatHRegion.sleepBetweenColumnFamilies) {
476 try {
477 Thread.sleep(HeartbeatHRegion.columnFamilySleepTime);
478 } catch (InterruptedException e) {
479 }
480 }
481 }
482
483 private static void rowSleep() {
484 try {
485 if (HeartbeatHRegion.sleepBetweenRows) {
486 Thread.sleep(HeartbeatHRegion.rowSleepTime);
487 }
488 } catch (InterruptedException e) {
489 }
490 }
491
492
493 @Override
494 protected RegionScanner instantiateRegionScanner(Scan scan,
495 List<KeyValueScanner> additionalScanners, long nonceGroup, long nonce) throws IOException {
496 if (scan.isReversed()) {
497 if (scan.getFilter() != null) {
498 scan.getFilter().setReversed(true);
499 }
500 return new HeartbeatReversedRegionScanner(scan, additionalScanners, this);
501 }
502 return new HeartbeatRegionScanner(scan, additionalScanners, this);
503 }
504 }
505
506
507
508
509
510 private static class HeartbeatReversedRegionScanner extends ReversedRegionScannerImpl {
511 HeartbeatReversedRegionScanner(Scan scan, List<KeyValueScanner> additionalScanners,
512 HRegion region) throws IOException {
513 super(scan, additionalScanners, region);
514 }
515
516 @Override
517 public boolean nextRaw(List<Cell> outResults, ScannerContext context)
518 throws IOException {
519 boolean moreRows = super.nextRaw(outResults, context);
520 HeartbeatHRegion.rowSleep();
521 return moreRows;
522 }
523
524 @Override
525 protected void initializeKVHeap(List<KeyValueScanner> scanners,
526 List<KeyValueScanner> joinedScanners, HRegion region) throws IOException {
527 this.storeHeap = new HeartbeatReversedKVHeap(scanners, region.getComparator());
528 if (!joinedScanners.isEmpty()) {
529 this.joinedHeap = new HeartbeatReversedKVHeap(joinedScanners, region.getComparator());
530 }
531 }
532 }
533
534
535
536
537
538 private static class HeartbeatRegionScanner extends RegionScannerImpl {
539 HeartbeatRegionScanner(Scan scan, List<KeyValueScanner> additionalScanners, HRegion region)
540 throws IOException {
541 region.super(scan, additionalScanners, region);
542 }
543
544 @Override
545 public boolean nextRaw(List<Cell> outResults, ScannerContext context)
546 throws IOException {
547 boolean moreRows = super.nextRaw(outResults, context);
548 HeartbeatHRegion.rowSleep();
549 return moreRows;
550 }
551
552 @Override
553 protected void initializeKVHeap(List<KeyValueScanner> scanners,
554 List<KeyValueScanner> joinedScanners, HRegion region) throws IOException {
555 this.storeHeap = new HeartbeatKVHeap(scanners, region.getComparator());
556 if (!joinedScanners.isEmpty()) {
557 this.joinedHeap = new HeartbeatKVHeap(joinedScanners, region.getComparator());
558 }
559 }
560 }
561
562
563
564
565
566 private static final class HeartbeatKVHeap extends KeyValueHeap {
567 public HeartbeatKVHeap(List<? extends KeyValueScanner> scanners, KVComparator comparator)
568 throws IOException {
569 super(scanners, comparator);
570 }
571
572 HeartbeatKVHeap(List<? extends KeyValueScanner> scanners, KVScannerComparator comparator)
573 throws IOException {
574 super(scanners, comparator);
575 }
576
577 @Override
578 public boolean next(List<Cell> result, ScannerContext context)
579 throws IOException {
580 if (HeartbeatHRegion.sleepBeforeColumnFamily) HeartbeatHRegion.columnFamilySleep();
581 boolean moreRows = super.next(result, context);
582 if (!HeartbeatHRegion.sleepBeforeColumnFamily) HeartbeatHRegion.columnFamilySleep();
583 return moreRows;
584 }
585 }
586
587
588
589
590
591 private static final class HeartbeatReversedKVHeap extends ReversedKeyValueHeap {
592 public HeartbeatReversedKVHeap(List<? extends KeyValueScanner> scanners,
593 KVComparator comparator) throws IOException {
594 super(scanners, comparator);
595 }
596
597 @Override
598 public boolean next(List<Cell> result, ScannerContext context)
599 throws IOException {
600 if (HeartbeatHRegion.sleepBeforeColumnFamily) HeartbeatHRegion.columnFamilySleep();
601 boolean moreRows = super.next(result, context);
602 if (!HeartbeatHRegion.sleepBeforeColumnFamily) HeartbeatHRegion.columnFamilySleep();
603 return moreRows;
604 }
605 }
606 }