001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.ipc; 019 020import static org.apache.hadoop.hbase.ipc.TestProtobufRpcServiceImpl.SERVICE; 021import static org.apache.hadoop.hbase.ipc.TestProtobufRpcServiceImpl.newBlockingStub; 022import static org.apache.hadoop.hbase.ipc.TestProtobufRpcServiceImpl.newStub; 023import static org.junit.Assert.assertEquals; 024import static org.junit.Assert.assertFalse; 025import static org.junit.Assert.assertNotNull; 026import static org.junit.Assert.assertNull; 027import static org.junit.Assert.assertTrue; 028import static org.junit.Assert.fail; 029import static org.mockito.Matchers.anyObject; 030import static org.mockito.Mockito.spy; 031import static org.mockito.Mockito.verify; 032import static org.mockito.internal.verification.VerificationModeFactory.times; 033 034import java.io.IOException; 035import java.net.InetSocketAddress; 036import java.util.ArrayList; 037import java.util.List; 038 039import org.apache.hadoop.conf.Configuration; 040import org.apache.hadoop.hbase.Cell; 041import org.apache.hadoop.hbase.CellScanner; 042import org.apache.hadoop.hbase.CellUtil; 043import org.apache.hadoop.hbase.DoNotRetryIOException; 044import org.apache.hadoop.hbase.HBaseConfiguration; 045import org.apache.hadoop.hbase.KeyValue; 046import org.apache.hadoop.hbase.Server; 047import org.apache.hadoop.hbase.ipc.RpcServer.BlockingServiceAndInterface; 048import org.apache.hbase.thirdparty.com.google.protobuf.ServiceException; 049import org.apache.hadoop.hbase.shaded.ipc.protobuf.generated.TestProtos.EchoRequestProto; 050import org.apache.hadoop.hbase.shaded.ipc.protobuf.generated.TestProtos.EchoResponseProto; 051import org.apache.hadoop.hbase.shaded.ipc.protobuf.generated.TestProtos.EmptyRequestProto; 052import org.apache.hadoop.hbase.shaded.ipc.protobuf.generated.TestProtos.EmptyResponseProto; 053import org.apache.hadoop.hbase.shaded.ipc.protobuf.generated.TestProtos.PauseRequestProto; 054import org.apache.hadoop.hbase.shaded.ipc.protobuf.generated.TestRpcServiceProtos.TestProtobufRpcProto.BlockingInterface; 055import org.apache.hadoop.hbase.shaded.ipc.protobuf.generated.TestRpcServiceProtos.TestProtobufRpcProto.Interface; 056import org.apache.hadoop.hbase.shaded.protobuf.ProtobufUtil; 057import org.apache.hadoop.hbase.util.Bytes; 058import org.apache.hadoop.io.compress.GzipCodec; 059import org.apache.hadoop.util.StringUtils; 060import org.junit.BeforeClass; 061import org.junit.Test; 062import org.slf4j.Logger; 063import org.slf4j.LoggerFactory; 064import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableList; 065import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 066 067/** 068 * Some basic ipc tests. 069 */ 070public abstract class AbstractTestIPC { 071 072 private static final Logger LOG = LoggerFactory.getLogger(AbstractTestIPC.class); 073 074 private static final byte[] CELL_BYTES = Bytes.toBytes("xyz"); 075 private static final KeyValue CELL = new KeyValue(CELL_BYTES, CELL_BYTES, CELL_BYTES, CELL_BYTES); 076 077 protected static final Configuration CONF = HBaseConfiguration.create(); 078 static { 079 // Set the default to be the old SimpleRpcServer. Subclasses test it and netty. 080 CONF.set(RpcServerFactory.CUSTOM_RPC_SERVER_IMPL_CONF_KEY, SimpleRpcServer.class.getName()); 081 } 082 083 protected abstract RpcServer createRpcServer(final Server server, final String name, 084 final List<BlockingServiceAndInterface> services, 085 final InetSocketAddress bindAddress, Configuration conf, 086 RpcScheduler scheduler) throws IOException; 087 088 protected abstract AbstractRpcClient<?> createRpcClientNoCodec(Configuration conf); 089 090 /** 091 * Ensure we do not HAVE TO HAVE a codec. 092 */ 093 @Test 094 public void testNoCodec() throws IOException, ServiceException { 095 Configuration conf = HBaseConfiguration.create(); 096 RpcServer rpcServer = createRpcServer(null, "testRpcServer", 097 Lists.newArrayList(new RpcServer.BlockingServiceAndInterface( 098 SERVICE, null)), new InetSocketAddress("localhost", 0), CONF, 099 new FifoRpcScheduler(CONF, 1)); 100 try (AbstractRpcClient<?> client = createRpcClientNoCodec(conf)) { 101 rpcServer.start(); 102 BlockingInterface stub = newBlockingStub(client, rpcServer.getListenerAddress()); 103 HBaseRpcController pcrc = new HBaseRpcControllerImpl(); 104 String message = "hello"; 105 assertEquals(message, 106 stub.echo(pcrc, EchoRequestProto.newBuilder().setMessage(message).build()).getMessage()); 107 assertNull(pcrc.cellScanner()); 108 } finally { 109 rpcServer.stop(); 110 } 111 } 112 113 protected abstract AbstractRpcClient<?> createRpcClient(Configuration conf); 114 115 /** 116 * It is hard to verify the compression is actually happening under the wraps. Hope that if 117 * unsupported, we'll get an exception out of some time (meantime, have to trace it manually to 118 * confirm that compression is happening down in the client and server). 119 */ 120 @Test 121 public void testCompressCellBlock() throws IOException, ServiceException { 122 Configuration conf = new Configuration(HBaseConfiguration.create()); 123 conf.set("hbase.client.rpc.compressor", GzipCodec.class.getCanonicalName()); 124 List<Cell> cells = new ArrayList<>(); 125 int count = 3; 126 for (int i = 0; i < count; i++) { 127 cells.add(CELL); 128 } 129 RpcServer rpcServer = createRpcServer(null, "testRpcServer", 130 Lists.newArrayList(new RpcServer.BlockingServiceAndInterface( 131 SERVICE, null)), new InetSocketAddress("localhost", 0), CONF, 132 new FifoRpcScheduler(CONF, 1)); 133 134 try (AbstractRpcClient<?> client = createRpcClient(conf)) { 135 rpcServer.start(); 136 BlockingInterface stub = newBlockingStub(client, rpcServer.getListenerAddress()); 137 HBaseRpcController pcrc = new HBaseRpcControllerImpl(CellUtil.createCellScanner(cells)); 138 String message = "hello"; 139 assertEquals(message, 140 stub.echo(pcrc, EchoRequestProto.newBuilder().setMessage(message).build()).getMessage()); 141 int index = 0; 142 CellScanner cellScanner = pcrc.cellScanner(); 143 assertNotNull(cellScanner); 144 while (cellScanner.advance()) { 145 assertEquals(CELL, cellScanner.current()); 146 index++; 147 } 148 assertEquals(count, index); 149 } finally { 150 rpcServer.stop(); 151 } 152 } 153 154 protected abstract AbstractRpcClient<?> createRpcClientRTEDuringConnectionSetup( 155 Configuration conf) throws IOException; 156 157 @Test 158 public void testRTEDuringConnectionSetup() throws Exception { 159 Configuration conf = HBaseConfiguration.create(); 160 RpcServer rpcServer = createRpcServer(null, "testRpcServer", 161 Lists.newArrayList(new RpcServer.BlockingServiceAndInterface( 162 SERVICE, null)), new InetSocketAddress("localhost", 0), CONF, 163 new FifoRpcScheduler(CONF, 1)); 164 try (AbstractRpcClient<?> client = createRpcClientRTEDuringConnectionSetup(conf)) { 165 rpcServer.start(); 166 BlockingInterface stub = newBlockingStub(client, rpcServer.getListenerAddress()); 167 stub.ping(null, EmptyRequestProto.getDefaultInstance()); 168 fail("Expected an exception to have been thrown!"); 169 } catch (Exception e) { 170 LOG.info("Caught expected exception: " + e.toString()); 171 assertTrue(e.toString(), StringUtils.stringifyException(e).contains("Injected fault")); 172 } finally { 173 rpcServer.stop(); 174 } 175 } 176 177 /** 178 * Tests that the rpc scheduler is called when requests arrive. 179 */ 180 @Test 181 public void testRpcScheduler() throws IOException, ServiceException, InterruptedException { 182 RpcScheduler scheduler = spy(new FifoRpcScheduler(CONF, 1)); 183 RpcServer rpcServer = createRpcServer(null, "testRpcServer", 184 Lists.newArrayList(new RpcServer.BlockingServiceAndInterface( 185 SERVICE, null)), new InetSocketAddress("localhost", 0), CONF, scheduler); 186 verify(scheduler).init((RpcScheduler.Context) anyObject()); 187 try (AbstractRpcClient<?> client = createRpcClient(CONF)) { 188 rpcServer.start(); 189 verify(scheduler).start(); 190 BlockingInterface stub = newBlockingStub(client, rpcServer.getListenerAddress()); 191 EchoRequestProto param = EchoRequestProto.newBuilder().setMessage("hello").build(); 192 for (int i = 0; i < 10; i++) { 193 stub.echo(null, param); 194 } 195 verify(scheduler, times(10)).dispatch((CallRunner) anyObject()); 196 } finally { 197 rpcServer.stop(); 198 verify(scheduler).stop(); 199 } 200 } 201 202 /** Tests that the rpc scheduler is called when requests arrive. */ 203 @Test 204 public void testRpcMaxRequestSize() throws IOException, ServiceException { 205 Configuration conf = new Configuration(CONF); 206 conf.setInt(RpcServer.MAX_REQUEST_SIZE, 1000); 207 RpcServer rpcServer = createRpcServer(null, "testRpcServer", 208 Lists.newArrayList(new RpcServer.BlockingServiceAndInterface( 209 SERVICE, null)), new InetSocketAddress("localhost", 0), conf, 210 new FifoRpcScheduler(conf, 1)); 211 try (AbstractRpcClient<?> client = createRpcClient(conf)) { 212 rpcServer.start(); 213 BlockingInterface stub = newBlockingStub(client, rpcServer.getListenerAddress()); 214 StringBuilder message = new StringBuilder(1200); 215 for (int i = 0; i < 200; i++) { 216 message.append("hello."); 217 } 218 // set total RPC size bigger than 100 bytes 219 EchoRequestProto param = EchoRequestProto.newBuilder().setMessage(message.toString()).build(); 220 stub.echo( 221 new HBaseRpcControllerImpl(CellUtil.createCellScanner(ImmutableList.<Cell> of(CELL))), 222 param); 223 fail("RPC should have failed because it exceeds max request size"); 224 } catch (ServiceException e) { 225 LOG.info("Caught expected exception: " + e); 226 assertTrue(e.toString(), 227 StringUtils.stringifyException(e).contains("RequestTooBigException")); 228 } finally { 229 rpcServer.stop(); 230 } 231 } 232 233 /** 234 * Tests that the RpcServer creates & dispatches CallRunner object to scheduler with non-null 235 * remoteAddress set to its Call Object 236 * @throws ServiceException 237 */ 238 @Test 239 public void testRpcServerForNotNullRemoteAddressInCallObject() 240 throws IOException, ServiceException { 241 RpcServer rpcServer = createRpcServer(null, "testRpcServer", 242 Lists.newArrayList(new RpcServer.BlockingServiceAndInterface( 243 SERVICE, null)), new InetSocketAddress("localhost", 0), CONF, 244 new FifoRpcScheduler(CONF, 1)); 245 InetSocketAddress localAddr = new InetSocketAddress("localhost", 0); 246 try (AbstractRpcClient<?> client = createRpcClient(CONF)) { 247 rpcServer.start(); 248 BlockingInterface stub = newBlockingStub(client, rpcServer.getListenerAddress()); 249 assertEquals(localAddr.getAddress().getHostAddress(), 250 stub.addr(null, EmptyRequestProto.getDefaultInstance()).getAddr()); 251 } finally { 252 rpcServer.stop(); 253 } 254 } 255 256 @Test 257 public void testRemoteError() throws IOException, ServiceException { 258 RpcServer rpcServer = createRpcServer(null, "testRpcServer", 259 Lists.newArrayList(new RpcServer.BlockingServiceAndInterface( 260 SERVICE, null)), new InetSocketAddress("localhost", 0), CONF, 261 new FifoRpcScheduler(CONF, 1)); 262 try (AbstractRpcClient<?> client = createRpcClient(CONF)) { 263 rpcServer.start(); 264 BlockingInterface stub = newBlockingStub(client, rpcServer.getListenerAddress()); 265 stub.error(null, EmptyRequestProto.getDefaultInstance()); 266 } catch (ServiceException e) { 267 LOG.info("Caught expected exception: " + e); 268 IOException ioe = ProtobufUtil.handleRemoteException(e); 269 assertTrue(ioe instanceof DoNotRetryIOException); 270 assertTrue(ioe.getMessage().contains("server error!")); 271 } finally { 272 rpcServer.stop(); 273 } 274 } 275 276 @Test 277 public void testTimeout() throws IOException { 278 RpcServer rpcServer = createRpcServer(null, "testRpcServer", 279 Lists.newArrayList(new RpcServer.BlockingServiceAndInterface( 280 SERVICE, null)), new InetSocketAddress("localhost", 0), CONF, 281 new FifoRpcScheduler(CONF, 1)); 282 try (AbstractRpcClient<?> client = createRpcClient(CONF)) { 283 rpcServer.start(); 284 BlockingInterface stub = newBlockingStub(client, rpcServer.getListenerAddress()); 285 HBaseRpcController pcrc = new HBaseRpcControllerImpl(); 286 int ms = 1000; 287 int timeout = 100; 288 for (int i = 0; i < 10; i++) { 289 pcrc.reset(); 290 pcrc.setCallTimeout(timeout); 291 long startTime = System.nanoTime(); 292 try { 293 stub.pause(pcrc, PauseRequestProto.newBuilder().setMs(ms).build()); 294 } catch (ServiceException e) { 295 long waitTime = (System.nanoTime() - startTime) / 1000000; 296 // expected 297 LOG.info("Caught expected exception: " + e); 298 IOException ioe = ProtobufUtil.handleRemoteException(e); 299 assertTrue(ioe.getCause() instanceof CallTimeoutException); 300 // confirm that we got exception before the actual pause. 301 assertTrue(waitTime < ms); 302 } 303 } 304 } finally { 305 rpcServer.stop(); 306 } 307 } 308 309 protected abstract RpcServer createTestFailingRpcServer(final Server server, final String name, 310 final List<BlockingServiceAndInterface> services, 311 final InetSocketAddress bindAddress, Configuration conf, 312 RpcScheduler scheduler) throws IOException; 313 314 /** Tests that the connection closing is handled by the client with outstanding RPC calls */ 315 @Test 316 public void testConnectionCloseWithOutstandingRPCs() throws InterruptedException, IOException { 317 Configuration conf = new Configuration(CONF); 318 RpcServer rpcServer = createTestFailingRpcServer(null, "testRpcServer", 319 Lists.newArrayList(new RpcServer.BlockingServiceAndInterface( 320 SERVICE, null)), new InetSocketAddress("localhost", 0), CONF, 321 new FifoRpcScheduler(CONF, 1)); 322 323 try (AbstractRpcClient<?> client = createRpcClient(conf)) { 324 rpcServer.start(); 325 BlockingInterface stub = newBlockingStub(client, rpcServer.getListenerAddress()); 326 EchoRequestProto param = EchoRequestProto.newBuilder().setMessage("hello").build(); 327 stub.echo(null, param); 328 fail("RPC should have failed because connection closed"); 329 } catch (ServiceException e) { 330 LOG.info("Caught expected exception: " + e.toString()); 331 } finally { 332 rpcServer.stop(); 333 } 334 } 335 336 @Test 337 public void testAsyncEcho() throws IOException { 338 Configuration conf = HBaseConfiguration.create(); 339 RpcServer rpcServer = createRpcServer(null, "testRpcServer", 340 Lists.newArrayList(new RpcServer.BlockingServiceAndInterface( 341 SERVICE, null)), new InetSocketAddress("localhost", 0), CONF, 342 new FifoRpcScheduler(CONF, 1)); 343 try (AbstractRpcClient<?> client = createRpcClient(conf)) { 344 rpcServer.start(); 345 Interface stub = newStub(client, rpcServer.getListenerAddress()); 346 int num = 10; 347 List<HBaseRpcController> pcrcList = new ArrayList<>(); 348 List<BlockingRpcCallback<EchoResponseProto>> callbackList = new ArrayList<>(); 349 for (int i = 0; i < num; i++) { 350 HBaseRpcController pcrc = new HBaseRpcControllerImpl(); 351 BlockingRpcCallback<EchoResponseProto> done = new BlockingRpcCallback<>(); 352 stub.echo(pcrc, EchoRequestProto.newBuilder().setMessage("hello-" + i).build(), done); 353 pcrcList.add(pcrc); 354 callbackList.add(done); 355 } 356 for (int i = 0; i < num; i++) { 357 HBaseRpcController pcrc = pcrcList.get(i); 358 assertFalse(pcrc.failed()); 359 assertNull(pcrc.cellScanner()); 360 assertEquals("hello-" + i, callbackList.get(i).get().getMessage()); 361 } 362 } finally { 363 rpcServer.stop(); 364 } 365 } 366 367 @Test 368 public void testAsyncRemoteError() throws IOException { 369 AbstractRpcClient<?> client = createRpcClient(CONF); 370 RpcServer rpcServer = createRpcServer(null, "testRpcServer", 371 Lists.newArrayList(new RpcServer.BlockingServiceAndInterface( 372 SERVICE, null)), new InetSocketAddress("localhost", 0), CONF, 373 new FifoRpcScheduler(CONF, 1)); 374 try { 375 rpcServer.start(); 376 Interface stub = newStub(client, rpcServer.getListenerAddress()); 377 BlockingRpcCallback<EmptyResponseProto> callback = new BlockingRpcCallback<>(); 378 HBaseRpcController pcrc = new HBaseRpcControllerImpl(); 379 stub.error(pcrc, EmptyRequestProto.getDefaultInstance(), callback); 380 assertNull(callback.get()); 381 assertTrue(pcrc.failed()); 382 LOG.info("Caught expected exception: " + pcrc.getFailed()); 383 IOException ioe = ProtobufUtil.handleRemoteException(pcrc.getFailed()); 384 assertTrue(ioe instanceof DoNotRetryIOException); 385 assertTrue(ioe.getMessage().contains("server error!")); 386 } finally { 387 client.close(); 388 rpcServer.stop(); 389 } 390 } 391 392 @Test 393 public void testAsyncTimeout() throws IOException { 394 RpcServer rpcServer = createRpcServer(null, "testRpcServer", 395 Lists.newArrayList(new RpcServer.BlockingServiceAndInterface( 396 SERVICE, null)), new InetSocketAddress("localhost", 0), CONF, 397 new FifoRpcScheduler(CONF, 1)); 398 try (AbstractRpcClient<?> client = createRpcClient(CONF)) { 399 rpcServer.start(); 400 Interface stub = newStub(client, rpcServer.getListenerAddress()); 401 List<HBaseRpcController> pcrcList = new ArrayList<>(); 402 List<BlockingRpcCallback<EmptyResponseProto>> callbackList = new ArrayList<>(); 403 int ms = 1000; 404 int timeout = 100; 405 long startTime = System.nanoTime(); 406 for (int i = 0; i < 10; i++) { 407 HBaseRpcController pcrc = new HBaseRpcControllerImpl(); 408 pcrc.setCallTimeout(timeout); 409 BlockingRpcCallback<EmptyResponseProto> callback = new BlockingRpcCallback<>(); 410 stub.pause(pcrc, PauseRequestProto.newBuilder().setMs(ms).build(), callback); 411 pcrcList.add(pcrc); 412 callbackList.add(callback); 413 } 414 for (BlockingRpcCallback<?> callback : callbackList) { 415 assertNull(callback.get()); 416 } 417 long waitTime = (System.nanoTime() - startTime) / 1000000; 418 for (HBaseRpcController pcrc : pcrcList) { 419 assertTrue(pcrc.failed()); 420 LOG.info("Caught expected exception: " + pcrc.getFailed()); 421 IOException ioe = ProtobufUtil.handleRemoteException(pcrc.getFailed()); 422 assertTrue(ioe.getCause() instanceof CallTimeoutException); 423 } 424 // confirm that we got exception before the actual pause. 425 assertTrue(waitTime < ms); 426 } finally { 427 rpcServer.stop(); 428 } 429 } 430}