/*
* Copyright (C) 2015 The Async HBase Authors. All rights reserved.
* This file is part of Async HBase.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* - Neither the name of the StumbleUpon nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
package org.hbase.async;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.security.sasl.SaslClient;
import org.hbase.async.MultiAction.Response;
import org.hbase.async.auth.SimpleClientAuthProvider;
import org.hbase.async.generated.CellPB.Cell;
import org.hbase.async.generated.ClientPB.GetResponse;
import org.hbase.async.generated.ClientPB.Result;
import org.hbase.async.generated.RPCPB;
import org.hbase.async.generated.RPCPB.CellBlockMeta;
import org.jboss.netty.buffer.BigEndianHeapChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.buffer.ReadOnlyChannelBuffer;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.Channels;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.handler.codec.replay.VoidEnum;
import org.junit.Before;
import org.junit.Test;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.reflect.Whitebox;
import com.google.protobuf.CodedOutputStream;
import com.stumbleupon.async.Deferred;
import com.stumbleupon.async.TimeoutException;
@PrepareForTest({ Channels.class, GetRequest.class,
ChannelHandlerContext.class })
public class TestRegionClientDecode extends BaseTestRegionClient {
private static final VoidEnum VOID = (VoidEnum)null;
private static final byte[] ROW = { 0, 0, 1 };
private static final byte[] FAMILY = { 'n', 'o', 'b' };
private static final byte[] TABLE = { 'd', 'w' };
private static final byte[] QUALIFIER = { 'v', 'i', 'm', 'e', 's' };
private static final byte[] VALUE = { 42 };
private static final long TIMESTAMP = 1356998400000L;
// NOTE: the TYPE of ChannelBuffer is important! ReplayingDecoderBuffer isn't
// backed by an array and we have methods that attemp to see if they can
// perform zero copy operations.
@Before
public void beforeLocal() throws Exception {
when(hbase_client.getDefaultRpcTimeout()).thenReturn(60000);
timer.stop();
}
@Test
public void goodGetRequest() throws Exception {
final int id = 42;
final ChannelBuffer buffer = buildGoodResponse(false, id);
final GetRequest get = new GetRequest(TABLE, ROW);
final Deferred<Object> deferred = get.getDeferred();
inflightTheRpc(id, get);
assertNull(region_client.decode(ctx, chan, buffer, VOID));
@SuppressWarnings("unchecked")
final List<KeyValue> kvs = (List<KeyValue>)deferred.joinUninterruptibly();
assertEquals(1, kvs.size());
assertArrayEquals(ROW, kvs.get(0).key());
assertArrayEquals(FAMILY, kvs.get(0).family());
assertArrayEquals(QUALIFIER, kvs.get(0).qualifier());
assertArrayEquals(VALUE, kvs.get(0).value());
assertEquals(TIMESTAMP, kvs.get(0).timestamp());
verify(secure_rpc_helper, never()).handleResponse(buffer, chan);
assertEquals(1, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
}
@Test
public void goodGetRequestArrayBacked() throws Exception {
final int id = 42;
final ChannelBuffer buffer = buildGoodResponse(true, id);
final GetRequest get = new GetRequest(TABLE, ROW);
final Deferred<Object> deferred = get.getDeferred();
inflightTheRpc(id, get);
assertNull(region_client.decode(ctx, chan, buffer, VOID));
@SuppressWarnings("unchecked")
final List<KeyValue> kvs = (List<KeyValue>)deferred.joinUninterruptibly();
assertEquals(1, kvs.size());
assertArrayEquals(ROW, kvs.get(0).key());
assertArrayEquals(FAMILY, kvs.get(0).family());
assertArrayEquals(QUALIFIER, kvs.get(0).qualifier());
assertArrayEquals(VALUE, kvs.get(0).value());
assertEquals(TIMESTAMP, kvs.get(0).timestamp());
verify(secure_rpc_helper, never()).handleResponse(buffer, chan);
assertEquals(1, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
}
@Test
public void goodGetRequestWithSecurity() throws Exception {
injectSecurity();
final int id = 42;
final ChannelBuffer buffer = buildGoodResponse(false, id);
final GetRequest get = new GetRequest(TABLE, ROW);
final Deferred<Object> deferred = get.getDeferred();
inflightTheRpc(id, get);
assertNull(region_client.decode(ctx, chan, buffer, VOID));
@SuppressWarnings("unchecked")
final List<KeyValue> kvs = (List<KeyValue>)deferred.joinUninterruptibly();
assertEquals(1, kvs.size());
assertArrayEquals(ROW, kvs.get(0).key());
assertArrayEquals(FAMILY, kvs.get(0).family());
assertArrayEquals(QUALIFIER, kvs.get(0).qualifier());
assertArrayEquals(VALUE, kvs.get(0).value());
assertEquals(TIMESTAMP, kvs.get(0).timestamp());
verify(secure_rpc_helper, times(1)).handleResponse(buffer, chan);
assertEquals(1, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
}
@Test
public void goodGetRequestWithSecurityArrayBacked() throws Exception {
injectSecurity();
final int id = 42;
final ChannelBuffer buffer = buildGoodResponse(true, id);
final GetRequest get = new GetRequest(TABLE, ROW);
final Deferred<Object> deferred = get.getDeferred();
inflightTheRpc(id, get);
assertNull(region_client.decode(ctx, chan, buffer, VOID));
@SuppressWarnings("unchecked")
final List<KeyValue> kvs = (List<KeyValue>)deferred.joinUninterruptibly();
assertEquals(1, kvs.size());
assertArrayEquals(ROW, kvs.get(0).key());
assertArrayEquals(FAMILY, kvs.get(0).family());
assertArrayEquals(QUALIFIER, kvs.get(0).qualifier());
assertArrayEquals(VALUE, kvs.get(0).value());
assertEquals(TIMESTAMP, kvs.get(0).timestamp());
verify(secure_rpc_helper, times(1)).handleResponse(buffer, chan);
assertEquals(1, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
}
@Test
public void goodGetRequestWithSecurityConsumesAll() throws Exception {
injectSecurity();
final int id = 42;
final ChannelBuffer buffer = buildGoodResponse(false, id);
final GetRequest get = new GetRequest(TABLE, ROW);
final Deferred<Object> deferred = get.getDeferred();
inflightTheRpc(id, get);
when(secure_rpc_helper.handleResponse(buffer, chan)).thenReturn(null);
assertNull(region_client.decode(ctx, chan, buffer, VOID));
try {
deferred.join(500);
fail("Expected a TimeoutException");
} catch (TimeoutException te) { }
assertEquals(1, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
verify(timer.timeouts.get(0), never()).cancel();
}
@Test
public void goodGetRequestWithSecurityConsumesAllArrayBacked() throws Exception {
injectSecurity();
final int id = 42;
final ChannelBuffer buffer = buildGoodResponse(true, id);
final GetRequest get = new GetRequest(TABLE, ROW);
final Deferred<Object> deferred = get.getDeferred();
inflightTheRpc(id, get);
when(secure_rpc_helper.handleResponse(buffer, chan)).thenReturn(null);
assertNull(region_client.decode(ctx, chan, buffer, VOID));
try {
deferred.join(500);
fail("Expected a TimeoutException");
} catch (TimeoutException te) { }
assertEquals(1, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
verify(timer.timeouts.get(0), never()).cancel();
}
@Test
public void goodMultiActionResponse94() throws Exception {
Whitebox.setInternalState(region_client, "server_version",
RegionClient.SERVER_VERSION_092_OR_ABOVE);
final MultiAction rpc = new MultiAction();
for (int i = 0; i < 100; i++) {
final PutRequest put = new PutRequest("test".getBytes(), "hello".getBytes(), "t".getBytes(),
Bytes.fromInt(100), new byte[] { 42 });
put.setRegion(region);
rpc.add(put);
}
final Deferred<Object> md = rpc.getDeferred();
inflightTheRpc(201, rpc);
region_client.decode(ctx, chan,
ChannelBuffers.wrappedBuffer(MULTI_ACTION_RESPONSE_094), VOID);
final Response response = (Response) md.join();
assertEquals(100, response.size());
for (int i = 0; i < 100; i++) {
assertEquals(MultiAction.SUCCESS, response.result(i));
}
}
@Test
public void pbufDeserializeFailure() throws Exception {
// in this case we have a good length and header but the actual pbuf result
// is missing. We pull the rpc from the inflight map and call it back with
// the exception.
final int id = 42;
final GetRequest get = new GetRequest(TABLE, ROW);
final Deferred<Object> deferred = get.getDeferred();
inflightTheRpc(id, get);
ChannelBuffer buffer = new ReadOnlyChannelBuffer(
ChannelBuffers.wrappedBuffer(new byte[] { 0, 0, 0, 3, 2, 8, 42 }));
try {
region_client.decode(ctx, chan, buffer, VOID);
fail("Expected a NonRecoverableException");
} catch (NonRecoverableException ex) { }
try {
deferred.join();
fail("Expected the join to throw a NonRecoverableException");
} catch (NonRecoverableException ex) { }
assertEquals(0, rpcs_inflight.size());
assertEquals(1, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
}
@Test
public void pbufDeserializeFailureArrayBacked() throws Exception {
// in this case we have a good length and header but the actual pbuf result
// is missing. We pull the rpc from the inflight map and call it back with
// the exception.
final int id = 42;
final GetRequest get = new GetRequest(TABLE, ROW);
final Deferred<Object> deferred = get.getDeferred();
inflightTheRpc(id, get);
ChannelBuffer buffer =
ChannelBuffers.wrappedBuffer(new byte[] { 0, 0, 0, 3, 2, 8, 42 });
try {
region_client.decode(ctx, chan, buffer, VOID);
fail("Expected a NonRecoverableException");
} catch (NonRecoverableException ex) { }
try {
deferred.join();
fail("Expected the join to throw a NonRecoverableException");
} catch (NonRecoverableException ex) { }
assertEquals(0, rpcs_inflight.size());
assertEquals(1, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
}
@Test
public void pbufDeserializeFailureWHBaseException() throws Exception {
final int id = 42;
final GetRequest get = new GetRequest(TABLE, ROW);
final Deferred<Object> deferred = get.getDeferred();
inflightTheRpc(id, get);
ChannelBuffer buffer = new ReadOnlyChannelBuffer(
ChannelBuffers.wrappedBuffer(new byte[] { 0, 0, 0, 1, 2, 8, 42 }));
RuntimeException e = null;
try {
region_client.decode(ctx, chan, buffer, VOID);
fail("Expected a HBaseException");
} catch (HBaseException ex) {
e = ex;
}
assertTrue(e instanceof HBaseException);
e = null;
try {
deferred.join(100);
fail("Expected the join to throw a HBaseException");
} catch (HBaseException ex) {
e = ex;
}
assertTrue(e instanceof HBaseException);
assertEquals(0, rpcs_inflight.size());
assertEquals(1, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
}
private void notServingRegionException(String remote_exception) throws Exception {
final int id = 42;
final GetRequest get = new GetRequest(TABLE, ROW);
get.region = region;
inflightTheRpc(id, get);
ChannelBuffer buffer = PBufResponses.generateException(id,
remote_exception);
assertNull(region_client.decode(ctx, chan, buffer, VOID));
assertEquals(0, rpcs_inflight.size());
verify(hbase_client, times(1)).handleNSRE(any(HBaseRpc.class),
any(byte[].class), any(RecoverableException.class));
assertEquals(1, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
verify(timer.timeouts.get(0), never()).cancel();
}
@Test
public void notServingRegionException() throws Exception {
notServingRegionException("org.apache.hadoop.hbase.NotServingRegionException");
}
@Test
public void regionServerAbortedException() throws Exception {
notServingRegionException("org.apache.hadoop.hbase.regionserver.RegionServerAbortedException");
}
@Test
public void regionServerStoppedException() throws Exception {
notServingRegionException("org.apache.hadoop.hbase.regionserver.RegionServerStoppedException");
}
@Test
public void serverNotRunningYetException() throws Exception {
notServingRegionException("org.apache.hadoop.hbase.ipc.ServerNotRunningYetException");
}
@Test
public void regionMovedException() throws Exception {
final int id = 42;
final GetRequest get = new GetRequest(TABLE, ROW);
get.region = region;
inflightTheRpc(id, get);
ChannelBuffer buffer = PBufResponses.generateException(id,
"org.apache.hadoop.hbase.exceptions.RegionMovedException");
assertNull(region_client.decode(ctx, chan, buffer, VOID));
assertEquals(0, rpcs_inflight.size());
verify(hbase_client, times(1)).handleNSRE(any(HBaseRpc.class),
any(byte[].class), any(RecoverableException.class));
assertEquals(1, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
verify(timer.timeouts.get(0), never()).cancel();
}
@Test
public void noSuchColumnFamilyException() throws Exception {
final int id = 42;
final GetRequest get = new GetRequest(TABLE, ROW);
get.region = region;
inflightTheRpc(id, get);
ChannelBuffer buffer = PBufResponses.generateException(id,
"org.apache.hadoop.hbase.regionserver.NoSuchColumnFamilyException");
assertNull(region_client.decode(ctx, chan, buffer, VOID));
assertEquals(0, rpcs_inflight.size());
verify(hbase_client, never()).handleNSRE(any(HBaseRpc.class),
any(byte[].class), any(RecoverableException.class));
assertEquals(1, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
}
@Test
public void regionOpeningException() throws Exception {
final int id = 42;
final GetRequest get = new GetRequest(TABLE, ROW);
get.region = region;
inflightTheRpc(id, get);
ChannelBuffer buffer = PBufResponses.generateException(id,
"org.apache.hadoop.hbase.exceptions.RegionOpeningException");
assertNull(region_client.decode(ctx, chan, buffer, VOID));
assertEquals(0, rpcs_inflight.size());
verify(hbase_client, never()).handleNSRE(any(HBaseRpc.class),
any(byte[].class), any(RecoverableException.class));
assertEquals(1, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
}
@Test
public void versionMismatchException() throws Exception {
final int id = 42;
final GetRequest get = new GetRequest(TABLE, ROW);
get.region = region;
inflightTheRpc(id, get);
ChannelBuffer buffer = PBufResponses.generateException(id,
"org.apache.hadoop.io.VersionMismatchException");
assertNull(region_client.decode(ctx, chan, buffer, VOID));
assertEquals(0, rpcs_inflight.size());
verify(hbase_client, never()).handleNSRE(any(HBaseRpc.class),
any(byte[].class), any(RecoverableException.class));
assertEquals(1, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
}
@Test
public void replayed() throws Exception {
resetMockClient();
final int id = 42;
final ChannelBuffer buffer = buildGoodResponse(true, id);
final byte[] array = new byte[buffer.writerIndex()];
buffer.readBytes(array);
final GetRequest get = new GetRequest(TABLE, ROW);
final Deferred<Object> deferred = get.getDeferred();
inflightTheRpc(id, get);
final MessageEvent event = mock(MessageEvent.class);
when(event.getMessage())
.thenReturn(ChannelBuffers.wrappedBuffer(Arrays.copyOf(array, 3)))
.thenReturn(ChannelBuffers.wrappedBuffer(
Arrays.copyOfRange(array, 3, 10)))
.thenReturn(ChannelBuffers.wrappedBuffer(
Arrays.copyOfRange(array, 10, array.length)));
region_client.messageReceived(ctx, event);
region_client.messageReceived(ctx, event);
region_client.messageReceived(ctx, event);
@SuppressWarnings("unchecked")
final List<KeyValue> kvs = (List<KeyValue>)deferred.join(100);
assertEquals(1, kvs.size());
assertArrayEquals(ROW, kvs.get(0).key());
assertArrayEquals(FAMILY, kvs.get(0).family());
assertArrayEquals(QUALIFIER, kvs.get(0).qualifier());
assertArrayEquals(VALUE, kvs.get(0).value());
assertEquals(TIMESTAMP, kvs.get(0).timestamp());
verify(secure_rpc_helper, never()).handleResponse(buffer, chan);
assertEquals(1, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
}
@Test
public void replayedMissingMiddle() throws Exception {
resetMockClient();
final int id = 42;
final ChannelBuffer buffer = buildGoodResponse(true, id);
final byte[] array = new byte[buffer.writerIndex()];
buffer.readBytes(array);
final GetRequest get = new GetRequest(TABLE, ROW);
final Deferred<Object> deferred = get.getDeferred();
inflightTheRpc(id, get);
final MessageEvent event = mock(MessageEvent.class);
when(event.getMessage())
.thenReturn(ChannelBuffers.wrappedBuffer(Arrays.copyOf(array, 3)))
.thenReturn(ChannelBuffers.wrappedBuffer(
Arrays.copyOfRange(array, 10, array.length)));
region_client.messageReceived(ctx, event);
try {
region_client.messageReceived(ctx, event);
fail("Expected an InvalidResponseException");
} catch (InvalidResponseException ex) { }
try {
deferred.join(100);
fail("Expected an TimeoutException");
} catch (TimeoutException ex) { }
assertEquals(1, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
verify(timer.timeouts.get(0), never()).cancel();
}
@Test
public void replayedSecure() throws Exception {
resetMockClient();
config.overrideConfig(SecureRpcHelper.SECURITY_AUTHENTICATION_KEY,
"simple");
config.overrideConfig(SimpleClientAuthProvider.USERNAME_KEY,
"Cohen");
final SecureRpcHelper96 secure_helper =
PowerMockito.spy(new SecureRpcHelper96(hbase_client,
region_client, new InetSocketAddress("127.0.0.1", 50512)));
final SaslClient sasl_client = mock(SaslClient.class);
Whitebox.setInternalState(secure_helper, "sasl_client", sasl_client);
when(sasl_client.isComplete()).thenReturn(false);
Whitebox.setInternalState(region_client, "secure_rpc_helper", secure_helper);
PowerMockito.when(secure_helper.processChallenge(any(byte[].class)))
.thenReturn(new byte[] { 24 });
final int id = 42;
final byte[] array = { 0, 0, 0, 0, 0, 0, 0, 4, 42, 24, 42, 24 };
final GetRequest get = new GetRequest(TABLE, ROW);
final Deferred<Object> deferred = get.getDeferred();
inflightTheRpc(id, get);
final byte[][] chunks = new byte[2][];
chunks[0] = Arrays.copyOf(array, 3);
chunks[1] = Arrays.copyOfRange(array, 10, array.length);
final MessageEvent event = mock(MessageEvent.class);
when(event.getMessage())
.thenReturn(ChannelBuffers.wrappedBuffer(Arrays.copyOf(array, 3)))
.thenReturn(ChannelBuffers.wrappedBuffer(
Arrays.copyOfRange(array, 3, 6)))
.thenReturn(ChannelBuffers.wrappedBuffer(
Arrays.copyOfRange(array, 6, 11)))
.thenReturn(ChannelBuffers.wrappedBuffer(
Arrays.copyOfRange(array, 11, array.length)));
region_client.messageReceived(ctx, event);
region_client.messageReceived(ctx, event);
region_client.messageReceived(ctx, event);
region_client.messageReceived(ctx, event);
try {
deferred.join(100);
} catch (TimeoutException ex) { }
assertEquals(1, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
verify(timer.timeouts.get(0), never()).cancel();
}
@Test
public void replayMultiRPCInBuffer() throws Exception {
resetMockClient();
List<Deferred<Object>> deferreds = new ArrayList<Deferred<Object>>(3);
final GetRequest get1 = new GetRequest(TABLE, ROW);
deferreds.add(get1.getDeferred());
inflightTheRpc(1, get1);
final GetRequest get2 = new GetRequest(TABLE, ROW);
deferreds.add(get2.getDeferred());
inflightTheRpc(2, get2);
final GetRequest get3 = new GetRequest(TABLE, ROW);
deferreds.add(get3.getDeferred());
inflightTheRpc(3, get3);
byte[] pbuf1 = buildGoodResponse(true, 1).array();
byte[] pbuf2 = buildGoodResponse(true, 2).array();
byte[] pbuf3 = buildGoodResponse(true, 3).array();
// chunk these guys up
byte[][] chunks = new byte[3][];
chunks[0] = Arrays.copyOf(pbuf1, pbuf1.length / 2);
int len = pbuf1.length - (pbuf1.length / 2);
byte[] buf = new byte[len + pbuf2.length / 2];
System.arraycopy(pbuf1, pbuf1.length / 2, buf, 0, len);
System.arraycopy(pbuf2, 0, buf, len, pbuf2.length / 2);
chunks[1] = buf;
len = pbuf2.length - (pbuf2.length / 2);
chunks[2] = Arrays.copyOfRange(pbuf2, pbuf2.length / 2, pbuf2.length);
final MessageEvent event = mock(MessageEvent.class);
when(event.getMessage())
.thenReturn(ChannelBuffers.wrappedBuffer(chunks[0]))
.thenReturn(ChannelBuffers.wrappedBuffer(chunks[1]))
.thenReturn(ChannelBuffers.wrappedBuffer(chunks[2]))
.thenReturn(ChannelBuffers.wrappedBuffer(pbuf3));
region_client.messageReceived(ctx, event);
// we gave netty just a fragment of the first RPC so it will throw an error
region_client.messageReceived(ctx, event);
// now netty has the full RPC1 AND a chunk of RPC2. We'll parse all of RPC1
// and replay to get the next chunk of 2
region_client.messageReceived(ctx, event);
// at this point we have read all of the data from the buffer so Netty will
// discard it
region_client.messageReceived(ctx, event);
for (final Deferred<Object> deferred : deferreds) {
@SuppressWarnings("unchecked")
final List<KeyValue> kvs = (List<KeyValue>)deferred.join(100);
assertEquals(1, kvs.size());
assertArrayEquals(ROW, kvs.get(0).key());
assertArrayEquals(FAMILY, kvs.get(0).family());
assertArrayEquals(QUALIFIER, kvs.get(0).qualifier());
assertArrayEquals(VALUE, kvs.get(0).value());
assertEquals(TIMESTAMP, kvs.get(0).timestamp());
}
assertEquals(3, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
assertEquals(60000, (long)timer.tasks.get(1).getValue());
assertEquals(60000, (long)timer.tasks.get(2).getValue());
verify(timer.timeouts.get(0)).cancel();
verify(timer.timeouts.get(1)).cancel();
verify(timer.timeouts.get(2)).cancel();
}
@SuppressWarnings("unchecked")
@Test
public void replayCorruptSecondRPC() throws Exception {
resetMockClient();
List<Deferred<Object>> deferreds = new ArrayList<Deferred<Object>>(3);
final GetRequest get1 = new GetRequest(TABLE, ROW);
deferreds.add(get1.getDeferred());
inflightTheRpc(1, get1);
final GetRequest get2 = new GetRequest(TABLE, ROW);
deferreds.add(get2.getDeferred());
inflightTheRpc(2, get2);
final GetRequest get3 = new GetRequest(TABLE, ROW);
deferreds.add(get3.getDeferred());
inflightTheRpc(3, get3);
byte[] pbuf1 = buildGoodResponse(true, 1).array();
byte[] pbuf2 = buildGoodResponse(true, 2).array();
// corrupt it
pbuf2 = Arrays.copyOf(pbuf2, pbuf2.length - 4);
byte[] pbuf3 = buildGoodResponse(true, 3).array();
// chunk these guys up
byte[][] chunks = new byte[3][];
chunks[0] = Arrays.copyOf(pbuf1, pbuf1.length / 2);
int len = pbuf1.length - (pbuf1.length / 2);
byte[] buf = new byte[len + pbuf2.length / 2];
System.arraycopy(pbuf1, pbuf1.length / 2, buf, 0, len);
System.arraycopy(pbuf2, 0, buf, len, pbuf2.length / 2);
chunks[1] = buf;
len = pbuf2.length - (pbuf2.length / 2);
chunks[2] = Arrays.copyOfRange(pbuf2, pbuf2.length / 2, pbuf2.length);
final MessageEvent event = mock(MessageEvent.class);
when(event.getMessage())
.thenReturn(ChannelBuffers.wrappedBuffer(chunks[0]))
.thenReturn(ChannelBuffers.wrappedBuffer(chunks[1]))
.thenReturn(ChannelBuffers.wrappedBuffer(chunks[2]))
.thenReturn(ChannelBuffers.wrappedBuffer(pbuf3));
region_client.messageReceived(ctx, event);
// we gave netty just a fragment of the first RPC so it will throw an error
region_client.messageReceived(ctx, event);
// now netty has the full RPC1 AND a chunk of RPC2. We'll parse all of RPC1
// and replay the rest
region_client.messageReceived(ctx, event);
// because our corrupted RPC is missing a little bit of data at the end
// we will proceed to replay
try {
region_client.messageReceived(ctx, event);
fail("Expected an InvalidResponseException");
} catch (InvalidResponseException e) { }
// Make sure the first RPC was called back
List<KeyValue> kvs = (List<KeyValue>)deferreds.get(0).join(100);
assertEquals(1, kvs.size());
assertArrayEquals(ROW, kvs.get(0).key());
assertArrayEquals(FAMILY, kvs.get(0).family());
assertArrayEquals(QUALIFIER, kvs.get(0).qualifier());
assertArrayEquals(VALUE, kvs.get(0).value());
assertEquals(TIMESTAMP, kvs.get(0).timestamp());
// The second will toss the invalid RPC exception, causing the region client
// to close.
try {
kvs = (List<KeyValue>)deferreds.get(1).join(100);
fail("Expected a TimeoutException");
} catch (InvalidResponseException e) { }
// and the third will never have been called
try {
kvs = (List<KeyValue>)deferreds.get(2).join(100);
fail("Expected a TimeoutException");
} catch (TimeoutException e) { }
assertEquals(3, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
assertEquals(60000, (long)timer.tasks.get(1).getValue());
assertEquals(60000, (long)timer.tasks.get(2).getValue());
verify(timer.timeouts.get(0)).cancel();
verify(timer.timeouts.get(1)).cancel();
verify(timer.timeouts.get(2), never()).cancel();
}
@Test
public void replayMoreChunks() throws Exception {
resetMockClient();
List<Deferred<Object>> deferreds = new ArrayList<Deferred<Object>>(3);
final GetRequest get1 = new GetRequest(TABLE, ROW);
deferreds.add(get1.getDeferred());
inflightTheRpc(1, get1);
final GetRequest get2 = new GetRequest(TABLE, ROW);
deferreds.add(get2.getDeferred());
inflightTheRpc(2, get2);
final GetRequest get3 = new GetRequest(TABLE, ROW);
deferreds.add(get3.getDeferred());
inflightTheRpc(3, get3);
byte[] pbuf1 = buildGoodResponse(true, 1).array();
byte[] pbuf2 = buildGoodResponse(true, 2).array();
byte[] pbuf3 = buildGoodResponse(true, 3).array();
// chunk these guys up
byte[][] chunks = new byte[6][];
chunks[0] = Arrays.copyOf(pbuf1, pbuf1.length / 2);
int len = pbuf1.length - (pbuf1.length / 2);
byte[] buf = new byte[len + pbuf2.length / 2];
System.arraycopy(pbuf1, pbuf1.length / 2, buf, 0, len);
System.arraycopy(pbuf2, 0, buf, len, pbuf2.length / 2);
chunks[1] = buf;
len = pbuf2.length / 2;
chunks[2] = Arrays.copyOfRange(pbuf2, len, len + 4);
chunks[3] = Arrays.copyOfRange(pbuf2, len + 4, pbuf2.length);
len = pbuf3.length / 2;
chunks[4] = Arrays.copyOfRange(pbuf3, 0, len);
chunks[5] = Arrays.copyOfRange(pbuf3, len, pbuf3.length);
final MessageEvent event = mock(MessageEvent.class);
when(event.getMessage())
.thenReturn(ChannelBuffers.wrappedBuffer(chunks[0]))
.thenReturn(ChannelBuffers.wrappedBuffer(chunks[1]))
.thenReturn(ChannelBuffers.wrappedBuffer(chunks[2]))
.thenReturn(ChannelBuffers.wrappedBuffer(chunks[3]))
.thenReturn(ChannelBuffers.wrappedBuffer(chunks[4]))
.thenReturn(ChannelBuffers.wrappedBuffer(chunks[5]));
region_client.messageReceived(ctx, event);
region_client.messageReceived(ctx, event);
region_client.messageReceived(ctx, event);
region_client.messageReceived(ctx, event);
region_client.messageReceived(ctx, event);
region_client.messageReceived(ctx, event);
for (final Deferred<Object> deferred : deferreds) {
@SuppressWarnings("unchecked")
final List<KeyValue> kvs = (List<KeyValue>)deferred.join(100);
assertEquals(1, kvs.size());
assertArrayEquals(ROW, kvs.get(0).key());
assertArrayEquals(FAMILY, kvs.get(0).family());
assertArrayEquals(QUALIFIER, kvs.get(0).qualifier());
assertArrayEquals(VALUE, kvs.get(0).value());
assertEquals(TIMESTAMP, kvs.get(0).timestamp());
}
assertEquals(3, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
assertEquals(60000, (long)timer.tasks.get(1).getValue());
assertEquals(60000, (long)timer.tasks.get(2).getValue());
verify(timer.timeouts.get(0)).cancel();
verify(timer.timeouts.get(1)).cancel();
verify(timer.timeouts.get(2)).cancel();
}
@Test (expected = IndexOutOfBoundsException.class)
public void eom() throws Exception {
// we read the whole message some how. Doesn't matter if it's array backed
// or not in this case.
final ChannelBuffer buffer = buildGoodResponse(true, 1);
buffer.readerIndex(buffer.writerIndex());
region_client.decode(ctx, chan, buffer, VOID);
}
@Test
public void notReadableInitial() throws Exception {
// Fails on the first call to ensureReadable because the size encoded
// in the first 4 bytes is much larger than it should be
ChannelBuffer buffer = new ReadOnlyChannelBuffer(
ChannelBuffers.wrappedBuffer(new byte[] { 0, 0, 0, 42, 1 }));
try {
region_client.decode(ctx, chan, buffer, VOID);
fail("Expected an IndexOutOfBoundsException");
} catch (IndexOutOfBoundsException oob) { }
buffer = ChannelBuffers.wrappedBuffer(new byte[] { 0, 0, 0, 42, 1 });
try {
region_client.decode(ctx, chan, buffer, VOID);
fail("Expected an IndexOutOfBoundsException");
} catch (IndexOutOfBoundsException oob) { }
}
@Test
public void negativeSize() throws Exception {
// Fails on the first call to ensureReadable because the size encoded
// in the first 4 bytes is much larger than it should be
ChannelBuffer buffer = new ReadOnlyChannelBuffer(
ChannelBuffers.wrappedBuffer(new byte[] { -1, -1, -1, -1, 1 }));
try {
region_client.decode(ctx, chan, buffer, VOID);
fail("Expected an IllegalArgumentException");
} catch (IllegalArgumentException ex) { }
buffer = ChannelBuffers.wrappedBuffer(new byte[] { -1, -1, -1, -1, 1 });
try {
region_client.decode(ctx, chan, buffer, VOID);
fail("Expected an IllegalArgumentException");
} catch (IllegalArgumentException ex) { }
}
@Test
public void rpcTooBig() throws Exception {
// we only accept RPCs up to 256MBs in size right now. In order to avoid
// allocating 256MB for unit testing, we'll tell the region client to
// skip the ensureReadable call. Just over the line is 268435456 bytes
// See HBaseRpc.MAX_BYTE_ARRAY_MASK
PowerMockito.mockStatic(RegionClient.class);
PowerMockito.doNothing().when(RegionClient.class, "ensureReadable",
any(ChannelBuffer.class), anyInt());
ChannelBuffer buffer = new ReadOnlyChannelBuffer(
ChannelBuffers.wrappedBuffer(new byte[] { 16, 0, 0, 0, 1 }));
try {
region_client.decode(ctx, chan, buffer, VOID);
fail("Expected an IllegalArgumentException");
} catch (IllegalArgumentException ex) { }
buffer = ChannelBuffers.wrappedBuffer(new byte[] { 16, 0, 0, 0, 1 });
try {
region_client.decode(ctx, chan, buffer, VOID);
fail("Expected an IllegalArgumentException");
} catch (IllegalArgumentException ex) { }
}
@Test (expected = IndexOutOfBoundsException.class)
public void nothingAfterInitialLength() throws Exception {
// gets into HBaseRpc.readProtobuf and tosses an exception when it tries
// to read the varint.
final ChannelBuffer buffer = new ReadOnlyChannelBuffer(
ChannelBuffers.wrappedBuffer(new byte[] { 0, 0, 0, 0 }));
region_client.decode(ctx, chan, buffer, VOID);
}
@Test
public void protobufVarintOnly() throws Exception {
// gets into HBaseRpc.readProtobuf and tosses an exception when it tries
// to readBytes on the non-array backed buffer
ChannelBuffer buffer = new ReadOnlyChannelBuffer(
ChannelBuffers.wrappedBuffer(new byte[] { 0, 0, 0, 1, 1 }));
try {
region_client.decode(ctx, chan, buffer, VOID);
fail("Expected an IndexOutOfBoundsException");
} catch (IndexOutOfBoundsException oob) { }
buffer = ChannelBuffers.wrappedBuffer(new byte[] { 0, 0, 0, 1, 1 });
try {
region_client.decode(ctx, chan, buffer, VOID);
fail("Expected an IndexOutOfBoundsException");
} catch (IndexOutOfBoundsException oob) { }
}
@Test
public void rpcNotInMap() throws Exception {
// doesn't matter if the rest of the message is missing, we fail as
// the ID isn't in the map. It also doesn't matter if it's negative since
// the ID counter can rollover
ChannelBuffer buffer = new ReadOnlyChannelBuffer(
ChannelBuffers.wrappedBuffer(new byte[] { 0, 0, 0, 1, 2, 8, 42 }));
region_client.decode(ctx, chan, buffer, VOID);
assertEquals(1, region_client.stats().rpcResponsesUnknown());
buffer = new ReadOnlyChannelBuffer(
ChannelBuffers.wrappedBuffer(
new byte[] { 0, 0, 0, 1, 6, 8, -42, -1, -1, -1, 15 }));
region_client.decode(ctx, chan, buffer, VOID);
assertEquals(2, region_client.stats().rpcResponsesUnknown());
buffer = ChannelBuffers.wrappedBuffer(new byte[] { 0, 0, 0, 1, 2, 8, 42 });
region_client.decode(ctx, chan, buffer, VOID);
assertEquals(3, region_client.stats().rpcResponsesUnknown());
buffer = ChannelBuffers.wrappedBuffer(
new byte[] { 0, 0, 0, 1, 6, 8, -42, -1, -1, -1, 15 });
region_client.decode(ctx, chan, buffer, VOID);
assertEquals(4, region_client.stats().rpcResponsesUnknown());
}
@Test
public void noCallId() throws Exception {
// passes the header parsing since it has a size of zero, but then we check
// to see if it has a call ID and it won't.
ChannelBuffer buffer = new ReadOnlyChannelBuffer(
ChannelBuffers.wrappedBuffer(new byte[] { 0, 0, 0, 1, 0 }));
try {
region_client.decode(ctx, chan, buffer, VOID);
fail("Expected an NonRecoverableException");
} catch (NonRecoverableException ex) { }
buffer = ChannelBuffers.wrappedBuffer(new byte[] { 0, 0, 0, 1, 0 });
try {
region_client.decode(ctx, chan, buffer, VOID);
fail("Expected an NonRecoverableException");
} catch (NonRecoverableException ex) { }
}
@Test
public void nullContext() throws Exception {
// just shows we don't care about the context object
final int id = 42;
final ChannelBuffer buffer = buildGoodResponse(true, id);
final GetRequest get = new GetRequest(TABLE, ROW);
final Deferred<Object> deferred = get.getDeferred();
inflightTheRpc(id, get);
region_client.decode(null, chan, buffer, VOID);
@SuppressWarnings("unchecked")
final List<KeyValue> kvs = (List<KeyValue>)deferred.joinUninterruptibly();
assertEquals(1, kvs.size());
assertArrayEquals(ROW, kvs.get(0).key());
assertArrayEquals(FAMILY, kvs.get(0).family());
assertArrayEquals(QUALIFIER, kvs.get(0).qualifier());
assertArrayEquals(VALUE, kvs.get(0).value());
assertEquals(TIMESTAMP, kvs.get(0).timestamp());
assertEquals(1, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
}
@Test
public void nullChannel() throws Exception {
// just shows we don't care about the channel either
final int id = 42;
final ChannelBuffer buffer = buildGoodResponse(true, id);
final GetRequest get = new GetRequest(TABLE, ROW);
final Deferred<Object> deferred = get.getDeferred();
inflightTheRpc(id, get);
region_client.decode(ctx, null, buffer, VOID);
@SuppressWarnings("unchecked")
final List<KeyValue> kvs = (List<KeyValue>)deferred.joinUninterruptibly();
assertEquals(1, kvs.size());
assertArrayEquals(ROW, kvs.get(0).key());
assertArrayEquals(FAMILY, kvs.get(0).family());
assertArrayEquals(QUALIFIER, kvs.get(0).qualifier());
assertArrayEquals(VALUE, kvs.get(0).value());
assertEquals(TIMESTAMP, kvs.get(0).timestamp());
assertEquals(1, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(0).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
}
@Test (expected = IndexOutOfBoundsException.class)
public void emptyBuffer() throws Exception {
// This shouldn't happen since we should only get a buffer if the socket
// had some data.
final ChannelBuffer buf = ChannelBuffers.wrappedBuffer(new byte[] {});
region_client.decode(ctx, chan, buf, VOID);
}
@Test (expected = NullPointerException.class)
public void nullBuffer() throws Exception {
// This should never happen, in theory
region_client.decode(ctx, chan, null, VOID);
}
@Test
public void timedoutRpcThenGoodRpc090() throws Exception {
resetMockClient();
Whitebox.setInternalState(region_client, "server_version",
RegionClient.SERVER_VERSION_090_AND_BEFORE);
// this one has been timed out and we assume it was popped from the map
final GetRequest timedout = new GetRequest(TABLE, ROW);
timedout.setTimeout(1);
final Deferred<Object> deferred_to = timedout.getDeferred();
inflightTheRpc(1, timedout);
assertEquals(1, rpcs_inflight.size());
assertEquals(1, timer.tasks.size());
assertEquals(1, (long)timer.tasks.get(0).getValue());
timer.tasks.get(0).getKey().run(null);
assertEquals(0, rpcs_inflight.size());
try {
deferred_to.join(100);
} catch (RpcTimedOutException ex) { }
final GetRequest rpc = new GetRequest(TABLE, KEY);
final Deferred<Object> deferred = rpc.getDeferred();
inflightTheRpc(3, rpc);
region_client.messageReceived(ctx, getMessage(
ChannelBuffers.wrappedBuffer(MULTI_ACTION_RESPONSE_090)));
region_client.messageReceived(ctx, getMessage(
ChannelBuffers.wrappedBuffer(GET_RESPONSE_090)));
@SuppressWarnings("unchecked")
final ArrayList<KeyValue> row = (ArrayList<KeyValue>)deferred.join(1);
assertArrayEquals(new byte[] { 0, 0, 0, 100}, row.get(0).qualifier());
assertArrayEquals("*".getBytes(), row.get(0).value());
assertEquals(1, region_client.stats().rpcsTimedout());
assertEquals(2, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(1).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
verify(timer.timeouts.get(1), times(1)).cancel();
}
@Test
public void timedoutRpcThenGoodRpc094() throws Exception {
resetMockClient();
Whitebox.setInternalState(region_client, "server_version",
RegionClient.SERVER_VERSION_092_OR_ABOVE);
// this one has been timed out and we assume it was popped from the map
final GetRequest timedout = new GetRequest(TABLE, ROW);
timedout.setTimeout(1);
final Deferred<Object> deferred_to = timedout.getDeferred();
inflightTheRpc(1, timedout);
assertEquals(1, rpcs_inflight.size());
assertEquals(1, timer.tasks.size());
assertEquals(1, (long)timer.tasks.get(0).getValue());
timer.tasks.get(0).getKey().run(null);
assertEquals(0, rpcs_inflight.size());
try {
deferred_to.join(100);
} catch (RpcTimedOutException ex) { }
final GetRequest rpc = new GetRequest(TABLE, KEY);
final Deferred<Object> deferred = rpc.getDeferred();
inflightTheRpc(3, rpc);
region_client.messageReceived(ctx, getMessage(
ChannelBuffers.wrappedBuffer(MULTI_ACTION_RESPONSE_094)));
region_client.messageReceived(ctx, getMessage(
ChannelBuffers.wrappedBuffer(GET_RESPONSE_094)));
@SuppressWarnings("unchecked")
final ArrayList<KeyValue> row = (ArrayList<KeyValue>)deferred.join(1);
assertArrayEquals(new byte[] { 0, 0, 0, 100}, row.get(0).qualifier());
assertArrayEquals("*".getBytes(), row.get(0).value());
assertEquals(1, region_client.stats().rpcsTimedout());
assertEquals(2, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(1).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
verify(timer.timeouts.get(1), times(1)).cancel();
}
@Test
public void timedoutRpcThenGoodRpc() throws Exception {
resetMockClient();
// this one has been timed out and we assume it was popped from the map
final GetRequest timedout = new GetRequest(TABLE, ROW);
timedout.setTimeout(1);
final Deferred<Object> deferred_to = timedout.getDeferred();
final ChannelBuffer buffer1 = buildGoodResponse(true, 1);
inflightTheRpc(1, timedout);
assertEquals(1, rpcs_inflight.size());
assertEquals(1, timer.tasks.size());
assertEquals(1, (long)timer.tasks.get(0).getValue());
timer.tasks.get(0).getKey().run(null);
assertEquals(0, rpcs_inflight.size());
try {
deferred_to.join(100);
} catch (RpcTimedOutException ex) { }
final int id = 42;
final ChannelBuffer buffer2 = buildGoodResponse(true, id);
final GetRequest get = new GetRequest(TABLE, ROW);
final Deferred<Object> deferred = get.getDeferred();
// only put the second RPC in the map
inflightTheRpc(id, get);
region_client.messageReceived(ctx, getMessage(buffer1));
region_client.messageReceived(ctx, getMessage(buffer2));
@SuppressWarnings("unchecked")
final List<KeyValue> kvs = (List<KeyValue>)deferred.join(100);
assertEquals(1, kvs.size());
assertArrayEquals(ROW, kvs.get(0).key());
assertArrayEquals(FAMILY, kvs.get(0).family());
assertArrayEquals(QUALIFIER, kvs.get(0).qualifier());
assertArrayEquals(VALUE, kvs.get(0).value());
assertEquals(TIMESTAMP, kvs.get(0).timestamp());
assertEquals(1, region_client.stats().rpcsTimedout());
assertEquals(2, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(1).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
verify(timer.timeouts.get(1), times(1)).cancel();
}
@Test
public void combinedTimedoutRpcThenGoodRpc090() throws Exception {
resetMockClient();
Whitebox.setInternalState(region_client, "server_version",
RegionClient.SERVER_VERSION_090_AND_BEFORE);
// this one has been timed out and we assume it was popped from the map
// never mind that it's a get, not a multi action for this 94 test
final GetRequest timedout = new GetRequest(TABLE, ROW);
timedout.setTimeout(1);
final Deferred<Object> deferred_to = timedout.getDeferred();
inflightTheRpc(1, timedout);
assertEquals(1, rpcs_inflight.size());
assertEquals(1, timer.tasks.size());
assertEquals(1, (long)timer.tasks.get(0).getValue());
timer.tasks.get(0).getKey().run(null);
assertEquals(0, rpcs_inflight.size());
try {
deferred_to.join(100);
} catch (RpcTimedOutException ex) { }
final byte[] data = new byte[MULTI_ACTION_RESPONSE_090.length +
GET_RESPONSE_090.length];
System.arraycopy(MULTI_ACTION_RESPONSE_090, 0, data, 0,
MULTI_ACTION_RESPONSE_090.length);
System.arraycopy(GET_RESPONSE_090, 0, data, MULTI_ACTION_RESPONSE_090.length,
GET_RESPONSE_090.length);
final GetRequest rpc = new GetRequest(TABLE, KEY);
final Deferred<Object> deferred = rpc.getDeferred();
inflightTheRpc(3, rpc);
region_client.messageReceived(ctx,
getMessage(ChannelBuffers.wrappedBuffer(data)));
@SuppressWarnings("unchecked")
final ArrayList<KeyValue> row = (ArrayList<KeyValue>)deferred.join(1);
assertArrayEquals(new byte[] { 0, 0, 0, 100}, row.get(0).qualifier());
assertArrayEquals("*".getBytes(), row.get(0).value());
assertEquals(1, region_client.stats().rpcsTimedout());
assertEquals(2, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(1).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
verify(timer.timeouts.get(1), times(1)).cancel();
}
@Test
public void combinedTimedoutRpcThenGoodRpc094() throws Exception {
resetMockClient();
Whitebox.setInternalState(region_client, "server_version",
RegionClient.SERVER_VERSION_092_OR_ABOVE);
// this one has been timed out and we assume it was popped from the map
// never mind that it's a get, not a multi action for this 94 test
final GetRequest timedout = new GetRequest(TABLE, ROW);
timedout.setTimeout(1);
final Deferred<Object> deferred_to = timedout.getDeferred();
inflightTheRpc(1, timedout);
assertEquals(1, rpcs_inflight.size());
assertEquals(1, timer.tasks.size());
assertEquals(1, (long)timer.tasks.get(0).getValue());
timer.tasks.get(0).getKey().run(null);
assertEquals(0, rpcs_inflight.size());
try {
deferred_to.join(100);
} catch (RpcTimedOutException ex) { }
final byte[] data = new byte[MULTI_ACTION_RESPONSE_094.length +
GET_RESPONSE_094.length];
System.arraycopy(MULTI_ACTION_RESPONSE_094, 0, data, 0,
MULTI_ACTION_RESPONSE_094.length);
System.arraycopy(GET_RESPONSE_094, 0, data, MULTI_ACTION_RESPONSE_094.length,
GET_RESPONSE_094.length);
final GetRequest rpc = new GetRequest(TABLE, KEY);
final Deferred<Object> deferred = rpc.getDeferred();
inflightTheRpc(3, rpc);
region_client.messageReceived(ctx,
getMessage(ChannelBuffers.wrappedBuffer(data)));
@SuppressWarnings("unchecked")
final ArrayList<KeyValue> row = (ArrayList<KeyValue>)deferred.join(1);
assertArrayEquals(new byte[] { 0, 0, 0, 100}, row.get(0).qualifier());
assertArrayEquals("*".getBytes(), row.get(0).value());
assertEquals(1, region_client.stats().rpcsTimedout());
assertEquals(2, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(1).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
verify(timer.timeouts.get(1), times(1)).cancel();
}
@Test
public void combinedTimedoutRpcThenGoodRpc() throws Exception {
resetMockClient();
final GetRequest timedout = new GetRequest(TABLE, ROW);
timedout.setTimeout(1);
final Deferred<Object> deferred_to = timedout.getDeferred();
inflightTheRpc(1, timedout);
assertEquals(1, rpcs_inflight.size());
assertEquals(1, timer.tasks.size());
assertEquals(1, (long)timer.tasks.get(0).getValue());
timer.tasks.get(0).getKey().run(null);
assertEquals(0, rpcs_inflight.size());
final ChannelBuffer buffer1 = buildGoodResponse(true, 1);
final int id = 42;
final ChannelBuffer buffer2 = buildGoodResponse(true, id);
final ChannelBuffer combined = new BigEndianHeapChannelBuffer(
buffer1.writerIndex() + buffer2.writerIndex());
combined.writeBytes(buffer1);
combined.writeBytes(buffer2);
final GetRequest get = new GetRequest(TABLE, ROW);
final Deferred<Object> deferred = get.getDeferred();
// only put the second RPC in the map
inflightTheRpc(id, get);
region_client.messageReceived(ctx, getMessage(combined));
try {
deferred_to.join(100);
} catch (RpcTimedOutException ex) { }
@SuppressWarnings("unchecked")
final List<KeyValue> kvs = (List<KeyValue>)deferred.join(100);
assertEquals(1, kvs.size());
assertArrayEquals(ROW, kvs.get(0).key());
assertArrayEquals(FAMILY, kvs.get(0).family());
assertArrayEquals(QUALIFIER, kvs.get(0).qualifier());
assertArrayEquals(VALUE, kvs.get(0).value());
assertEquals(TIMESTAMP, kvs.get(0).timestamp());
assertEquals(1, region_client.stats().rpcsTimedout());
assertEquals(2, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(1).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
verify(timer.timeouts.get(1), times(1)).cancel();
}
@Test
public void chunkedLateRpcThenGoodRpc090() throws Exception {
resetMockClient();
Whitebox.setInternalState(region_client, "server_version",
RegionClient.SERVER_VERSION_090_AND_BEFORE);
final GetRequest timedout = new GetRequest(TABLE, ROW);
timedout.setTimeout(1);
final Deferred<Object> deferred_to = timedout.getDeferred();
inflightTheRpc(1, timedout);
assertEquals(1, rpcs_inflight.size());
assertEquals(1, timer.tasks.size());
assertEquals(1, (long)timer.tasks.get(0).getValue());
timer.tasks.get(0).getKey().run(null);
assertEquals(0, rpcs_inflight.size());
final GetRequest rpc = new GetRequest(TABLE, KEY);
final Deferred<Object> deferred = rpc.getDeferred();
inflightTheRpc(3, rpc);
int cutoff = 48;
final byte[] data = new byte[MULTI_ACTION_RESPONSE_090.length +
GET_RESPONSE_090.length - cutoff];
System.arraycopy(MULTI_ACTION_RESPONSE_090, cutoff, data, 0,
MULTI_ACTION_RESPONSE_090.length - cutoff);
System.arraycopy(GET_RESPONSE_090, 0, data,
MULTI_ACTION_RESPONSE_090.length - cutoff,
GET_RESPONSE_090.length);
region_client.messageReceived(ctx, getMessage(
Arrays.copyOfRange(MULTI_ACTION_RESPONSE_090, 0, cutoff)));
region_client.messageReceived(ctx, getMessage(data));
try {
deferred_to.join(100);
} catch (RpcTimedOutException ex) { }
@SuppressWarnings("unchecked")
final ArrayList<KeyValue> row = (ArrayList<KeyValue>)deferred.join(1);
assertArrayEquals(new byte[] { 0, 0, 0, 100}, row.get(0).qualifier());
assertArrayEquals("*".getBytes(), row.get(0).value());
assertEquals(1, region_client.stats().rpcsTimedout());
assertEquals(2, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(1).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
verify(timer.timeouts.get(1), times(1)).cancel();
}
@Test
public void chunkedLateRpcThenGoodRpc094() throws Exception {
resetMockClient();
Whitebox.setInternalState(region_client, "server_version",
RegionClient.SERVER_VERSION_092_OR_ABOVE);
final GetRequest timedout = new GetRequest(TABLE, ROW);
timedout.setTimeout(1);
final Deferred<Object> deferred_to = timedout.getDeferred();
inflightTheRpc(1, timedout);
assertEquals(1, rpcs_inflight.size());
assertEquals(1, timer.tasks.size());
assertEquals(1, (long)timer.tasks.get(0).getValue());
timer.tasks.get(0).getKey().run(null);
assertEquals(0, rpcs_inflight.size());
final GetRequest rpc = new GetRequest(TABLE, KEY);
final Deferred<Object> deferred = rpc.getDeferred();
inflightTheRpc(3, rpc);
int cutoff = 48;
final byte[] data = new byte[MULTI_ACTION_RESPONSE_094.length +
GET_RESPONSE_094.length - cutoff];
System.arraycopy(MULTI_ACTION_RESPONSE_094, cutoff, data, 0,
MULTI_ACTION_RESPONSE_094.length - cutoff);
System.arraycopy(GET_RESPONSE_094, 0, data,
MULTI_ACTION_RESPONSE_094.length - cutoff,
GET_RESPONSE_094.length);
region_client.messageReceived(ctx, getMessage(
Arrays.copyOfRange(MULTI_ACTION_RESPONSE_094, 0, cutoff)));
region_client.messageReceived(ctx, getMessage(data));
try {
deferred_to.join(100);
} catch (RpcTimedOutException ex) { }
@SuppressWarnings("unchecked")
final ArrayList<KeyValue> row = (ArrayList<KeyValue>)deferred.join(1);
assertArrayEquals(new byte[] { 0, 0, 0, 100}, row.get(0).qualifier());
assertArrayEquals("*".getBytes(), row.get(0).value());
assertEquals(1, region_client.stats().rpcsTimedout());
assertEquals(2, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(1).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
verify(timer.timeouts.get(1), times(1)).cancel();
}
@Test
public void chunkedLateRpcThenGoodRpc() throws Exception {
resetMockClient();
final GetRequest timedout = new GetRequest(TABLE, ROW);
timedout.setTimeout(1);
final Deferred<Object> deferred_to = timedout.getDeferred();
inflightTheRpc(1, timedout);
assertEquals(1, rpcs_inflight.size());
assertEquals(1, timer.tasks.size());
assertEquals(1, (long)timer.tasks.get(0).getValue());
timer.tasks.get(0).getKey().run(null);
assertEquals(0, rpcs_inflight.size());
final ChannelBuffer buffer1 = buildGoodResponse(true, 1);
final int id = 42;
final ChannelBuffer buffer2 = buildGoodResponse(true, id);
final ChannelBuffer combined = new BigEndianHeapChannelBuffer(
buffer1.writerIndex() + buffer2.writerIndex());
combined.writeBytes(buffer1);
combined.writeBytes(buffer2);
final byte[] bytes = new byte[combined.readableBytes()];
combined.readBytes(bytes);
final GetRequest get = new GetRequest(TABLE, ROW);
final Deferred<Object> deferred = get.getDeferred();
// only put the second RPC in the map
inflightTheRpc(id, get);
region_client.messageReceived(ctx, getMessage(
Arrays.copyOfRange(bytes, 0, 48)));
region_client.messageReceived(ctx, getMessage(
Arrays.copyOfRange(bytes, 48, bytes.length)));
try {
deferred_to.join(100);
} catch (RpcTimedOutException ex) { }
@SuppressWarnings("unchecked")
final List<KeyValue> kvs = (List<KeyValue>)deferred.join(100);
assertEquals(1, kvs.size());
assertArrayEquals(ROW, kvs.get(0).key());
assertArrayEquals(FAMILY, kvs.get(0).family());
assertArrayEquals(QUALIFIER, kvs.get(0).qualifier());
assertArrayEquals(VALUE, kvs.get(0).value());
assertEquals(TIMESTAMP, kvs.get(0).timestamp());
assertEquals(1, region_client.stats().rpcsTimedout());
assertEquals(2, timer.tasks.size());
assertEquals(60000, (long)timer.tasks.get(1).getValue());
verify(timer.timeouts.get(0), times(1)).cancel();
verify(timer.timeouts.get(1), times(1)).cancel();
}
/**
* Puts the RPC in the map with the given ID and tells the RPC that this
* region client is handling it
* @param id The ID to use
* @param rpc The RPC
*/
private void inflightTheRpc(final int id, final HBaseRpc rpc) {
rpcs_inflight.put(id, rpc);
rpc.rpc_id = id;
rpc.enqueueTimeout(region_client);
}
/**
* Creates a simple GetRequest response with some dummy data and a single
* column in a row. The request lacks cell meta data.
* @param array_backed Whether or not the Channel Buffer should have a backing
* array or not to exercise zero-copy code paths
* @param id The ID of the RPC that we're responding to.
* @return A channel buffer with a ProtoBuf object as HBase would return
* @throws IOException If we couldn't write the ProtoBuf for some reason
*/
private ChannelBuffer buildGoodResponse(final boolean array_backed, final int id)
throws IOException {
final Cell cell = Cell.newBuilder()
.setRow(Bytes.wrap(ROW))
.setFamily(Bytes.wrap(FAMILY))
.setQualifier(Bytes.wrap(QUALIFIER))
.setTimestamp(TIMESTAMP)
.setValue(Bytes.wrap(VALUE))
.build();
final Result result = Result.newBuilder()
.addCell(cell)
.build();
final GetResponse get_response =
GetResponse.newBuilder()
.setResult(result)
.build();
// TODO - test objects that return cell blocks, possibly scanners
// final CellBlockMeta meta = CellBlockMeta.newBuilder()
// .setLength(cell.getSerializedSize())
// .build();
final RPCPB.ResponseHeader header = RPCPB.ResponseHeader.newBuilder()
.setCallId(id)
//.setCellBlockMeta(meta)
.build();
final int hlen = header.getSerializedSize();
final int vhlen = CodedOutputStream.computeRawVarint32Size(hlen);
final int pblen = get_response.getSerializedSize();
final int vlen = CodedOutputStream.computeRawVarint32Size(pblen);
final byte[] buf = new byte[hlen + vhlen + vlen + pblen + 4];
final CodedOutputStream out = CodedOutputStream.newInstance(buf, 4,
hlen + vhlen + vlen + pblen);
out.writeMessageNoTag(header);
out.writeMessageNoTag(get_response);
Bytes.setInt(buf, buf.length - 4);
if (array_backed) {
return ChannelBuffers.wrappedBuffer(buf);
} else {
return new ReadOnlyChannelBuffer(ChannelBuffers.wrappedBuffer(buf));
}
}
/** Simple test implementation of the HBaseException class */
class TestingHBaseException extends HBaseException {
private static final long serialVersionUID = 7717718589747017699L;
TestingHBaseException(final String msg) {
super(msg);
}
}
/** Creates a new mock client that isn't spied. Necessary for the replay
* tests
*/
private void resetMockClient() throws Exception {
region_client = new RegionClient(hbase_client);
Whitebox.setInternalState(region_client, "chan", chan);
Whitebox.setInternalState(region_client, "server_version",
RegionClient.SERVER_VERSION_095_OR_ABOVE);
rpcs_inflight = Whitebox.getInternalState(
region_client, "rpcs_inflight");
}
/**
* Generate a mock MessageEvent from a byte array
* @param data The data to pass on
* @return The event to hand to messageReceived(
*/
static MessageEvent getMessage(final byte[] data) {
final ChannelBuffer buf = ChannelBuffers.wrappedBuffer(data);
return getMessage(buf);
}
/**
* Generate a mock MessageEvent from a ChannelBuffer
* @param buf The buffer to pass on
* @return The event to hand to messageReceived(
*/
static MessageEvent getMessage(final ChannelBuffer buf) {
final MessageEvent event = mock(MessageEvent.class);
when(event.getMessage()).thenReturn(buf);
return event;
}
/** Response for a MultiAction request with PUTs from 0.94 */
public static final byte[] MULTI_ACTION_RESPONSE_094 = new byte[] {
0x00, 0x00, 0x00, (byte) 0xc9, 0x02, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00,
0x00, 0x00, 0x43, 0x43, 0x00, 0x00, 0x00, 0x01, 0x35, 0x74, 0x65, 0x73,
0x74, 0x2c, 0x2c, 0x31, 0x34, 0x34, 0x33, 0x39, 0x30, 0x39, 0x35, 0x30,
0x31, 0x33, 0x35, 0x30, 0x2e, 0x61, 0x35, 0x35, 0x30, 0x65, 0x38, 0x34,
0x66, 0x36, 0x38, 0x34, 0x63, 0x63, 0x65, 0x34, 0x64, 0x33, 0x37, 0x62,
0x66, 0x31, 0x34, 0x66, 0x34, 0x34, 0x61, 0x33, 0x39, 0x33, 0x36, 0x34,
0x34, 0x2e, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x64, 0x00, 0x25,
0x25, 0x00, 0x00, 0x00, 0x00
};
/** Response for a GET request from 0.94 */
public static final byte[] GET_RESPONSE_094 = new byte[] {
0x00, 0x00, 0x00, 0x03, 0x02, 0x00,
0x00, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x25, 0x25, 0x00, 0x00, 0x00,
0x23, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
0x01, 0x00, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x01, 0x74, 0x00, 0x00,
0x00, 0x64, 0x00, 0x00, 0x01, 0x50, 0x2f, (byte) 0xe2, (byte) 0x82,
(byte) 0x9f, 0x04, 0x2a };
/** Response for a MultiAction request with PUTs from 0.90 */
public static final byte[] MULTI_ACTION_RESPONSE_090 = new byte[] {
0x00, 0x00, 0x00, (byte) 0xc9, 0x00, 0x3a, 0x3a, 0x00, 0x00, 0x00, 0x01,
0x35, 0x74, 0x65, 0x73, 0x74, 0x2c, 0x2c, 0x31, 0x34, 0x34, 0x33, 0x39,
0x32, 0x30, 0x39, 0x33, 0x35, 0x36, 0x32, 0x39, 0x2e, 0x66, 0x65, 0x36,
0x36, 0x35, 0x36, 0x32, 0x62, 0x37, 0x35, 0x61, 0x30, 0x30, 0x38, 0x63,
0x30, 0x66, 0x30, 0x63, 0x35, 0x35, 0x33, 0x32, 0x30, 0x64, 0x63, 0x65,
0x62, 0x37, 0x61, 0x37, 0x66, 0x2e, (byte) 0xff, (byte) 0xff,
(byte) 0xff, (byte) 0xff };
/** Response for a GET request from 0.90 */
public static final byte[] GET_RESPONSE_090 = new byte[] {
0x00, 0x00, 0x00, 0x03, 0x00, 0x25, 0x25, 0x00, 0x00, 0x00, 0x23, 0x00,
0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x01, 0x00,
0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x01, 0x74, 0x00, 0x00, 0x00, 0x64,
0x00, 0x00, 0x01, 0x50, 0x30, 0x66, (byte) 0x8c, (byte) 0xad, 0x04, 0x2a };
}