@Test public void testFailoverOnNetworkExceptionIdempotentOperation() throws UnreliableException, IOException, StandbyException { UnreliableInterface unreliable = (UnreliableInterface)RetryProxy.create( UnreliableInterface.class, newFlipFlopProxyProvider( TypeOfExceptionToFailWith.IO_EXCEPTION, TypeOfExceptionToFailWith.UNRELIABLE_EXCEPTION), RetryPolicies.failoverOnNetworkException(1)); assertEquals("impl1", unreliable.succeedsOnceThenFailsReturningString()); try { unreliable.succeedsOnceThenFailsReturningString(); fail("should not have succeeded twice"); } catch (IOException e) { // Make sure we *don't* fail over since the first implementation threw an // IOException and this method is not idempotent assertEquals("impl1", e.getMessage()); } assertEquals("impl1", unreliable.succeedsOnceThenFailsReturningStringIdempotent()); // Make sure we fail over since the first implementation threw an // IOException and this method is idempotent. assertEquals("impl2", unreliable.succeedsOnceThenFailsReturningStringIdempotent()); }
/** * Test that if a non-idempotent void function is called, and there is an exception, * the exception is properly propagated */ @Test public void testExceptionPropagatedForNonIdempotentVoid() throws Exception { UnreliableInterface unreliable = (UnreliableInterface)RetryProxy .create(UnreliableInterface.class, newFlipFlopProxyProvider( TypeOfExceptionToFailWith.IO_EXCEPTION, TypeOfExceptionToFailWith.UNRELIABLE_EXCEPTION), RetryPolicies.failoverOnNetworkException(1)); try { unreliable.nonIdempotentVoidFailsIfIdentifierDoesntMatch("impl2"); fail("did not throw an exception"); } catch (Exception e) { } }
/** * Ensure that normal IO exceptions don't result in a failover. */ @Test public void testExpectedIOException() { UnreliableInterface unreliable = (UnreliableInterface)RetryProxy.create( UnreliableInterface.class, newFlipFlopProxyProvider( TypeOfExceptionToFailWith.REMOTE_EXCEPTION, TypeOfExceptionToFailWith.UNRELIABLE_EXCEPTION), RetryPolicies.failoverOnNetworkException( RetryPolicies.TRY_ONCE_THEN_FAIL, 10, 1000, 10000)); try { unreliable.failsIfIdentifierDoesntMatch("no-such-identifier"); fail("Should have thrown *some* exception"); } catch (Exception e) { assertTrue("Expected IOE but got " + e.getClass(), e instanceof IOException); } }
@Test public void testFailoverOnNetworkExceptionIdempotentOperation() throws UnreliableException, IOException, StandbyException { UnreliableInterface unreliable = (UnreliableInterface)RetryProxy .create(UnreliableInterface.class, new FlipFlopProxyProvider(UnreliableInterface.class, new UnreliableImplementation("impl1", TypeOfExceptionToFailWith.IO_EXCEPTION), new UnreliableImplementation("impl2", TypeOfExceptionToFailWith.UNRELIABLE_EXCEPTION)), RetryPolicies.failoverOnNetworkException(1)); assertEquals("impl1", unreliable.succeedsOnceThenFailsReturningString()); try { unreliable.succeedsOnceThenFailsReturningString(); fail("should not have succeeded twice"); } catch (IOException e) { // Make sure we *don't* fail over since the first implementation threw an // IOException and this method is not idempotent assertEquals("impl1", e.getMessage()); } assertEquals("impl1", unreliable.succeedsOnceThenFailsReturningStringIdempotent()); // Make sure we fail over since the first implementation threw an // IOException and this method is idempotent. assertEquals("impl2", unreliable.succeedsOnceThenFailsReturningStringIdempotent()); }
/** * Test that if a non-idempotent void function is called, and there is an exception, * the exception is properly propagated */ @Test public void testExceptionPropagatedForNonIdempotentVoid() throws Exception { UnreliableInterface unreliable = (UnreliableInterface)RetryProxy .create(UnreliableInterface.class, new FlipFlopProxyProvider(UnreliableInterface.class, new UnreliableImplementation("impl1", TypeOfExceptionToFailWith.IO_EXCEPTION), new UnreliableImplementation("impl2", TypeOfExceptionToFailWith.UNRELIABLE_EXCEPTION)), RetryPolicies.failoverOnNetworkException(1)); try { unreliable.nonIdempotentVoidFailsIfIdentifierDoesntMatch("impl2"); fail("did not throw an exception"); } catch (Exception e) { } }
/** * Test that concurrent failed method invocations only result in a single * failover. */ @Test public void testConcurrentMethodFailures() throws InterruptedException { FlipFlopProxyProvider proxyProvider = new FlipFlopProxyProvider( UnreliableInterface.class, new SynchronizedUnreliableImplementation("impl1", TypeOfExceptionToFailWith.STANDBY_EXCEPTION, 2), new UnreliableImplementation("impl2", TypeOfExceptionToFailWith.STANDBY_EXCEPTION)); final UnreliableInterface unreliable = (UnreliableInterface)RetryProxy .create(UnreliableInterface.class, proxyProvider, RetryPolicies.failoverOnNetworkException(10)); ConcurrentMethodThread t1 = new ConcurrentMethodThread(unreliable); ConcurrentMethodThread t2 = new ConcurrentMethodThread(unreliable); t1.start(); t2.start(); t1.join(); t2.join(); assertEquals("impl2", t1.result); assertEquals("impl2", t2.result); assertEquals(1, proxyProvider.getFailoversOccurred()); }
/** * Ensure that normal IO exceptions don't result in a failover. */ @Test public void testExpectedIOException() { UnreliableInterface unreliable = (UnreliableInterface)RetryProxy .create(UnreliableInterface.class, new FlipFlopProxyProvider(UnreliableInterface.class, new UnreliableImplementation("impl1", TypeOfExceptionToFailWith.REMOTE_EXCEPTION), new UnreliableImplementation("impl2", TypeOfExceptionToFailWith.UNRELIABLE_EXCEPTION)), RetryPolicies.failoverOnNetworkException( RetryPolicies.TRY_ONCE_THEN_FAIL, 10, 1000, 10000)); try { unreliable.failsIfIdentifierDoesntMatch("no-such-identifier"); fail("Should have thrown *some* exception"); } catch (Exception e) { assertTrue("Expected IOE but got " + e.getClass(), e instanceof IOException); } }
private static FlipFlopProxyProvider<UnreliableInterface> newFlipFlopProxyProvider(TypeOfExceptionToFailWith t1, TypeOfExceptionToFailWith t2) { return new FlipFlopProxyProvider<UnreliableInterface>( UnreliableInterface.class, new UnreliableImplementation("impl1", t1), new UnreliableImplementation("impl2", t2)); }
@Test public void testFailoverOnStandbyException() throws UnreliableException, IOException, StandbyException { UnreliableInterface unreliable = (UnreliableInterface)RetryProxy.create( UnreliableInterface.class, newFlipFlopProxyProvider(), RetryPolicies.failoverOnNetworkException(1)); assertEquals("impl1", unreliable.succeedsOnceThenFailsReturningString()); try { unreliable.succeedsOnceThenFailsReturningString(); fail("should not have succeeded twice"); } catch (UnreliableException e) { // Make sure there was no failover on normal exception. assertEquals("impl1", e.getMessage()); } unreliable = (UnreliableInterface)RetryProxy .create(UnreliableInterface.class, newFlipFlopProxyProvider( TypeOfExceptionToFailWith.STANDBY_EXCEPTION, TypeOfExceptionToFailWith.UNRELIABLE_EXCEPTION), RetryPolicies.failoverOnNetworkException(1)); assertEquals("impl1", unreliable.succeedsOnceThenFailsReturningString()); // Make sure we fail over since the first implementation threw a StandbyException assertEquals("impl2", unreliable.succeedsOnceThenFailsReturningString()); }
/** * Test that concurrent failed method invocations only result in a single * failover. */ @Test public void testConcurrentMethodFailures() throws InterruptedException { FlipFlopProxyProvider<UnreliableInterface> proxyProvider = new FlipFlopProxyProvider<UnreliableInterface>( UnreliableInterface.class, new SynchronizedUnreliableImplementation("impl1", TypeOfExceptionToFailWith.STANDBY_EXCEPTION, 2), new UnreliableImplementation("impl2", TypeOfExceptionToFailWith.STANDBY_EXCEPTION)); final UnreliableInterface unreliable = (UnreliableInterface)RetryProxy .create(UnreliableInterface.class, proxyProvider, RetryPolicies.failoverOnNetworkException(10)); ConcurrentMethodThread t1 = new ConcurrentMethodThread(unreliable); ConcurrentMethodThread t2 = new ConcurrentMethodThread(unreliable); t1.start(); t2.start(); t1.join(); t2.join(); assertEquals("impl2", t1.result); assertEquals("impl2", t2.result); assertEquals(1, proxyProvider.getFailoversOccurred()); }
/** * Ensure that when all configured services are throwing StandbyException * that we fail over back and forth between them until one is no longer * throwing StandbyException. */ @Test public void testFailoverBetweenMultipleStandbys() throws UnreliableException, StandbyException, IOException { final long millisToSleep = 10000; final UnreliableImplementation impl1 = new UnreliableImplementation("impl1", TypeOfExceptionToFailWith.STANDBY_EXCEPTION); FlipFlopProxyProvider<UnreliableInterface> proxyProvider = new FlipFlopProxyProvider<UnreliableInterface>( UnreliableInterface.class, impl1, new UnreliableImplementation("impl2", TypeOfExceptionToFailWith.STANDBY_EXCEPTION)); final UnreliableInterface unreliable = (UnreliableInterface)RetryProxy .create(UnreliableInterface.class, proxyProvider, RetryPolicies.failoverOnNetworkException( RetryPolicies.TRY_ONCE_THEN_FAIL, 10, 1000, 10000)); new Thread() { @Override public void run() { ThreadUtil.sleepAtLeastIgnoreInterrupts(millisToSleep); impl1.setIdentifier("renamed-impl1"); } }.start(); String result = unreliable.failsIfIdentifierDoesntMatch("renamed-impl1"); assertEquals("renamed-impl1", result); }