RequestHedgingProxyProvider extends
        ConfiguredFailoverProxyProvider<T> { @SuppressWarnings("unchecked") @Override public synchronized ProxyInfo<T> getProxy() { if (successfulProxy != null) { return successfulProxy; } Map<String, ProxyInfo<T>> targetProxyInfos = new HashMap<>(); StringBuilder combinedInfo = new StringBuilder('['); for (int i = 0; i < proxies.size(); i++) { ProxyInfo<T> pInfo = super.getProxy(); incrementProxyIndex(); targetProxyInfos.put(pInfo.proxyInfo, pInfo); combinedInfo.append(pInfo.proxyInfo).append(','); } combinedInfo.append(']'); T wrappedProxy = (T) Proxy.newProxyInstance( RequestHedgingInvocationHandler.class.getClassLoader(), new Class<?>[]{xface}, new RequestHedgingInvocationHandler(targetProxyInfos)); return new ProxyInfo<T>(wrappedProxy, combinedInfo.toString()); } RequestHedgingProxyProvider(
          Configuration conf, URI uri, Class<T> xface); @VisibleForTesting  RequestHedgingProxyProvider(Configuration conf, URI uri,
                              Class<T> xface, ProxyFactory<T> factory); @SuppressWarnings("unchecked") @Override synchronized ProxyInfo<T> getProxy(); @Override synchronized void performFailover(T currentProxy);  }
@Test public void testHedgingWhenOneFails() throws Exception { final NamenodeProtocols goodMock = Mockito.mock(NamenodeProtocols.class); Mockito.when(goodMock.getStats()).thenReturn(new long[] {1}); final NamenodeProtocols badMock = Mockito.mock(NamenodeProtocols.class); Mockito.when(badMock.getStats()).thenThrow(new IOException("Bad mock !!")); RequestHedgingProxyProvider<NamenodeProtocols> provider = new RequestHedgingProxyProvider<>(conf, nnUri, NamenodeProtocols.class, createFactory(goodMock, badMock)); long[] stats = provider.getProxy().proxy.getStats(); Assert.assertTrue(stats.length == 1); Mockito.verify(badMock).getStats(); Mockito.verify(goodMock).getStats(); }
@Test public void testHedgingWhenOneIsSlow() throws Exception { final NamenodeProtocols goodMock = Mockito.mock(NamenodeProtocols.class); Mockito.when(goodMock.getStats()).thenAnswer(new Answer<long[]>() { @Override public long[] answer(InvocationOnMock invocation) throws Throwable { Thread.sleep(1000); return new long[]{1}; } }); final NamenodeProtocols badMock = Mockito.mock(NamenodeProtocols.class); Mockito.when(badMock.getStats()).thenThrow(new IOException("Bad mock !!")); RequestHedgingProxyProvider<NamenodeProtocols> provider = new RequestHedgingProxyProvider<>(conf, nnUri, NamenodeProtocols.class, createFactory(goodMock, badMock)); long[] stats = provider.getProxy().proxy.getStats(); Assert.assertTrue(stats.length == 1); Assert.assertEquals(1, stats[0]); Mockito.verify(badMock).getStats(); Mockito.verify(goodMock).getStats(); }
@Test public void testHedgingWhenBothFail() throws Exception { NamenodeProtocols badMock = Mockito.mock(NamenodeProtocols.class); Mockito.when(badMock.getStats()).thenThrow(new IOException("Bad mock !!")); NamenodeProtocols worseMock = Mockito.mock(NamenodeProtocols.class); Mockito.when(worseMock.getStats()).thenThrow( new IOException("Worse mock !!")); RequestHedgingProxyProvider<NamenodeProtocols> provider = new RequestHedgingProxyProvider<>(conf, nnUri, NamenodeProtocols.class, createFactory(badMock, worseMock)); try { provider.getProxy().proxy.getStats(); Assert.fail("Should fail since both namenodes throw IOException !!"); } catch (Exception e) { Assert.assertTrue(e instanceof MultiException); } Mockito.verify(badMock).getStats(); Mockito.verify(worseMock).getStats(); }