use of org.neo4j.kernel.ha.cluster.HighAvailabilityMemberState.PENDING in project neo4j by neo4j.
the class HighAvailabilityModeSwitcherTest method shouldRecognizeNewMasterIfNewMasterBecameAvailableDuringSwitch.
@Test
public void shouldRecognizeNewMasterIfNewMasterBecameAvailableDuringSwitch() throws Throwable {
// When messages coming in the following ordering, the slave should detect that the master id has changed
// M1: Get masterIsAvailable for instance 1 at PENDING state, changing PENDING -> TO_SLAVE
// M2: Get masterIsAvailable for instance 2 at TO_SLAVE state, changing TO_SLAVE -> TO_SLAVE
// Given
final CountDownLatch firstMasterAvailableHandled = new CountDownLatch(1);
final CountDownLatch secondMasterAvailableComes = new CountDownLatch(1);
final CountDownLatch secondMasterAvailableHandled = new CountDownLatch(1);
SwitchToSlaveCopyThenBranch switchToSlave = mock(SwitchToSlaveCopyThenBranch.class);
HighAvailabilityModeSwitcher modeSwitcher = new HighAvailabilityModeSwitcher(switchToSlave, mock(SwitchToMaster.class), mock(Election.class), mock(ClusterMemberAvailability.class), mock(ClusterClient.class), mock(Supplier.class), new InstanceId(4), new ComponentSwitcherContainer(), neoStoreDataSourceSupplierMock(), NullLogService.getInstance()) {
@Override
ScheduledExecutorService createExecutor() {
final ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
final ExecutorService realExecutor = Executors.newSingleThreadExecutor();
when(executor.submit(any(Runnable.class))).thenAnswer((Answer<Future<?>>) invocation -> realExecutor.submit((Runnable) () -> ((Runnable) invocation.getArguments()[0]).run()));
when(executor.schedule(any(Runnable.class), anyLong(), any(TimeUnit.class))).thenAnswer((Answer<Future<?>>) invocation -> {
realExecutor.submit((Callable<Void>) () -> {
firstMasterAvailableHandled.countDown();
secondMasterAvailableComes.await();
((Runnable) invocation.getArguments()[0]).run();
secondMasterAvailableHandled.countDown();
return null;
});
return mock(ScheduledFuture.class);
});
return executor;
}
};
modeSwitcher.init();
modeSwitcher.start();
modeSwitcher.listeningAt(URI.create("ha://server3?serverId=3"));
// When
// masterIsAvailable for instance 1
URI uri1 = URI.create("ha://server1");
// The first masterIsAvailable should fail so that the slave instance stops at TO_SLAVE state
doThrow(new ComException("Fail to switch to slave and reschedule to retry")).when(switchToSlave).switchToSlave(any(LifeSupport.class), any(URI.class), eq(uri1), any(CancellationRequest.class));
modeSwitcher.masterIsAvailable(new HighAvailabilityMemberChangeEvent(PENDING, TO_SLAVE, new InstanceId(1), uri1));
// wait until the first masterIsAvailable triggers the exception handling
firstMasterAvailableHandled.await();
verify(switchToSlave).switchToSlave(any(LifeSupport.class), any(URI.class), eq(uri1), any(CancellationRequest.class));
// masterIsAvailable for instance 2
URI uri2 = URI.create("ha://server2");
modeSwitcher.masterIsAvailable(new HighAvailabilityMemberChangeEvent(TO_SLAVE, TO_SLAVE, new InstanceId(2), uri2));
secondMasterAvailableComes.countDown();
// wait until switchToSlave method is invoked again
secondMasterAvailableHandled.await();
// Then
// switchToSlave should be retried with new master id
verify(switchToSlave).switchToSlave(any(LifeSupport.class), any(URI.class), eq(uri2), any(CancellationRequest.class));
}
use of org.neo4j.kernel.ha.cluster.HighAvailabilityMemberState.PENDING in project neo4j by neo4j.
the class HighAvailabilityModeSwitcherTest method shouldNotResetAvailableMasterURIIfElectionResultReceived.
@Test
public void shouldNotResetAvailableMasterURIIfElectionResultReceived() throws Throwable {
/*
* It is possible that a masterIsElected nulls out the current available master URI in the HAMS. That can
* be a problem if handing the mIE event is concurrent with an ongoing switch which re-runs because
* the store was incompatible or a log was missing. In such a case it will find a null master URI on
* rerun and it will fail.
*/
// Given
SwitchToSlaveCopyThenBranch switchToSlave = mock(SwitchToSlaveCopyThenBranch.class);
// The fist run through switchToSlave
final CountDownLatch firstCallMade = new CountDownLatch(1);
// The second run through switchToSlave
final CountDownLatch secondCallMade = new CountDownLatch(1);
// The latch for waiting for the masterIsElected to come through
final CountDownLatch waitForSecondMessage = new CountDownLatch(1);
HighAvailabilityModeSwitcher toTest = new HighAvailabilityModeSwitcher(switchToSlave, mock(SwitchToMaster.class), mock(Election.class), mock(ClusterMemberAvailability.class), mock(ClusterClient.class), storeSupplierMock(), new InstanceId(1), new ComponentSwitcherContainer(), neoStoreDataSourceSupplierMock(), NullLogService.getInstance());
URI uri1 = URI.create("ha://server1");
toTest.init();
toTest.start();
toTest.listeningAt(URI.create("ha://server3?serverId=3"));
when(switchToSlave.switchToSlave(any(LifeSupport.class), any(URI.class), any(URI.class), any(CancellationRequest.class))).thenAnswer(invocation -> {
firstCallMade.countDown();
waitForSecondMessage.await();
throw new MismatchingStoreIdException(StoreId.DEFAULT, StoreId.DEFAULT);
}).thenAnswer(invocation -> {
secondCallMade.countDown();
return URI.create("ha://server3");
});
// When
// The first message goes through, start the first run
toTest.masterIsAvailable(new HighAvailabilityMemberChangeEvent(PENDING, TO_SLAVE, new InstanceId(1), uri1));
// Wait for it to be processed but get just before the exception
firstCallMade.await();
// It is just about to throw the exception, i.e. rerun. Send in the event
toTest.masterIsElected(new HighAvailabilityMemberChangeEvent(TO_SLAVE, TO_SLAVE, new InstanceId(1), null));
// Allow to continue and do the second run
waitForSecondMessage.countDown();
// Wait for the call to finish
secondCallMade.await();
// Then
verify(switchToSlave, times(2)).switchToSlave(any(LifeSupport.class), any(URI.class), eq(uri1), any(CancellationRequest.class));
}
Aggregations