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