1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.ipc;
20
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.assertFalse;
23 import static org.junit.Assert.assertTrue;
24 import static org.junit.Assert.fail;
25
26 import java.io.IOException;
27 import java.net.InetSocketAddress;
28 import java.util.ArrayList;
29 import java.util.List;
30
31 import org.apache.commons.logging.Log;
32 import org.apache.commons.logging.LogFactory;
33 import org.apache.hadoop.conf.Configuration;
34 import org.apache.hadoop.hbase.HBaseConfiguration;
35 import org.apache.hadoop.hbase.HConstants;
36 import org.apache.hadoop.hbase.testclassification.MediumTests;
37 import org.apache.hadoop.hbase.ServerName;
38 import org.apache.hadoop.hbase.ipc.protobuf.generated.TestDelayedRpcProtos;
39 import org.apache.hadoop.hbase.ipc.protobuf.generated.TestDelayedRpcProtos.TestArg;
40 import org.apache.hadoop.hbase.ipc.protobuf.generated.TestDelayedRpcProtos.TestResponse;
41 import org.apache.hadoop.hbase.security.User;
42 import org.apache.log4j.AppenderSkeleton;
43 import org.apache.log4j.Level;
44 import org.apache.log4j.Logger;
45 import org.apache.log4j.spi.LoggingEvent;
46 import org.junit.Test;
47 import org.junit.experimental.categories.Category;
48
49 import com.google.common.collect.Lists;
50 import com.google.protobuf.BlockingRpcChannel;
51 import com.google.protobuf.BlockingService;
52 import com.google.protobuf.RpcController;
53 import com.google.protobuf.ServiceException;
54
55
56
57
58
59
60 @Category(MediumTests.class)
61 public class TestDelayedRpc {
62 private static final Log LOG = LogFactory.getLog(TestDelayedRpc.class);
63 public static RpcServerInterface rpcServer;
64 public static final int UNDELAYED = 0;
65 public static final int DELAYED = 1;
66 private static final int RPC_CLIENT_TIMEOUT = 30000;
67
68 @Test (timeout=60000)
69 public void testDelayedRpcImmediateReturnValue() throws Exception {
70 testDelayedRpc(false);
71 }
72
73 @Test (timeout=60000)
74 public void testDelayedRpcDelayedReturnValue() throws Exception {
75 testDelayedRpc(true);
76 }
77
78 private void testDelayedRpc(boolean delayReturnValue) throws Exception {
79 LOG.info("Running testDelayedRpc delayReturnValue=" + delayReturnValue);
80 Configuration conf = HBaseConfiguration.create();
81 InetSocketAddress isa = new InetSocketAddress("localhost", 0);
82 TestDelayedImplementation instance = new TestDelayedImplementation(delayReturnValue);
83 BlockingService service =
84 TestDelayedRpcProtos.TestDelayedService.newReflectiveBlockingService(instance);
85 rpcServer = new RpcServer(null, "testDelayedRpc",
86 Lists.newArrayList(new RpcServer.BlockingServiceAndInterface(service, null)),
87 isa,
88 conf,
89 new FifoRpcScheduler(conf, 1));
90 rpcServer.start();
91 RpcClient rpcClient = new RpcClient(conf, HConstants.DEFAULT_CLUSTER_ID.toString());
92 try {
93 InetSocketAddress address = rpcServer.getListenerAddress();
94 if (address == null) {
95 throw new IOException("Listener channel is closed");
96 }
97 BlockingRpcChannel channel = rpcClient.createBlockingRpcChannel(
98 ServerName.valueOf(address.getHostName(),address.getPort(), System.currentTimeMillis()),
99 User.getCurrent(), RPC_CLIENT_TIMEOUT);
100 TestDelayedRpcProtos.TestDelayedService.BlockingInterface stub =
101 TestDelayedRpcProtos.TestDelayedService.newBlockingStub(channel);
102 List<Integer> results = new ArrayList<Integer>();
103
104 TestThread th1 = new TestThread(stub, true, results);
105
106 TestThread th2 = new TestThread(stub, false, results);
107 TestThread th3 = new TestThread(stub, false, results);
108 th1.start();
109 Thread.sleep(100);
110 th2.start();
111 Thread.sleep(200);
112 th3.start();
113
114 th1.join();
115 th2.join();
116 th3.join();
117
118
119 assertEquals(UNDELAYED, results.get(0).intValue());
120 assertEquals(UNDELAYED, results.get(1).intValue());
121 assertEquals(results.get(2).intValue(), delayReturnValue ? DELAYED : 0xDEADBEEF);
122 } finally {
123 rpcClient.stop();
124 }
125 }
126
127 private static class ListAppender extends AppenderSkeleton {
128 private final List<String> messages = new ArrayList<String>();
129
130 @Override
131 protected void append(LoggingEvent event) {
132 messages.add(event.getMessage().toString());
133 }
134
135 @Override
136 public void close() {
137 }
138
139 @Override
140 public boolean requiresLayout() {
141 return false;
142 }
143
144 public List<String> getMessages() {
145 return messages;
146 }
147 }
148
149
150
151
152
153 @Test (timeout=60000)
154 public void testTooManyDelayedRpcs() throws Exception {
155 Configuration conf = HBaseConfiguration.create();
156 final int MAX_DELAYED_RPC = 10;
157 conf.setInt("hbase.ipc.warn.delayedrpc.number", MAX_DELAYED_RPC);
158
159 ListAppender listAppender = new ListAppender();
160 Logger log = Logger.getLogger("org.apache.hadoop.ipc.RpcServer");
161 log.addAppender(listAppender);
162 log.setLevel(Level.WARN);
163
164
165 InetSocketAddress isa = new InetSocketAddress("localhost", 0);
166 TestDelayedImplementation instance = new TestDelayedImplementation(true);
167 BlockingService service =
168 TestDelayedRpcProtos.TestDelayedService.newReflectiveBlockingService(instance);
169 rpcServer = new RpcServer(null, "testTooManyDelayedRpcs",
170 Lists.newArrayList(new RpcServer.BlockingServiceAndInterface(service, null)),
171 isa,
172 conf,
173 new FifoRpcScheduler(conf, 1));
174 rpcServer.start();
175 RpcClient rpcClient = new RpcClient(conf, HConstants.DEFAULT_CLUSTER_ID.toString());
176 try {
177 InetSocketAddress address = rpcServer.getListenerAddress();
178 if (address == null) {
179 throw new IOException("Listener channel is closed");
180 }
181 BlockingRpcChannel channel = rpcClient.createBlockingRpcChannel(
182 ServerName.valueOf(address.getHostName(),address.getPort(), System.currentTimeMillis()),
183 User.getCurrent(), RPC_CLIENT_TIMEOUT);
184 TestDelayedRpcProtos.TestDelayedService.BlockingInterface stub =
185 TestDelayedRpcProtos.TestDelayedService.newBlockingStub(channel);
186 Thread threads[] = new Thread[MAX_DELAYED_RPC + 1];
187 for (int i = 0; i < MAX_DELAYED_RPC; i++) {
188 threads[i] = new TestThread(stub, true, null);
189 threads[i].start();
190 }
191
192
193 assertTrue(listAppender.getMessages().isEmpty());
194
195
196 threads[MAX_DELAYED_RPC] = new TestThread(stub, true, null);
197 threads[MAX_DELAYED_RPC].start();
198
199 for (int i = 0; i < MAX_DELAYED_RPC; i++) {
200 threads[i].join();
201 }
202
203 assertFalse(listAppender.getMessages().isEmpty());
204 assertTrue(listAppender.getMessages().get(0).startsWith("Too many delayed calls"));
205
206 log.removeAppender(listAppender);
207 } finally {
208 rpcClient.stop();
209 }
210 }
211
212 public static class TestDelayedImplementation
213 implements TestDelayedRpcProtos.TestDelayedService.BlockingInterface {
214
215
216
217
218 private final boolean delayReturnValue;
219
220
221
222
223
224 public TestDelayedImplementation(boolean delayReturnValue) {
225 this.delayReturnValue = delayReturnValue;
226 }
227
228 @Override
229 public TestResponse test(final RpcController rpcController, final TestArg testArg)
230 throws ServiceException {
231 boolean delay = testArg.getDelay();
232 TestResponse.Builder responseBuilder = TestResponse.newBuilder();
233 if (!delay) {
234 responseBuilder.setResponse(UNDELAYED);
235 return responseBuilder.build();
236 }
237 final Delayable call = RpcServer.getCurrentCall();
238 call.startDelay(delayReturnValue);
239 new Thread() {
240 @Override
241 public void run() {
242 try {
243 Thread.sleep(500);
244 TestResponse.Builder responseBuilder = TestResponse.newBuilder();
245 call.endDelay(delayReturnValue ?
246 responseBuilder.setResponse(DELAYED).build() : null);
247 } catch (Exception e) {
248 e.printStackTrace();
249 }
250 }
251 }.start();
252
253
254 responseBuilder.setResponse(0xDEADBEEF);
255 return responseBuilder.build();
256 }
257 }
258
259 public static class TestThread extends Thread {
260 private final TestDelayedRpcProtos.TestDelayedService.BlockingInterface stub;
261 private final boolean delay;
262 private final List<Integer> results;
263
264 public TestThread(TestDelayedRpcProtos.TestDelayedService.BlockingInterface stub,
265 boolean delay, List<Integer> results) {
266 this.stub = stub;
267 this.delay = delay;
268 this.results = results;
269 }
270
271 @Override
272 public void run() {
273 Integer result;
274 try {
275 result = new Integer(stub.test(null, TestArg.newBuilder().setDelay(delay).build()).
276 getResponse());
277 } catch (ServiceException e) {
278 throw new RuntimeException(e);
279 }
280 if (results != null) {
281 synchronized (results) {
282 results.add(result);
283 }
284 }
285 }
286 }
287
288 @Test
289 public void testEndDelayThrowing() throws IOException {
290 Configuration conf = HBaseConfiguration.create();
291 InetSocketAddress isa = new InetSocketAddress("localhost", 0);
292 FaultyTestDelayedImplementation instance = new FaultyTestDelayedImplementation();
293 BlockingService service =
294 TestDelayedRpcProtos.TestDelayedService.newReflectiveBlockingService(instance);
295 rpcServer = new RpcServer(null, "testEndDelayThrowing",
296 Lists.newArrayList(new RpcServer.BlockingServiceAndInterface(service, null)),
297 isa,
298 conf,
299 new FifoRpcScheduler(conf, 1));
300 rpcServer.start();
301 RpcClient rpcClient = new RpcClient(conf, HConstants.DEFAULT_CLUSTER_ID.toString());
302 try {
303 InetSocketAddress address = rpcServer.getListenerAddress();
304 if (address == null) {
305 throw new IOException("Listener channel is closed");
306 }
307 BlockingRpcChannel channel = rpcClient.createBlockingRpcChannel(
308 ServerName.valueOf(address.getHostName(),address.getPort(), System.currentTimeMillis()),
309 User.getCurrent(), 1000);
310 TestDelayedRpcProtos.TestDelayedService.BlockingInterface stub =
311 TestDelayedRpcProtos.TestDelayedService.newBlockingStub(channel);
312
313 int result = 0xDEADBEEF;
314
315 try {
316 result = stub.test(null, TestArg.newBuilder().setDelay(false).build()).getResponse();
317 } catch (Exception e) {
318 fail("No exception should have been thrown.");
319 }
320 assertEquals(result, UNDELAYED);
321
322 boolean caughtException = false;
323 try {
324 result = stub.test(null, TestArg.newBuilder().setDelay(true).build()).getResponse();
325 } catch(Exception e) {
326
327 if (e.getCause().getMessage().contains("java.lang.Exception: Something went wrong")) {
328 caughtException = true;
329 }
330 LOG.warn("Caught exception, expected=" + caughtException);
331 }
332 assertTrue(caughtException);
333 } finally {
334 rpcClient.stop();
335 }
336 }
337
338
339
340
341 private static class FaultyTestDelayedImplementation extends TestDelayedImplementation {
342 public FaultyTestDelayedImplementation() {
343 super(false);
344 }
345
346 @Override
347 public TestResponse test(RpcController rpcController, TestArg arg)
348 throws ServiceException {
349 LOG.info("In faulty test, delay=" + arg.getDelay());
350 if (!arg.getDelay()) return TestResponse.newBuilder().setResponse(UNDELAYED).build();
351 Delayable call = RpcServer.getCurrentCall();
352 call.startDelay(true);
353 LOG.info("In faulty test, delaying");
354 try {
355 call.endDelayThrowing(new Exception("Something went wrong"));
356 } catch (IOException e) {
357 e.printStackTrace();
358 }
359
360 return TestResponse.newBuilder().setResponse(DELAYED).build();
361 }
362 }
363 }