1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.hadoop.hbase.client;
19
20 import static org.junit.Assert.assertEquals;
21 import static org.junit.Assert.assertTrue;
22
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.List;
26 import java.util.Random;
27 import java.util.concurrent.Callable;
28 import java.util.concurrent.CountDownLatch;
29 import java.util.concurrent.ExecutorService;
30 import java.util.concurrent.Executors;
31 import java.util.concurrent.Future;
32 import java.util.concurrent.atomic.AtomicInteger;
33 import java.util.concurrent.atomic.AtomicLong;
34
35 import org.apache.commons.logging.Log;
36 import org.apache.commons.logging.LogFactory;
37 import org.apache.hadoop.conf.Configuration;
38 import org.apache.hadoop.hbase.ClusterStatus;
39 import org.apache.hadoop.hbase.HBaseConfiguration;
40 import org.apache.hadoop.hbase.HBaseTestingUtility;
41 import org.apache.hadoop.hbase.HColumnDescriptor;
42 import org.apache.hadoop.hbase.HConstants;
43 import org.apache.hadoop.hbase.HTableDescriptor;
44 import org.apache.hadoop.hbase.ServerName;
45 import org.apache.hadoop.hbase.TableName;
46 import org.apache.hadoop.hbase.exceptions.PreemptiveFastFailException;
47 import org.apache.hadoop.hbase.regionserver.HRegionServer;
48 import org.apache.hadoop.hbase.regionserver.Region;
49 import org.apache.hadoop.hbase.ipc.SimpleRpcScheduler;
50 import org.apache.hadoop.hbase.testclassification.MediumTests;
51 import org.apache.hadoop.hbase.util.Bytes;
52 import org.apache.hadoop.hbase.util.LoadTestKVGenerator;
53 import org.junit.After;
54 import org.junit.AfterClass;
55 import org.junit.Before;
56 import org.junit.BeforeClass;
57 import org.junit.Test;
58 import org.junit.Ignore;
59 import org.junit.experimental.categories.Category;
60
61 @Category({MediumTests.class})
62 public class TestFastFail {
63 private static final Log LOG = LogFactory.getLog(TestFastFail.class);
64 private final static HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
65 private static byte[] FAMILY = Bytes.toBytes("testFamily");
66 private static final Random random = new Random();
67 private static int SLAVES = 1;
68 private static byte[] QUALIFIER = Bytes.toBytes("testQualifier");
69 private static final int SLEEPTIME = 5000;
70
71
72
73
74 @BeforeClass
75 public static void setUpBeforeClass() throws Exception {
76
77 TEST_UTIL.getConfiguration().set(
78 SimpleRpcScheduler.CALL_QUEUE_TYPE_CONF_KEY, "deadline");
79 TEST_UTIL.startMiniCluster(SLAVES);
80 }
81
82
83
84
85 @AfterClass
86 public static void tearDownAfterClass() throws Exception {
87 TEST_UTIL.shutdownMiniCluster();
88 }
89
90
91
92
93 @Before
94 public void setUp() throws Exception {
95 MyPreemptiveFastFailInterceptor.numBraveSouls.set(0);
96 CallQueueTooBigPffeInterceptor.numCallQueueTooBig.set(0);
97 }
98
99
100
101
102 @After
103 public void tearDown() throws Exception {
104
105 }
106
107 @Ignore ("Can go zombie -- see HBASE-14421; FIX") @Test
108 public void testFastFail() throws IOException, InterruptedException {
109 Admin admin = TEST_UTIL.getHBaseAdmin();
110
111 final String tableName = "testClientRelearningExperiment";
112 HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(Bytes
113 .toBytes(tableName)));
114 desc.addFamily(new HColumnDescriptor(FAMILY));
115 admin.createTable(desc, Bytes.toBytes("aaaa"), Bytes.toBytes("zzzz"), 32);
116 final long numRows = 1000;
117
118 Configuration conf = TEST_UTIL.getConfiguration();
119 conf.setLong(HConstants.HBASE_CLIENT_OPERATION_TIMEOUT, SLEEPTIME * 100);
120 conf.setInt(HConstants.HBASE_CLIENT_PAUSE, SLEEPTIME / 10);
121 conf.setBoolean(HConstants.HBASE_CLIENT_FAST_FAIL_MODE_ENABLED, true);
122 conf.setLong(HConstants.HBASE_CLIENT_FAST_FAIL_THREASHOLD_MS, 0);
123 conf.setClass(HConstants.HBASE_CLIENT_FAST_FAIL_INTERCEPTOR_IMPL,
124 MyPreemptiveFastFailInterceptor.class,
125 PreemptiveFastFailInterceptor.class);
126
127 final Connection connection = ConnectionFactory.createConnection(conf);
128
129
130
131
132 List<Put> puts = new ArrayList<>();
133 for (long i = 0; i < numRows; i++) {
134 byte[] rowKey = longToByteArrayKey(i);
135 Put put = new Put(rowKey);
136 byte[] value = rowKey;
137 put.add(FAMILY, QUALIFIER, value);
138 puts.add(put);
139 }
140 try (Table table = connection.getTable(TableName.valueOf(tableName))) {
141 table.put(puts);
142 LOG.info("Written all puts.");
143 }
144
145
146
147
148
149 int nThreads = 100;
150 ExecutorService service = Executors.newFixedThreadPool(nThreads);
151 final CountDownLatch continueOtherHalf = new CountDownLatch(1);
152 final CountDownLatch doneHalfway = new CountDownLatch(nThreads);
153
154 final AtomicInteger numSuccessfullThreads = new AtomicInteger(0);
155 final AtomicInteger numFailedThreads = new AtomicInteger(0);
156
157
158 final AtomicLong totalTimeTaken = new AtomicLong(0);
159 final AtomicInteger numBlockedWorkers = new AtomicInteger(0);
160 final AtomicInteger numPreemptiveFastFailExceptions = new AtomicInteger(0);
161
162 List<Future<Boolean>> futures = new ArrayList<Future<Boolean>>();
163 for (int i = 0; i < nThreads; i++) {
164 futures.add(service.submit(new Callable<Boolean>() {
165
166
167
168
169
170 public Boolean call() throws Exception {
171 try (Table table = connection.getTable(TableName.valueOf(tableName))) {
172 Thread.sleep(Math.abs(random.nextInt()) % 250);
173 byte[] row = longToByteArrayKey(Math.abs(random.nextLong())
174 % numRows);
175 Get g = new Get(row);
176 g.addColumn(FAMILY, QUALIFIER);
177 try {
178 table.get(g);
179 } catch (Exception e) {
180 LOG.debug("Get failed : ", e);
181 doneHalfway.countDown();
182 return false;
183 }
184
185
186 doneHalfway.countDown();
187 continueOtherHalf.await();
188
189 long startTime = System.currentTimeMillis();
190 g = new Get(row);
191 g.addColumn(FAMILY, QUALIFIER);
192 try {
193 table.get(g);
194
195 numSuccessfullThreads.addAndGet(1);
196 } catch (Exception e) {
197 if (e instanceof PreemptiveFastFailException) {
198
199 numPreemptiveFastFailExceptions.addAndGet(1);
200 }
201
202 numFailedThreads.addAndGet(1);
203 return false;
204 } finally {
205 long enTime = System.currentTimeMillis();
206 totalTimeTaken.addAndGet(enTime - startTime);
207 if ((enTime - startTime) >= SLEEPTIME) {
208
209
210
211
212
213 numBlockedWorkers.addAndGet(1);
214 }
215 }
216 return true;
217 } catch (Exception e) {
218 LOG.error("Caught unknown exception", e);
219 doneHalfway.countDown();
220 return false;
221 }
222 }
223 }));
224 }
225
226 doneHalfway.await();
227
228 ClusterStatus status = TEST_UTIL.getHBaseCluster().getClusterStatus();
229
230
231 for (int i = 0; i < SLAVES; i++) {
232 HRegionServer server = TEST_UTIL.getHBaseCluster().getRegionServer(i);
233 List<Region> regions = server.getOnlineRegions(TableName.META_TABLE_NAME);
234 if (regions.size() > 0) continue;
235
236 server.getRpcServer().stop();
237 server.stop("Testing");
238 }
239
240
241 continueOtherHalf.countDown();
242
243 Thread.sleep(2 * SLEEPTIME);
244
245 TEST_UTIL.getHBaseCluster().restoreClusterStatus(status);
246
247 int numThreadsReturnedFalse = 0;
248 int numThreadsReturnedTrue = 0;
249 int numThreadsThrewExceptions = 0;
250 for (Future<Boolean> f : futures) {
251 try {
252 numThreadsReturnedTrue += f.get() ? 1 : 0;
253 numThreadsReturnedFalse += f.get() ? 0 : 1;
254 } catch (Exception e) {
255 numThreadsThrewExceptions++;
256 }
257 }
258 LOG.debug("numThreadsReturnedFalse:"
259 + numThreadsReturnedFalse
260 + " numThreadsReturnedTrue:"
261 + numThreadsReturnedTrue
262 + " numThreadsThrewExceptions:"
263 + numThreadsThrewExceptions
264 + " numFailedThreads:"
265 + numFailedThreads.get()
266 + " numSuccessfullThreads:"
267 + numSuccessfullThreads.get()
268 + " numBlockedWorkers:"
269 + numBlockedWorkers.get()
270 + " totalTimeWaited: "
271 + totalTimeTaken.get()
272 / (numBlockedWorkers.get() == 0 ? Long.MAX_VALUE : numBlockedWorkers
273 .get()) + " numPFFEs: " + numPreemptiveFastFailExceptions.get());
274
275 assertEquals("The expected number of all the successfull and the failed "
276 + "threads should equal the total number of threads that we spawned",
277 nThreads, numFailedThreads.get() + numSuccessfullThreads.get());
278 assertEquals(
279 "All the failures should be coming from the secondput failure",
280 numFailedThreads.get(), numThreadsReturnedFalse);
281 assertEquals("Number of threads that threw execution exceptions "
282 + "otherwise should be 0", numThreadsThrewExceptions, 0);
283 assertEquals("The regionservers that returned true should equal to the"
284 + " number of successful threads", numThreadsReturnedTrue,
285 numSuccessfullThreads.get());
286
287
288
289
290
291
292
293
294
295
296
297
298
299 assertTrue(
300 "Only few thread should ideally be waiting for the dead "
301 + "regionserver to be coming back. numBlockedWorkers:"
302 + numBlockedWorkers.get() + " threads that retried : "
303 + MyPreemptiveFastFailInterceptor.numBraveSouls.get(),
304 numBlockedWorkers.get() <= MyPreemptiveFastFailInterceptor.numBraveSouls
305 .get());
306 }
307
308 @Test
309 public void testCallQueueTooBigExceptionDoesntTriggerPffe() throws Exception {
310 Admin admin = TEST_UTIL.getHBaseAdmin();
311
312 final String tableName = "testCallQueueTooBigException";
313 HTableDescriptor desc = new HTableDescriptor(TableName.valueOf(Bytes
314 .toBytes(tableName)));
315 desc.addFamily(new HColumnDescriptor(FAMILY));
316 admin.createTable(desc, Bytes.toBytes("aaaa"), Bytes.toBytes("zzzz"), 3);
317
318 Configuration conf = TEST_UTIL.getConfiguration();
319 conf.setLong(HConstants.HBASE_CLIENT_OPERATION_TIMEOUT, 100);
320 conf.setInt(HConstants.HBASE_CLIENT_PAUSE, 500);
321 conf.setInt(HConstants.HBASE_CLIENT_RETRIES_NUMBER, 1);
322
323 conf.setBoolean(HConstants.HBASE_CLIENT_FAST_FAIL_MODE_ENABLED, true);
324 conf.setLong(HConstants.HBASE_CLIENT_FAST_FAIL_THREASHOLD_MS, 0);
325 conf.setClass(HConstants.HBASE_CLIENT_FAST_FAIL_INTERCEPTOR_IMPL,
326 CallQueueTooBigPffeInterceptor.class, PreemptiveFastFailInterceptor.class);
327
328 final Connection connection = ConnectionFactory.createConnection(conf);
329
330
331 SimpleRpcScheduler srs = (SimpleRpcScheduler)
332 TEST_UTIL.getHBaseCluster().getRegionServer(0).getRpcServer().getScheduler();
333 Configuration newConf = HBaseConfiguration.create(TEST_UTIL.getConfiguration());
334 newConf.setInt("hbase.ipc.server.max.callqueue.length", 0);
335 srs.onConfigurationChange(newConf);
336
337 try (Table table = connection.getTable(TableName.valueOf(tableName))) {
338 Get get = new Get(new byte[1]);
339 table.get(get);
340 } catch (Throwable ex) {
341 }
342
343 assertEquals("We should have not entered PFFE mode on CQTBE, but we did;"
344 + " number of times this mode should have been entered:", 0,
345 CallQueueTooBigPffeInterceptor.numCallQueueTooBig.get());
346
347 newConf = HBaseConfiguration.create(TEST_UTIL.getConfiguration());
348 newConf.setInt("hbase.ipc.server.max.callqueue.length", 250);
349 srs.onConfigurationChange(newConf);
350 }
351
352 public static class MyPreemptiveFastFailInterceptor extends
353 PreemptiveFastFailInterceptor {
354 public static AtomicInteger numBraveSouls = new AtomicInteger();
355
356 @Override
357 protected boolean shouldRetryInspiteOfFastFail(FailureInfo fInfo) {
358 boolean ret = super.shouldRetryInspiteOfFastFail(fInfo);
359 if (ret)
360 numBraveSouls.addAndGet(1);
361 return ret;
362 }
363
364 public MyPreemptiveFastFailInterceptor(Configuration conf) {
365 super(conf);
366 }
367 }
368
369 private byte[] longToByteArrayKey(long rowKey) {
370 return LoadTestKVGenerator.md5PrefixedKey(rowKey).getBytes();
371 }
372
373 public static class CallQueueTooBigPffeInterceptor extends
374 PreemptiveFastFailInterceptor {
375 public static AtomicInteger numCallQueueTooBig = new AtomicInteger();
376
377 @Override
378 protected void handleFailureToServer(ServerName serverName, Throwable t) {
379 super.handleFailureToServer(serverName, t);
380 numCallQueueTooBig.incrementAndGet();
381 }
382
383 public CallQueueTooBigPffeInterceptor(Configuration conf) {
384 super(conf);
385 }
386 }
387 }