diff --git a/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/catchup/tx/CatchupPollingProcess.java b/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/catchup/tx/CatchupPollingProcess.java index 609feba9b2655..b0b3a15e695f8 100644 --- a/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/catchup/tx/CatchupPollingProcess.java +++ b/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/catchup/tx/CatchupPollingProcess.java @@ -37,6 +37,7 @@ import org.neo4j.causalclustering.core.consensus.schedule.TimerService.TimerName; import org.neo4j.causalclustering.core.state.snapshot.TopologyLookupException; import org.neo4j.causalclustering.discovery.TopologyService; +import org.neo4j.causalclustering.helper.Enableable; import org.neo4j.causalclustering.identity.MemberId; import org.neo4j.causalclustering.identity.StoreId; import org.neo4j.causalclustering.upstream.UpstreamDatabaseSelectionException; @@ -44,7 +45,6 @@ import org.neo4j.helpers.AdvertisedSocketAddress; import org.neo4j.kernel.impl.transaction.CommittedTransactionRepresentation; import org.neo4j.kernel.internal.DatabaseHealth; -import org.neo4j.kernel.lifecycle.Lifecycle; import org.neo4j.kernel.lifecycle.LifecycleAdapter; import org.neo4j.kernel.monitoring.Monitors; import org.neo4j.logging.Log; @@ -86,7 +86,7 @@ enum State private final LocalDatabase localDatabase; private final Log log; - private final Lifecycle startStopOnStoreCopy; + private final Enableable enableDisableOnStoreCopy; private final StoreCopyProcess storeCopyProcess; private final Supplier databaseHealthSupplier; private final CatchUpClient catchUpClient; @@ -103,14 +103,15 @@ enum State private CompletableFuture upToDateFuture; // we are up-to-date when we are successfully pulling private volatile long latestTxIdOfUpStream; - public CatchupPollingProcess( LogProvider logProvider, LocalDatabase localDatabase, Lifecycle startStopOnStoreCopy, CatchUpClient catchUpClient, - UpstreamDatabaseStrategySelector selectionStrategy, TimerService timerService, long txPullIntervalMillis, BatchingTxApplier applier, - Monitors monitors, StoreCopyProcess storeCopyProcess, Supplier databaseHealthSupplier, TopologyService topologyService ) + public CatchupPollingProcess( LogProvider logProvider, LocalDatabase localDatabase, Enableable enableDisableOnSoreCopy, CatchUpClient catchUpClient, + UpstreamDatabaseStrategySelector selectionStrategy, TimerService timerService, long txPullIntervalMillis, + BatchingTxApplier applier, Monitors monitors, StoreCopyProcess storeCopyProcess, + Supplier databaseHealthSupplier, TopologyService topologyService ) { this.localDatabase = localDatabase; this.log = logProvider.getLog( getClass() ); - this.startStopOnStoreCopy = startStopOnStoreCopy; + this.enableDisableOnStoreCopy = enableDisableOnSoreCopy; this.catchUpClient = catchUpClient; this.selectionStrategyPipeline = selectionStrategy; this.timerService = timerService; @@ -312,7 +313,7 @@ private void downloadDatabase( StoreId localStoreId ) try { localDatabase.stopForStoreCopy(); - startStopOnStoreCopy.stop(); + enableDisableOnStoreCopy.disable(); } catch ( Throwable throwable ) { @@ -339,7 +340,7 @@ private void downloadDatabase( StoreId localStoreId ) try { localDatabase.start(); - startStopOnStoreCopy.start(); + enableDisableOnStoreCopy.enable(); } catch ( Throwable throwable ) { diff --git a/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/core/server/CoreServerModule.java b/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/core/server/CoreServerModule.java index 9883e06a43e57..637c9b808572d 100644 --- a/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/core/server/CoreServerModule.java +++ b/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/core/server/CoreServerModule.java @@ -24,7 +24,6 @@ import java.util.Collection; import java.util.Optional; import java.util.concurrent.TimeUnit; -import java.util.function.Function; import java.util.function.Supplier; import org.neo4j.causalclustering.ReplicationModule; @@ -33,7 +32,6 @@ import org.neo4j.causalclustering.catchup.CatchupProtocolServerInstaller; import org.neo4j.causalclustering.catchup.CatchupServerBuilder; import org.neo4j.causalclustering.catchup.CatchupServerHandler; -import org.neo4j.causalclustering.catchup.CatchupServerProtocol; import org.neo4j.causalclustering.catchup.CheckpointerSupplier; import org.neo4j.causalclustering.catchup.RegularCatchupServerHandler; import org.neo4j.causalclustering.catchup.storecopy.CommitStateHelper; @@ -65,8 +63,10 @@ import org.neo4j.causalclustering.core.state.snapshot.CoreStateDownloaderService; import org.neo4j.causalclustering.core.state.storage.DurableStateStorage; import org.neo4j.causalclustering.core.state.storage.StateStorage; +import org.neo4j.causalclustering.helper.CompositeEnableable; import org.neo4j.causalclustering.helper.ExponentialBackoffStrategy; import org.neo4j.causalclustering.messaging.LifecycleMessageHandler; +import org.neo4j.causalclustering.helper.Enableable; import org.neo4j.causalclustering.net.InstalledProtocolHandler; import org.neo4j.causalclustering.net.Server; import org.neo4j.causalclustering.protocol.ModifierProtocolInstaller; @@ -147,7 +147,7 @@ public CoreServerModule( IdentityModule identityModule, final PlatformModule pla this.logProvider = logging.getInternalLogProvider(); LogProvider userLogProvider = logging.getUserLogProvider(); - LifeSupport servicesToStopOnStoreCopy = new LifeSupport(); + CompositeEnableable servicesToStopOnStoreCopy = new CompositeEnableable(); StateStorage lastFlushedStorage = platformModule.life.add( new DurableStateStorage<>( platformModule.fileSystem, clusterStateDirectory, LAST_FLUSHED_NAME, new LongIndexMarshal(), @@ -251,7 +251,7 @@ private CatchUpClient createCatchupClient( NettyPipelineBuilderFactory clientPip return catchUpClient; } - private CoreStateDownloader createCoreStateDownloader( LifeSupport servicesToStopOnStoreCopy, CatchUpClient catchUpClient ) + private CoreStateDownloader createCoreStateDownloader( Enableable servicesToStopOnStoreCopy, CatchUpClient catchUpClient ) { ExponentialBackoffStrategy storeCopyBackoffStrategy = new ExponentialBackoffStrategy( 1, config.get( CausalClusteringSettings.store_copy_backoff_max_wait ).toMillis(), TimeUnit.MILLISECONDS ); diff --git a/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/core/state/snapshot/CoreStateDownloader.java b/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/core/state/snapshot/CoreStateDownloader.java index ea19ef33dfb7b..328f8571d4d64 100644 --- a/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/core/state/snapshot/CoreStateDownloader.java +++ b/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/core/state/snapshot/CoreStateDownloader.java @@ -37,9 +37,9 @@ import org.neo4j.causalclustering.catchup.storecopy.StoreIdDownloadFailedException; import org.neo4j.causalclustering.core.state.CoreSnapshotService; import org.neo4j.causalclustering.core.state.machines.CoreStateMachines; +import org.neo4j.causalclustering.helper.Enableable; import org.neo4j.causalclustering.identity.StoreId; import org.neo4j.helpers.AdvertisedSocketAddress; -import org.neo4j.kernel.lifecycle.Lifecycle; import org.neo4j.kernel.lifecycle.LifecycleException; import org.neo4j.logging.Log; import org.neo4j.logging.LogProvider; @@ -51,7 +51,7 @@ public class CoreStateDownloader { private final LocalDatabase localDatabase; - private final Lifecycle startStopOnStoreCopy; + private final Enableable enableDisableOnStoreCopy; private final RemoteStore remoteStore; private final CatchUpClient catchUpClient; private final Log log; @@ -60,13 +60,13 @@ public class CoreStateDownloader private final CoreSnapshotService snapshotService; private CommitStateHelper commitStateHelper; - public CoreStateDownloader( LocalDatabase localDatabase, Lifecycle startStopOnStoreCopy, RemoteStore remoteStore, - CatchUpClient catchUpClient, LogProvider logProvider, StoreCopyProcess storeCopyProcess, - CoreStateMachines coreStateMachines, CoreSnapshotService snapshotService, - CommitStateHelper commitStateHelper ) + public CoreStateDownloader( LocalDatabase localDatabase, Enableable enableDisableOnStoreCopy, RemoteStore remoteStore, + CatchUpClient catchUpClient, LogProvider logProvider, StoreCopyProcess storeCopyProcess, + CoreStateMachines coreStateMachines, CoreSnapshotService snapshotService, + CommitStateHelper commitStateHelper ) { this.localDatabase = localDatabase; - this.startStopOnStoreCopy = startStopOnStoreCopy; + this.enableDisableOnStoreCopy = enableDisableOnStoreCopy; this.remoteStore = remoteStore; this.catchUpClient = catchUpClient; this.log = logProvider.getLog( getClass() ); @@ -129,7 +129,7 @@ boolean downloadSnapshot( CatchupAddressProvider addressProvider ) return false; } - ensure( startStopOnStoreCopy::stop, "stop auxiliary services before store copy" ); + ensure( enableDisableOnStoreCopy::disable, "disable auxiliary services before store copy" ); ensure( localDatabase::stopForStoreCopy, "stop local database for store copy" ); log.info( "Downloading snapshot from core server at %s", primary ); @@ -211,7 +211,7 @@ else if ( catchupResult != SUCCESS_END_OF_STREAM ) ensure( localDatabase::start, "start local database after store copy" ); coreStateMachines.installCommitProcess( localDatabase.getCommitProcess() ); - ensure( startStopOnStoreCopy::start, "start auxiliary services after store copy" ); + ensure( enableDisableOnStoreCopy::enable, "start auxiliary services after store copy" ); return true; } diff --git a/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/helper/CompositeEnableable.java b/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/helper/CompositeEnableable.java new file mode 100644 index 0000000000000..232716e7d5f87 --- /dev/null +++ b/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/helper/CompositeEnableable.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2002-2018 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.neo4j.causalclustering.helper; + +import java.util.ArrayList; +import java.util.List; + +public class CompositeEnableable implements Enableable +{ + private final List enableables = new ArrayList<>(); + + public void add( Enableable enableable ) + { + enableables.add( enableable ); + } + + @Override + public void enable() + { + enableables.forEach( Enableable::enable ); + } + + @Override + public void disable() + { + enableables.forEach( Enableable::disable ); + } +} diff --git a/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/helper/Enableable.java b/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/helper/Enableable.java new file mode 100644 index 0000000000000..f4bf5d354151f --- /dev/null +++ b/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/helper/Enableable.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2002-2018 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.neo4j.causalclustering.helper; + +public interface Enableable +{ + void enable(); + + void disable(); +} diff --git a/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/net/Server.java b/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/net/Server.java index d826186276c80..31576f49c4bc8 100644 --- a/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/net/Server.java +++ b/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/net/Server.java @@ -30,6 +30,7 @@ import java.net.BindException; import java.util.concurrent.TimeUnit; +import org.neo4j.causalclustering.helper.Enableable; import org.neo4j.helpers.ListenSocketAddress; import org.neo4j.helpers.NamedThreadFactory; import org.neo4j.kernel.lifecycle.LifecycleAdapter; @@ -37,7 +38,7 @@ import org.neo4j.logging.LogProvider; import org.neo4j.logging.NullLogProvider; -public class Server extends LifecycleAdapter +public class Server extends LifecycleAdapter implements Enableable { private final Log debugLog; private final Log userLog; @@ -50,15 +51,17 @@ public class Server extends LifecycleAdapter private EventLoopGroup workerGroup; private Channel channel; + private boolean enabled = true; + private boolean stoppedByLifeCycle = true; public Server( ChildInitializer childInitializer, LogProvider debugLogProvider, LogProvider userLogProvider, ListenSocketAddress listenAddress, - String serverName ) + String serverName ) { this( childInitializer, null, debugLogProvider, userLogProvider, listenAddress, serverName ); } public Server( ChildInitializer childInitializer, ChannelInboundHandler parentHandler, LogProvider debugLogProvider, LogProvider userLogProvider, - ListenSocketAddress listenAddress, String serverName ) + ListenSocketAddress listenAddress, String serverName ) { this.childInitializer = childInitializer; this.parentHandler = parentHandler; @@ -76,6 +79,32 @@ public Server( ChildInitializer childInitializer, ListenSocketAddress listenAddr @Override public synchronized void start() + { + stoppedByLifeCycle = false; + if ( !enabled ) + { + debugLog.info( "Start call from lifecycle is ignored because server is disabled." ); + } + else + { + doStart(); + } + } + + @Override + public void stop() + { + stoppedByLifeCycle = true; + doStop(); + } + + @Override + public void shutdown() + { + stoppedByLifeCycle = true; + } + + private void doStart() { if ( channel != null ) { @@ -84,8 +113,7 @@ public synchronized void start() workerGroup = new NioEventLoopGroup( 0, threadFactory ); - ServerBootstrap bootstrap = new ServerBootstrap() - .group( workerGroup ) + ServerBootstrap bootstrap = new ServerBootstrap().group( workerGroup ) .channel( NioServerSocketChannel.class ) .option( ChannelOption.SO_REUSEADDR, Boolean.TRUE ) .localAddress( listenAddress.socketAddress() ) @@ -114,8 +142,12 @@ public synchronized void start() } } - @Override - public synchronized void stop() + public boolean isRunnig() + { + return channel != null; + } + + private void doStop() { if ( channel == null ) { @@ -145,4 +177,25 @@ public ListenSocketAddress address() { return listenAddress; } + + @Override + public synchronized void enable() + { + enabled = true; + if ( !stoppedByLifeCycle ) + { + doStart(); + } + else + { + debugLog.info( "Server will not start. It was enabled but is stopped by lifecycle" ); + } + } + + @Override + public synchronized void disable() + { + enabled = false; + doStop(); + } } diff --git a/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/readreplica/EnterpriseReadReplicaEditionModule.java b/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/readreplica/EnterpriseReadReplicaEditionModule.java index fdb7c514c6d15..82df705dff590 100644 --- a/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/readreplica/EnterpriseReadReplicaEditionModule.java +++ b/enterprise/causal-clustering/src/main/java/org/neo4j/causalclustering/readreplica/EnterpriseReadReplicaEditionModule.java @@ -61,6 +61,7 @@ import org.neo4j.causalclustering.handlers.DuplexPipelineWrapperFactory; import org.neo4j.causalclustering.handlers.PipelineWrapper; import org.neo4j.causalclustering.handlers.VoidPipelineWrapperFactory; +import org.neo4j.causalclustering.helper.CompositeEnableable; import org.neo4j.causalclustering.helper.ExponentialBackoffStrategy; import org.neo4j.causalclustering.identity.MemberId; import org.neo4j.causalclustering.net.InstalledProtocolHandler; @@ -285,7 +286,7 @@ public EnterpriseReadReplicaEditionModule( final PlatformModule platformModule, txPulling.add( copiedStoreRecovery ); - LifeSupport servicesToStopOnStoreCopy = new LifeSupport(); + CompositeEnableable servicesToStopOnStoreCopy = new CompositeEnableable(); StoreCopyProcess storeCopyProcess = new StoreCopyProcess( fileSystem, pageCache, localDatabase, copiedStoreRecovery, remoteStore, logProvider ); diff --git a/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/catchup/tx/CatchupPollingProcessTest.java b/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/catchup/tx/CatchupPollingProcessTest.java index 35e140b29411c..fbfa44875f554 100644 --- a/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/catchup/tx/CatchupPollingProcessTest.java +++ b/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/catchup/tx/CatchupPollingProcessTest.java @@ -36,6 +36,8 @@ import org.neo4j.causalclustering.core.consensus.schedule.CountingTimerService; import org.neo4j.causalclustering.core.consensus.schedule.Timer; import org.neo4j.causalclustering.discovery.TopologyService; +import org.neo4j.causalclustering.helper.CompositeEnableable; +import org.neo4j.causalclustering.helper.Enableable; import org.neo4j.causalclustering.identity.MemberId; import org.neo4j.causalclustering.identity.StoreId; import org.neo4j.causalclustering.upstream.UpstreamDatabaseStrategySelector; @@ -89,7 +91,7 @@ public class CatchupPollingProcessTest when( topologyService.findCatchupAddress( coreMemberId ) ).thenReturn( Optional.of( coreMemberAddress ) ); } - private final Lifecycle startStopOnStoreCopy = mock( Lifecycle.class ); + private final Enableable startStopOnStoreCopy = mock( Enableable.class ); private final CatchupPollingProcess txPuller = new CatchupPollingProcess( NullLogProvider.getInstance(), localDatabase, startStopOnStoreCopy, catchUpClient, strategyPipeline, timerService, @@ -181,10 +183,10 @@ public void nextStateShouldBeTxPullingAfterASuccessfulStoreCopy() throws Throwab // then verify( localDatabase ).stopForStoreCopy(); - verify( startStopOnStoreCopy ).stop(); + verify( startStopOnStoreCopy ).disable(); verify( storeCopyProcess ).replaceWithStoreFrom( any( CatchupAddressProvider.class ), eq( storeId ) ); verify( localDatabase ).start(); - verify( startStopOnStoreCopy ).start(); + verify( startStopOnStoreCopy ).enable(); verify( txApplier ).refreshFromNewStore(); // then diff --git a/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/core/state/snapshot/CoreStateDownloaderTest.java b/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/core/state/snapshot/CoreStateDownloaderTest.java index f4c73ce07f29d..07ef54cedae98 100644 --- a/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/core/state/snapshot/CoreStateDownloaderTest.java +++ b/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/core/state/snapshot/CoreStateDownloaderTest.java @@ -35,10 +35,10 @@ import org.neo4j.causalclustering.core.state.CoreSnapshotService; import org.neo4j.causalclustering.core.state.machines.CoreStateMachines; import org.neo4j.causalclustering.discovery.TopologyService; +import org.neo4j.causalclustering.helper.Enableable; import org.neo4j.causalclustering.identity.MemberId; import org.neo4j.causalclustering.identity.StoreId; import org.neo4j.helpers.AdvertisedSocketAddress; -import org.neo4j.kernel.lifecycle.Lifecycle; import org.neo4j.logging.NullLogProvider; import static org.junit.Assert.assertFalse; @@ -54,7 +54,7 @@ public class CoreStateDownloaderTest { private final LocalDatabase localDatabase = mock( LocalDatabase.class ); - private final Lifecycle startStopLife = mock( Lifecycle.class ); + private final Enableable startStopLife = mock( Enableable.class ); private final RemoteStore remoteStore = mock( RemoteStore.class ); private final CatchUpClient catchUpClient = mock( CatchUpClient.class ); private final StoreCopyProcess storeCopyProcess = mock( StoreCopyProcess.class ); @@ -109,10 +109,10 @@ public void shouldStopDatabaseDuringDownload() throws Throwable downloader.downloadSnapshot( catchupAddressProvider ); // then - verify( startStopLife ).stop(); + verify( startStopLife ).disable(); verify( localDatabase ).stopForStoreCopy(); verify( localDatabase ).start(); - verify( startStopLife ).start(); + verify( startStopLife ).enable(); } @Test diff --git a/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/net/ServeLifeCycleStateChangeTest.java b/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/net/ServeLifeCycleStateChangeTest.java new file mode 100644 index 0000000000000..d13140e158d46 --- /dev/null +++ b/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/net/ServeLifeCycleStateChangeTest.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2002-2018 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.neo4j.causalclustering.net; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.List; + +import org.neo4j.causalclustering.net.ServerStateTestHelpers.EnableableState; +import org.neo4j.causalclustering.net.ServerStateTestHelpers.LifeCycleState; + +import static org.junit.Assert.assertEquals; +import static org.neo4j.causalclustering.net.ServerStateTestHelpers.LifeCycleState.values; +import static org.neo4j.causalclustering.net.ServerStateTestHelpers.createServer; +import static org.neo4j.causalclustering.net.ServerStateTestHelpers.setEnableableState; +import static org.neo4j.causalclustering.net.ServerStateTestHelpers.setInitialState; + +@RunWith( Parameterized.class ) +public class ServeLifeCycleStateChangeTest +{ + @Parameterized.Parameter() + public LifeCycleState fromState; + + @Parameterized.Parameter( 1 ) + public EnableableState fromEnableableState; + + @Parameterized.Parameter( 2 ) + public LifeCycleState toLifeCycleState; + + @Parameterized.Parameter( 3 ) + public boolean shouldBeRunning; + + @Parameterized.Parameters( name = "From {0} and {1} to {2} Server is running ? {3}" ) + public static Iterable data() + { + List params = new ArrayList<>(); + for ( LifeCycleState lifeCycleState : values() ) + { + for ( EnableableState enableableState : EnableableState.values() ) + { + for ( LifeCycleState toState : lifeCycleOperation() ) + { + params.add( new Object[]{lifeCycleState, enableableState, toState, expectedResult( enableableState, toState )} ); + } + } + } + return params; + } + + private Server server; + + private static LifeCycleState[] lifeCycleOperation() + { + return new LifeCycleState[]{LifeCycleState.Start, LifeCycleState.Stop}; + } + + @Before + public void setUpServer() throws Throwable + { + server = createServer(); + setInitialState( server, fromState ); + setEnableableState( server, fromEnableableState ); + } + + @After + public void shutdown() + { + ServerStateTestHelpers.teardown( server ); + } + + @Test + public void executeEnableable() throws Throwable + { + toLifeCycleState.set( server ); + assertEquals( shouldBeRunning, server.isRunnig() ); + } + + private static boolean expectedResult( EnableableState state, LifeCycleState toLifeCycle ) + { + if ( state == EnableableState.Untouched || state == EnableableState.Enabled ) + { + if ( toLifeCycle == LifeCycleState.Stop || toLifeCycle == LifeCycleState.Shutdown ) + { + return false; + } + else if ( toLifeCycle == LifeCycleState.Start ) + { + return true; + } + else + { + throw new IllegalStateException( "Should not transition to state " + toLifeCycle ); + } + } + else if ( state == EnableableState.Disabled ) + { + return false; + } + else + { + throw new IllegalStateException( "Unknown state " + state ); + } + } +} diff --git a/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/net/ServerEnableableStateChangeTest.java b/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/net/ServerEnableableStateChangeTest.java new file mode 100644 index 0000000000000..dd7b1051a9204 --- /dev/null +++ b/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/net/ServerEnableableStateChangeTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2002-2018 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.neo4j.causalclustering.net; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.List; + +import org.neo4j.causalclustering.net.ServerStateTestHelpers.EnableableState; +import org.neo4j.causalclustering.net.ServerStateTestHelpers.LifeCycleState; + +import static org.junit.Assert.assertEquals; +import static org.neo4j.causalclustering.net.ServerStateTestHelpers.LifeCycleState.values; +import static org.neo4j.causalclustering.net.ServerStateTestHelpers.createServer; +import static org.neo4j.causalclustering.net.ServerStateTestHelpers.setEnableableState; +import static org.neo4j.causalclustering.net.ServerStateTestHelpers.setInitialState; + +@RunWith( Parameterized.class ) +public class ServerEnableableStateChangeTest +{ + @Parameterized.Parameter() + public LifeCycleState fromState; + + @Parameterized.Parameter( 1 ) + public EnableableState fromEnableableState; + + @Parameterized.Parameter( 2 ) + public EnableableState toEnableableState; + + @Parameterized.Parameter( 3 ) + public boolean shouldBeRunning; + + @Parameterized.Parameters( name = "From {0} and {1} to {2} Server is running ? {3}" ) + public static Iterable data() + { + List params = new ArrayList<>(); + for ( LifeCycleState lifeCycleState : values() ) + { + for ( EnableableState enableableState : EnableableState.values() ) + { + for ( EnableableState toEnableable : toEnableableState() ) + { + params.add( new Object[]{lifeCycleState, enableableState, toEnableable, expectedResult( lifeCycleState, toEnableable )} ); + } + } + } + return params; + } + + private Server server; + + private static EnableableState[] toEnableableState() + { + return new EnableableState[]{EnableableState.Enabled, EnableableState.Disabled}; + } + + @Before + public void setUpServer() throws Throwable + { + server = createServer(); + setInitialState( server, fromState ); + setEnableableState( server, fromEnableableState ); + } + + @After + public void shutdown() + { + ServerStateTestHelpers.teardown( server ); + } + + @Test + public void executeEnableable() + { + setEnableableState( server, toEnableableState ); + assertEquals( shouldBeRunning, server.isRunnig() ); + } + + private static boolean expectedResult( LifeCycleState fromState, EnableableState toEnableable ) + { + if ( toEnableable == EnableableState.Disabled ) + { + return false; + } + else if ( toEnableable == EnableableState.Enabled ) + { + return fromState != LifeCycleState.Stop && fromState != LifeCycleState.Shutdown && fromState != LifeCycleState.Init; + } + else + { + throw new IllegalStateException( "Should not transition to any other state got: " + toEnableable ); + } + } +} diff --git a/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/net/ServerStateTestHelpers.java b/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/net/ServerStateTestHelpers.java new file mode 100644 index 0000000000000..2c3d094bebbe3 --- /dev/null +++ b/enterprise/causal-clustering/src/test/java/org/neo4j/causalclustering/net/ServerStateTestHelpers.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2002-2018 "Neo Technology," + * Network Engine for Objects in Lund AB [http://neotechnology.com] + * + * This file is part of Neo4j. + * + * Neo4j is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package org.neo4j.causalclustering.net; + +import org.neo4j.helpers.ListenSocketAddress; +import org.neo4j.kernel.lifecycle.Lifecycle; +import org.neo4j.logging.FormattedLogProvider; +import org.neo4j.logging.Level; +import org.neo4j.ports.allocation.PortAuthority; + +class ServerStateTestHelpers +{ + static void teardown( Server server ) + { + server.stop(); + server.shutdown(); + } + + static Server createServer() + { + return new Server( channel -> + { + }, FormattedLogProvider.withDefaultLogLevel( Level.DEBUG ).toOutputStream( System.out ), + FormattedLogProvider.withDefaultLogLevel( Level.DEBUG ).toOutputStream( System.out ), + new ListenSocketAddress( "localhost", PortAuthority.allocatePort() ), "serverName" ); + } + + static void setEnableableState( Server server, EnableableState enableableState ) + { + switch ( enableableState ) + { + case Enabled: + server.enable(); + return; + case Disabled: + server.disable(); + return; + case Untouched: + return; + default: + throw new IllegalStateException( "Not recognized state " + enableableState ); + } + } + + static void setInitialState( Server server, LifeCycleState state ) throws Throwable + { + for ( LifeCycleState lifeCycleState : LifeCycleState.values() ) + { + if ( lifeCycleState.compareTo( state ) <= 0 ) + { + lifeCycleState.set( server ); + } + } + } + + enum LifeCycleState + { + Init + { + @Override + void set( Lifecycle lifecycle ) throws Throwable + { + lifecycle.init(); + } + }, + Start + { + @Override + void set( Lifecycle lifecycle ) throws Throwable + { + lifecycle.start(); + } + }, + Stop + { + @Override + void set( Lifecycle lifecycle ) throws Throwable + { + lifecycle.stop(); + } + }, + Shutdown + { + @Override + void set( Lifecycle lifecycle ) throws Throwable + { + lifecycle.shutdown(); + } + }; + + abstract void set( Lifecycle lifecycle ) throws Throwable; + } + + enum EnableableState + { + Untouched, + Enabled, + Disabled + } +}