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.Mockito.when;
import java.util.ArrayList;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
import com.stumbleupon.async.Deferred;
@RunWith(PowerMockRunner.class)
//"Classloader hell"... It's real. Tell PowerMock to ignore these classes
//because they fiddle with the class loader. We don't test them anyway.
@PowerMockIgnore({"javax.management.*", "javax.xml.*",
"ch.qos.*", "org.slf4j.*",
"com.sum.*", "org.xml.*"})
@PrepareForTest({ HBaseClient.class, RegionClient.class, RegionInfo.class,
HBaseRpc.class, RegionClientStats.class })
public class TestHBaseClientLocateRegion extends BaseTestHBaseClient {
private Deferred<Object> root_deferred;
private GetRequest get;
@Before
public void beforeLocal() throws Exception {
root_deferred = new Deferred<Object>();
when(zkclient.getDeferredRoot()).thenReturn(root_deferred);
get = new GetRequest(TABLE, KEY);
Whitebox.setInternalState(client, "has_root", true);
}
//-------- ROOT AND DEAD CLIENT ----------
@Test
public void locateRegionRootHadRootLookupInZK() throws Exception {
final Object obj = Whitebox.invokeMethod(client, "locateRegion",
get, HBaseClient.ROOT, EMPTY_ARRAY);
assertTrue(root_deferred == obj);
assertCounters(0, 0, 0);
}
@Test
public void locateRegionRootNoRootLookupInZK() throws Exception {
Whitebox.setInternalState(client, "has_root", false);
final Object obj = Whitebox.invokeMethod(client, "locateRegion",
get, HBaseClient.ROOT, EMPTY_ARRAY);
assertTrue(root_deferred == obj);
assertCounters(0, 0, 0);
}
//--------- META AND DEAD CLIENT ------------
@Test
public void locateRegionMetaHadRootLookupInZK() throws Exception {
final Object obj = Whitebox.invokeMethod(client, "locateRegion",
get, HBaseClient.META, EMPTY_ARRAY);
assertTrue(root_deferred == obj);
assertCounters(0, 0, 0);
}
@Test
public void locateRegionMeta96HadRootLookupInZK() throws Exception {
final Object obj = Whitebox.invokeMethod(client, "locateRegion",
get, HBaseClient.HBASE96_META, EMPTY_ARRAY);
assertTrue(root_deferred == obj);
assertCounters(0, 0, 0);
}
@Test
public void locateRegionMetaNoRootLookupInZK() throws Exception {
Whitebox.setInternalState(client, "has_root", false);
final Object obj = Whitebox.invokeMethod(client, "locateRegion",
get, HBaseClient.META, EMPTY_ARRAY);
assertTrue(root_deferred == obj);
assertCounters(0, 0, 0);
}
@Test
public void locateRegionMeta96NoRootLookupInZK() throws Exception {
Whitebox.setInternalState(client, "has_root", false);
final Object obj = Whitebox.invokeMethod(client, "locateRegion",
get, HBaseClient.HBASE96_META, EMPTY_ARRAY);
assertTrue(root_deferred == obj);
assertCounters(0, 0, 0);
}
// --------------- ROOT RECURSION --------------
// These make sure we don't check the root region for the root region
// because that would be plain silly.
@SuppressWarnings("unchecked")
@Test
public void locateRegionRootHadRootLiveClient() throws Exception {
setLiveRootClient();
final Object obj = Whitebox.invokeMethod(client, "locateRegion",
get, HBaseClient.ROOT, EMPTY_ARRAY);
assertTrue(root_deferred != obj);
assertNull(((Deferred<Object>)obj).joinUninterruptibly());
assertCounters(0, 0, 0);
}
//--------------- META LOOKUP IN ROOT --------------
@Test
public void locateRegionMetaLiveClient() throws Exception {
clearCaches();
Whitebox.setInternalState(client, "rootregion", rootclient);
when(rootclient.isAlive()).thenReturn(true);
when(rootclient.getClosestRowBefore(any(RegionInfo.class), any(byte[].class),
any(byte[].class), any(byte[].class)))
.thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(metaRow()));
final Deferred<Object> obj = Whitebox.invokeMethod(client, "locateRegion",
get, HBaseClient.META, EMPTY_ARRAY);
assertTrue(root_deferred != obj);
final RegionClient rc = (RegionClient)obj.joinUninterruptibly();
assertCounters(1, 0, 0);
assertEquals(1, client2regions.size());
assertNotNull(client2regions.get(rc));
}
@Test
public void locateRegionMetaLiveClientTableNotFound() throws Exception {
Whitebox.setInternalState(client, "rootregion", rootclient);
when(rootclient.isAlive()).thenReturn(true);
when(rootclient.getClosestRowBefore(any(RegionInfo.class), any(byte[].class),
any(byte[].class), any(byte[].class)))
.thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(
new ArrayList<KeyValue>(0)));
final Deferred<Object> obj = Whitebox.invokeMethod(client, "locateRegion",
get, HBaseClient.META, EMPTY_ARRAY);
assertTrue(root_deferred != obj);
assertCounters(1, 0, 0);
assertEquals(2, client2regions.size());
TableNotFoundException ex = null;
try {
obj.joinUninterruptibly();
fail("Expected a TableNotFoundException exception");
} catch (final TableNotFoundException e) {
ex = e;
}
assertArrayEquals(HBaseClient.META, ex.getTable());
}
@Test
public void locateRegionMetaLiveClientRecoverableException() throws Exception {
Whitebox.setInternalState(client, "rootregion", rootclient);
when(rootclient.isAlive()).thenReturn(true);
when(rootclient.getClosestRowBefore(any(RegionInfo.class), any(byte[].class),
any(byte[].class), any(byte[].class)))
.thenReturn(Deferred.<ArrayList<KeyValue>>fromError(
new RegionOfflineException(EMPTY_ARRAY)))
.thenReturn(Deferred.<ArrayList<KeyValue>>fromError(
new RegionOfflineException(EMPTY_ARRAY)))
.thenReturn(Deferred.<ArrayList<KeyValue>>fromError(
new RegionOfflineException(EMPTY_ARRAY)))
.thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(metaRow()));
final Deferred<Object> obj = Whitebox.invokeMethod(client, "locateRegion",
get, HBaseClient.META, EMPTY_ARRAY);
assertTrue(root_deferred != obj);
assertCounters(4, 0, 0);
assertEquals(3, client2regions.size());
final RegionClient rc = (RegionClient)obj.joinUninterruptibly();
assertNotNull(client2regions.get(rc));
}
// This used to be a tight loop that would continue indefinitely since we
// didn't track how many times we looped.
@Test
public void locateRegionMetaLiveClientTooManyAttempts() throws Exception {
Whitebox.setInternalState(client, "rootregion", rootclient);
when(rootclient.isAlive()).thenReturn(true);
final Deferred<Object> deferred = get.getDeferred();
when(rootclient.getClosestRowBefore(any(RegionInfo.class), any(byte[].class),
any(byte[].class), any(byte[].class)))
.thenAnswer(new Answer<Deferred<ArrayList<KeyValue>>>() {
@Override
public Deferred<ArrayList<KeyValue>> answer(InvocationOnMock invocation)
throws Throwable {
return Deferred.<ArrayList<KeyValue>>fromError(
new RegionOfflineException(EMPTY_ARRAY));
}
});
final Deferred<Object> obj = Whitebox.invokeMethod(client, "locateRegion",
get, HBaseClient.META, EMPTY_ARRAY);
assertTrue(root_deferred != obj);
assertCounters(12, 0, 0);
assertEquals(2, client2regions.size());
try {
obj.joinUninterruptibly();
fail("Expected a NonRecoverableException exception");
} catch (NonRecoverableException e) { }
try {
deferred.join();
fail("Expected a NonRecoverableException exception");
} catch (NonRecoverableException e) { }
}
@Test (expected = RuntimeException.class)
public void locateRegionMetaLiveClientNonRecoverableException() throws Exception {
Whitebox.setInternalState(client, "rootregion", rootclient);
when(rootclient.isAlive()).thenReturn(true);
when(rootclient.getClosestRowBefore(any(RegionInfo.class), any(byte[].class),
any(byte[].class), any(byte[].class)))
.thenReturn(Deferred.<ArrayList<KeyValue>>fromError(
new RuntimeException("Boo!")));
final Deferred<Object> obj = Whitebox.invokeMethod(client, "locateRegion",
get, HBaseClient.META, EMPTY_ARRAY);
assertTrue(root_deferred != obj);
assertCounters(1, 0, 0);
assertEquals(2, client2regions.size());
obj.joinUninterruptibly();
}
@Test
public void locateRegionMetaLiveClientNSREdDueToSplit() throws Exception {
Whitebox.setInternalState(client, "rootregion", rootclient);
when(rootclient.isAlive()).thenReturn(true);
final ArrayList<KeyValue> row = new ArrayList<KeyValue>(2);
// Don't know if this is valid to be online and splitting. Prolly is
row.add(metaRegionInfo(EMPTY_ARRAY, EMPTY_ARRAY, false, true, TABLE));
row.add(new KeyValue(meta.name(), INFO, SERVER, "localhost:54321".getBytes()));
when(rootclient.getClosestRowBefore(any(RegionInfo.class), any(byte[].class),
any(byte[].class), any(byte[].class)))
.thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(row));
final Deferred<Object> obj = Whitebox.invokeMethod(client, "locateRegion",
get, HBaseClient.META, EMPTY_ARRAY);
assertTrue(root_deferred != obj);
assertCounters(1, 0, 0);
assertEquals(2, client2regions.size());
assertNull(obj.joinUninterruptibly());
}
@Test
public void locateRegionMetaLiveClientOffline() throws Exception {
Whitebox.setInternalState(client, "rootregion", rootclient);
when(rootclient.isAlive()).thenReturn(true);
final ArrayList<KeyValue> row = new ArrayList<KeyValue>(2);
row.add(metaRegionInfo(EMPTY_ARRAY, EMPTY_ARRAY, true, false, TABLE));
row.add(new KeyValue(meta.name(), INFO, SERVER, "localhost:54321".getBytes()));
when(rootclient.getClosestRowBefore(any(RegionInfo.class), any(byte[].class),
any(byte[].class), any(byte[].class)))
.thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(row))
.thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(metaRow()));
final Deferred<Object> obj = Whitebox.invokeMethod(client, "locateRegion",
get, HBaseClient.META, EMPTY_ARRAY);
assertTrue(root_deferred != obj);
assertCounters(2, 0, 0);
assertEquals(3, client2regions.size());
final RegionClient rc = (RegionClient)obj.joinUninterruptibly();
assertNotNull(client2regions.get(rc));
}
@Test (expected = BrokenMetaException.class)
public void locateRegionMetaLiveClientBrokenMeta() throws Exception {
Whitebox.setInternalState(client, "rootregion", rootclient);
when(rootclient.isAlive()).thenReturn(true);
final ArrayList<KeyValue> row = new ArrayList<KeyValue>(2);
row.add(metaRegionInfo(EMPTY_ARRAY, EMPTY_ARRAY, false, false, TABLE));
row.add(new KeyValue(meta.name(), INFO, SERVER, "localhost:myport".getBytes()));
when(rootclient.getClosestRowBefore(any(RegionInfo.class), any(byte[].class),
any(byte[].class), any(byte[].class)))
.thenReturn(Deferred.<ArrayList<KeyValue>>fromResult(row));
final Deferred<Object> obj = Whitebox.invokeMethod(client, "locateRegion",
get, HBaseClient.META, EMPTY_ARRAY);
assertTrue(root_deferred != obj);
assertCounters(1, 0, 0);
assertEquals(2, client2regions.size());
obj.joinUninterruptibly();
}
// -------------- GENERAL LOOKUP ------------
@Test
public void locateRegionLookupInZK() throws Exception {
final Object obj = Whitebox.invokeMethod(client, "locateRegion",
get, TABLE, EMPTY_ARRAY);
assertTrue(root_deferred == obj);
assertCounters(0, 0, 0);
}
// ---------- PARAMS -----------
@Test (expected = NullPointerException.class)
public void locateRegionNullTable() throws Exception {
Whitebox.invokeMethod(client, "locateRegion", get, (byte[])null, EMPTY_ARRAY);
}
@Test (expected = NullPointerException.class)
public void locateRegionNullKey() throws Exception {
Whitebox.invokeMethod(client, "locateRegion", get, TABLE, (byte[])null);
}
@Test
public void locateRegionEmptyTable() throws Exception {
final Deferred<Object> root_deferred = new Deferred<Object>();
when(zkclient.getDeferredRoot()).thenReturn(root_deferred);
final Object obj = Whitebox.invokeMethod(client, "locateRegion",
get, EMPTY_ARRAY, EMPTY_ARRAY);
assertNotNull(obj);
assertTrue(root_deferred == obj);
}
@Test
public void locateRegionEmptyKey() throws Exception {
final Deferred<Object> root_deferred = new Deferred<Object>();
when(zkclient.getDeferredRoot()).thenReturn(root_deferred);
final Object obj = Whitebox.invokeMethod(client, "locateRegion",
get, TABLE, EMPTY_ARRAY);
assertNotNull(obj);
assertTrue(root_deferred == obj);
}
// This one is OK because we only check the RPC we have an exception when
// getting the closest row.
@Test
public void locateRegionNullRequest() throws Exception {
final Deferred<Object> root_deferred = new Deferred<Object>();
when(zkclient.getDeferredRoot()).thenReturn(root_deferred);
final Object obj = Whitebox.invokeMethod(client, "locateRegion",
(HBaseRpc)null, TABLE, EMPTY_ARRAY);
assertNotNull(obj);
assertTrue(root_deferred == obj);
}
@Test (expected = NullPointerException.class)
public void locateRegionNullRequestNPE() throws Exception {
Whitebox.setInternalState(client, "rootregion", rootclient);
when(rootclient.isAlive()).thenReturn(true);
when(rootclient.getClosestRowBefore(any(RegionInfo.class), any(byte[].class),
any(byte[].class), any(byte[].class)))
.thenAnswer(new Answer<Deferred<ArrayList<KeyValue>>>() {
@Override
public Deferred<ArrayList<KeyValue>> answer(InvocationOnMock invocation)
throws Throwable {
return Deferred.<ArrayList<KeyValue>>fromError(
new RegionOfflineException(EMPTY_ARRAY));
}
});
final Deferred<Object> obj = Whitebox.invokeMethod(client, "locateRegion",
(HBaseRpc)null, TABLE, EMPTY_ARRAY);
obj.join();
}
// ---------- HELPERS -----------
/** Simply sets the root region to the root client and mocks the alive call */
private void setLiveRootClient() {
Whitebox.setInternalState(client, "rootregion", rootclient);
when(rootclient.isAlive()).thenReturn(true);
}
/**
* Helper to check our counters
* @param root_lookups The number of root lookups expected
* @param meta_lookups_with_permit The number of lookups with permits
* @param meta_lookups_wo_permit The number of lookups without permits
*/
private void assertCounters(final int root_lookups,
final int meta_lookups_with_permit, final int meta_lookups_wo_permit) {
assertEquals(root_lookups,
((Counter)Whitebox.getInternalState(client, "root_lookups")).get());
assertEquals(meta_lookups_with_permit,
((Counter)Whitebox.getInternalState(client,
"meta_lookups_with_permit")).get());
assertEquals(meta_lookups_wo_permit,
((Counter)Whitebox.getInternalState(client,
"meta_lookups_wo_permit")).get());
}
}