use of org.neo4j.kernel.ha.cluster.HighAvailabilityMemberChangeEvent 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));
}
use of org.neo4j.kernel.ha.cluster.HighAvailabilityMemberChangeEvent in project neo4j by neo4j.
the class HighAvailabilityModeSwitcherTest method shouldReswitchToSlaveIfNewMasterBecameElectedAndAvailableDuringSwitch.
@Test
public void shouldReswitchToSlaveIfNewMasterBecameElectedAndAvailableDuringSwitch() throws Throwable {
// Given
final CountDownLatch switching = new CountDownLatch(1);
final CountDownLatch slaveAvailable = new CountDownLatch(2);
final AtomicBoolean firstSwitch = new AtomicBoolean(true);
ClusterMemberAvailability availability = mock(ClusterMemberAvailability.class);
SwitchToSlaveCopyThenBranch switchToSlave = mock(SwitchToSlaveCopyThenBranch.class);
@SuppressWarnings("resource") SwitchToMaster switchToMaster = mock(SwitchToMaster.class);
when(switchToSlave.switchToSlave(any(LifeSupport.class), any(URI.class), any(URI.class), any(CancellationRequest.class))).thenAnswer(invocationOnMock -> {
switching.countDown();
CancellationRequest cancel = (CancellationRequest) invocationOnMock.getArguments()[3];
if (firstSwitch.get()) {
while (!cancel.cancellationRequested()) {
Thread.sleep(1);
}
firstSwitch.set(false);
}
slaveAvailable.countDown();
return URI.create("ha://slave");
});
HighAvailabilityModeSwitcher toTest = new HighAvailabilityModeSwitcher(switchToSlave, switchToMaster, mock(Election.class), availability, mock(ClusterClient.class), storeSupplierMock(), mock(InstanceId.class), new ComponentSwitcherContainer(), neoStoreDataSourceSupplierMock(), NullLogService.getInstance());
toTest.init();
toTest.start();
toTest.listeningAt(URI.create("ha://server3?serverId=3"));
// When
// This will start a switch to slave
toTest.masterIsAvailable(new HighAvailabilityMemberChangeEvent(PENDING, TO_SLAVE, mock(InstanceId.class), URI.create("ha://server1")));
// Wait until it starts and blocks on the cancellation request
switching.await();
// change the elected master, moving to pending, cancelling the previous change. This will block until the
// previous switch is aborted
toTest.masterIsElected(new HighAvailabilityMemberChangeEvent(TO_SLAVE, PENDING, new InstanceId(2), URI.create("ha://server2")));
// Now move to the new master by switching to TO_SLAVE
toTest.masterIsAvailable(new HighAvailabilityMemberChangeEvent(PENDING, TO_SLAVE, new InstanceId(2), URI.create("ha://server2")));
// Then
// The second switch must happen and this test won't block
slaveAvailable.await();
}
use of org.neo4j.kernel.ha.cluster.HighAvailabilityMemberChangeEvent in project neo4j by neo4j.
the class HighlyAvailableEditionModule method registerRecovery.
private void registerRecovery(final DatabaseInfo databaseInfo, final DependencyResolver dependencyResolver, final LogService logging) {
memberStateMachine.addHighAvailabilityMemberListener(new HighAvailabilityMemberListener.Adapter() {
@Override
public void masterIsAvailable(HighAvailabilityMemberChangeEvent event) {
if (event.getOldState().equals(HighAvailabilityMemberState.TO_MASTER) && event.getNewState().equals(HighAvailabilityMemberState.MASTER)) {
doAfterRecoveryAndStartup(true);
}
}
@Override
public void slaveIsAvailable(HighAvailabilityMemberChangeEvent event) {
if (event.getOldState().equals(HighAvailabilityMemberState.TO_SLAVE) && event.getNewState().equals(HighAvailabilityMemberState.SLAVE)) {
doAfterRecoveryAndStartup(false);
}
}
private void doAfterRecoveryAndStartup(boolean isMaster) {
try {
HighlyAvailableEditionModule.this.doAfterRecoveryAndStartup(databaseInfo, dependencyResolver, isMaster);
} catch (Throwable throwable) {
Log messagesLog = logging.getInternalLog(EnterpriseEditionModule.class);
messagesLog.error("Post recovery error", throwable);
try {
memberStateMachine.stop();
} catch (Throwable throwable1) {
messagesLog.warn("Could not stop", throwable1);
}
try {
memberStateMachine.start();
} catch (Throwable throwable1) {
messagesLog.warn("Could not start", throwable1);
}
}
}
});
}
Aggregations