use of org.neo4j.kernel.ha.cluster.SwitchToSlaveCopyThenBranch in project neo4j by neo4j.
the class HighAvailabilityModeSwitcherTest method shouldUseProperServerIdWhenDemotingFromMasterOnException.
@Test
public void shouldUseProperServerIdWhenDemotingFromMasterOnException() throws Throwable {
/*
* This a test that acts as a driver to prove a bug which had an instance send out a demote message
* with instance id -1, since it used HAMS#getServerId(URI) with a URI coming from the NetworkReceiver binding
* which did not contain the serverId URI argument. This has been fixed by explicitly adding the instanceid
* as a constructor argument of the HAMS.
*/
// Given
SwitchToSlaveCopyThenBranch sts = mock(SwitchToSlaveCopyThenBranch.class);
SwitchToMaster stm = mock(SwitchToMaster.class);
// this is necessary to trigger a revert which uses the serverId from the HAMS#me field
when(stm.switchToMaster(any(LifeSupport.class), any(URI.class))).thenThrow(new RuntimeException());
Election election = mock(Election.class);
ClusterMemberAvailability cma = mock(ClusterMemberAvailability.class);
InstanceId instanceId = new InstanceId(14);
HighAvailabilityModeSwitcher theSwitcher = new HighAvailabilityModeSwitcher(sts, stm, election, cma, mock(ClusterClient.class), storeSupplierMock(), instanceId, new ComponentSwitcherContainer(), neoStoreDataSourceSupplierMock(), NullLogService.getInstance());
theSwitcher.init();
theSwitcher.start();
/*
* This is the trick, kind of. NetworkReceiver creates this and passes it on to NetworkReceiver#getURI() and
* that
* is what HAMS uses as the HAMS#me field value. But we should not be using this to extract the instanceId.
* Note the lack of a serverId argument
*/
URI listeningAt = URI.create("ha://0.0.0.0:5001?name=someName");
theSwitcher.listeningAt(listeningAt);
// When
try {
// the instance fails to switch to master
theSwitcher.masterIsElected(new HighAvailabilityMemberChangeEvent(HighAvailabilityMemberState.PENDING, HighAvailabilityMemberState.TO_MASTER, instanceId, listeningAt));
} finally {
theSwitcher.stop();
theSwitcher.shutdown();
}
// Then
// The demotion message must have used the proper instance id
verify(election).demote(instanceId);
}
use of org.neo4j.kernel.ha.cluster.SwitchToSlaveCopyThenBranch 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.SwitchToSlaveCopyThenBranch in project neo4j by neo4j.
the class HighAvailabilityModeSwitcherTest method shouldTakeNoActionIfSwitchingToSlaveForItselfAsMaster.
@Test
public void shouldTakeNoActionIfSwitchingToSlaveForItselfAsMaster() throws Throwable {
// Given
// A HAMS
SwitchToSlaveCopyThenBranch switchToSlave = mock(SwitchToSlaveCopyThenBranch.class);
AssertableLogProvider logProvider = new AssertableLogProvider();
SimpleLogService logService = new SimpleLogService(NullLogProvider.getInstance(), logProvider);
HighAvailabilityModeSwitcher toTest = new HighAvailabilityModeSwitcher(switchToSlave, mock(SwitchToMaster.class), mock(Election.class), mock(ClusterMemberAvailability.class), mock(ClusterClient.class), storeSupplierMock(), new InstanceId(2), new ComponentSwitcherContainer(), neoStoreDataSourceSupplierMock(), logService);
// That is properly started
toTest.init();
toTest.start();
/*
* This is the URI at which we are registered as server - includes our own id, but we don't necessarily listen
* there
*/
URI serverHaUri = URI.create("ha://server2?serverId=2");
toTest.listeningAt(serverHaUri);
// When
// The HAMS tries to switch to slave for a master that is itself
toTest.masterIsAvailable(new HighAvailabilityMemberChangeEvent(PENDING, TO_SLAVE, new InstanceId(2), serverHaUri));
// Then
// No switching to slave must happen
verifyZeroInteractions(switchToSlave);
// And an error must be logged
logProvider.assertAtLeastOnce(inLog(HighAvailabilityModeSwitcher.class).error("I (ha://server2?serverId=2) tried to switch to " + "slave for myself as master (ha://server2?serverId=2)"));
}
use of org.neo4j.kernel.ha.cluster.SwitchToSlaveCopyThenBranch in project neo4j by neo4j.
the class HighAvailabilityModeSwitcherTest method shouldAllowForcedElectionsAfterModeSwitch.
@Test
public void shouldAllowForcedElectionsAfterModeSwitch() throws Throwable {
// Given
SwitchToSlaveCopyThenBranch switchToSlave = mock(SwitchToSlaveCopyThenBranch.class);
when(switchToSlave.switchToSlave(any(LifeSupport.class), any(URI.class), any(URI.class), any(CancellationRequest.class))).thenReturn(URI.create("http://localhost"));
ClusterMemberAvailability memberAvailability = mock(ClusterMemberAvailability.class);
Election election = mock(Election.class);
final CountDownLatch modeSwitchHappened = new CountDownLatch(1);
HighAvailabilityModeSwitcher modeSwitcher = new HighAvailabilityModeSwitcher(switchToSlave, mock(SwitchToMaster.class), election, memberAvailability, mock(ClusterClient.class), storeSupplierMock(), mock(InstanceId.class), new ComponentSwitcherContainer(), neoStoreDataSourceSupplierMock(), NullLogService.getInstance()) {
@Override
ScheduledExecutorService createExecutor() {
ScheduledExecutorService executor = mock(ScheduledExecutorService.class);
doAnswer(invocation -> {
((Runnable) invocation.getArguments()[0]).run();
modeSwitchHappened.countDown();
return mock(Future.class);
}).when(executor).submit(any(Runnable.class));
return executor;
}
};
modeSwitcher.init();
modeSwitcher.start();
modeSwitcher.forceElections();
reset(memberAvailability, election);
// When
modeSwitcher.masterIsAvailable(new HighAvailabilityMemberChangeEvent(PENDING, TO_SLAVE, mock(InstanceId.class), URI.create("http://localhost:9090?serverId=42")));
modeSwitchHappened.await();
modeSwitcher.forceElections();
// Then
InOrder inOrder = inOrder(memberAvailability, election);
inOrder.verify(memberAvailability).memberIsUnavailable(HighAvailabilityModeSwitcher.SLAVE);
inOrder.verify(election).performRoleElections();
inOrder.verifyNoMoreInteractions();
}
use of org.neo4j.kernel.ha.cluster.SwitchToSlaveCopyThenBranch in project neo4j by neo4j.
the class HighAvailabilityModeSwitcherTest method shouldSwitchToSlaveForNullMasterAndBeSilentWhenMovingToDetached.
@Test
public void shouldSwitchToSlaveForNullMasterAndBeSilentWhenMovingToDetached() throws Throwable {
// Given
SwitchToSlaveCopyThenBranch sts = mock(SwitchToSlaveCopyThenBranch.class);
SwitchToMaster stm = mock(SwitchToMaster.class);
Election election = mock(Election.class);
ClusterMemberAvailability cma = mock(ClusterMemberAvailability.class);
InstanceId instanceId = new InstanceId(14);
ComponentSwitcher componentSwitcher = mock(ComponentSwitcher.class);
HighAvailabilityModeSwitcher theSwitcher = new HighAvailabilityModeSwitcher(sts, stm, election, cma, mock(ClusterClient.class), storeSupplierMock(), instanceId, componentSwitcher, neoStoreDataSourceSupplierMock(), NullLogService.getInstance());
// When
theSwitcher.init();
theSwitcher.start();
theSwitcher.instanceDetached(new HighAvailabilityMemberChangeEvent(HighAvailabilityMemberState.MASTER, HighAvailabilityMemberState.PENDING, null, null));
// Then
verify(componentSwitcher).switchToSlave();
verifyZeroInteractions(cma);
}
Aggregations