/**
* Copyright (c) 2002-2012 "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 <http://www.gnu.org/licenses/>.
*/
package org.neo4j.cluster.protocol.atomicbroadcast.multipaxos;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Semaphore;
import ch.qos.logback.classic.LoggerContext;
import org.hamcrest.CoreMatchers;
import org.junit.Assert;
import org.junit.Test;
import org.neo4j.cluster.BindingListener;
import org.neo4j.cluster.ClusterSettings;
import org.neo4j.cluster.MultiPaxosServerFactory;
import org.neo4j.cluster.NetworkedServerFactory;
import org.neo4j.cluster.ProtocolServer;
import org.neo4j.cluster.com.NetworkInstance;
import org.neo4j.cluster.protocol.atomicbroadcast.AtomicBroadcast;
import org.neo4j.cluster.protocol.atomicbroadcast.AtomicBroadcastMap;
import org.neo4j.cluster.protocol.cluster.Cluster;
import org.neo4j.cluster.protocol.cluster.ClusterConfiguration;
import org.neo4j.cluster.protocol.cluster.ClusterListener;
import org.neo4j.cluster.protocol.election.ServerIdElectionCredentialsProvider;
import org.neo4j.cluster.protocol.snapshot.Snapshot;
import org.neo4j.cluster.timeout.FixedTimeoutStrategy;
import org.neo4j.graphdb.factory.GraphDatabaseSettings;
import org.neo4j.helpers.collection.MapUtil;
import org.neo4j.kernel.configuration.Config;
import org.neo4j.kernel.configuration.ConfigurationDefaults;
import org.neo4j.kernel.lifecycle.LifeSupport;
import org.neo4j.kernel.logging.LogbackService;
import org.slf4j.LoggerFactory;
/**
* TODO
*/
public class MultiPaxosNetworkTest
{
@Test
public void testBroadcast()
throws ExecutionException, InterruptedException, URISyntaxException, BrokenBarrierException
{
final LifeSupport life = new LifeSupport();
Config config = new Config( new ConfigurationDefaults( GraphDatabaseSettings.class ).apply( MapUtil.stringMap
( GraphDatabaseSettings.store_dir.name(), "test" ) ) );
LoggerContext loggerContext = new LoggerContext();
loggerContext.putProperty( "host", "none" );
LogbackService logging = life.add( new LogbackService( config, loggerContext ) );
NetworkedServerFactory serverFactory = new NetworkedServerFactory( life,
new MultiPaxosServerFactory(
new ClusterConfiguration( "default",
"cluster://localhost:5001",
"cluster://localhost:5002",
"cluster://localhost:5003" ),
logging ),
new FixedTimeoutStrategy( 5000 ), logging );
ServerIdElectionCredentialsProvider serverIdElectionCredentialsProvider = new
ServerIdElectionCredentialsProvider();
final ProtocolServer server1 = serverFactory.newNetworkedServer( new Config( new ConfigurationDefaults(
NetworkInstance.Configuration.class ).apply( MapUtil.stringMap( ClusterSettings.cluster_server.name(),
":5001" ) ) ),
new InMemoryAcceptorInstanceStore(), serverIdElectionCredentialsProvider );
server1.addBindingListener( serverIdElectionCredentialsProvider );
serverIdElectionCredentialsProvider = new ServerIdElectionCredentialsProvider();
final ProtocolServer server2 = serverFactory.newNetworkedServer( new Config( new ConfigurationDefaults(
NetworkInstance.Configuration.class ).apply( MapUtil.stringMap( ClusterSettings.cluster_server
.name(), ":5002" ) ) ), new InMemoryAcceptorInstanceStore(), serverIdElectionCredentialsProvider );
server2.addBindingListener( serverIdElectionCredentialsProvider );
serverIdElectionCredentialsProvider = new ServerIdElectionCredentialsProvider();
final ProtocolServer server3 = serverFactory.newNetworkedServer( new Config( new ConfigurationDefaults(
NetworkInstance.Configuration.class ).apply( MapUtil.stringMap( ClusterSettings.cluster_server
.name(), ":5003" ) ) ), new InMemoryAcceptorInstanceStore(), serverIdElectionCredentialsProvider );
server3.addBindingListener( serverIdElectionCredentialsProvider );
server1.addBindingListener( new BindingListener()
{
@Override
public void listeningAt( URI me )
{
server1.newClient( Cluster.class ).create( "default" );
}
} );
server2.addBindingListener( new BindingListener()
{
@Override
public void listeningAt( URI me )
{
server2.newClient( Cluster.class ).join( server1.getServerId() );
}
} );
AtomicBroadcast atomicBroadcast1 = server1.newClient( AtomicBroadcast.class );
AtomicBroadcast atomicBroadcast2 = server2.newClient( AtomicBroadcast.class );
AtomicBroadcast atomicBroadcast3 = server3.newClient( AtomicBroadcast.class );
Snapshot snapshot1 = server1.newClient( Snapshot.class );
Snapshot snapshot2 = server2.newClient( Snapshot.class );
Snapshot snapshot3 = server3.newClient( Snapshot.class );
final AtomicBroadcastMap<String, String> map = new AtomicBroadcastMap<String, String>( atomicBroadcast1,
snapshot1 );
final AtomicBroadcastMap<String, String> map2 = new AtomicBroadcastMap<String, String>( atomicBroadcast2,
snapshot2 );
final AtomicBroadcastMap<String, String> map3 = new AtomicBroadcastMap<String, String>( atomicBroadcast3,
snapshot3 );
final Semaphore semaphore = new Semaphore( 0 );
server1.newClient( Cluster.class ).addClusterListener( new ClusterListener.Adapter()
{
@Override
public void joinedCluster( URI member )
{
LoggerFactory.getLogger( getClass() ).info( "1 sees join by " + member );
}
} );
server2.newClient( Cluster.class ).addClusterListener( new ClusterListener.Adapter()
{
@Override
public void enteredCluster( ClusterConfiguration clusterConfiguration )
{
semaphore.release();
}
@Override
public void joinedCluster( URI member )
{
LoggerFactory.getLogger( getClass() ).info( "2 sees join by " + member );
}
} );
life.start();
semaphore.acquire();
LoggerFactory.getLogger( getClass() ).info( "Joined cluster - set data" );
for ( int i = 0; i < 100; i++ )
{
map.put( "foo" + i, "bar" + i );
}
LoggerFactory.getLogger( getClass() ).info( "Set all values" );
String value = map.get( "foo1" );
LoggerFactory.getLogger( getClass() ).info( "3 joins 1" );
server3.newClient( Cluster.class ).addClusterListener( new ClusterListener.Adapter()
{
@Override
public void enteredCluster( ClusterConfiguration clusterConfiguration )
{
LoggerFactory.getLogger( getClass() ).info( "3 entered cluster of:" + clusterConfiguration.getMembers
() );
semaphore.release();
}
} );
server3.newClient( Cluster.class ).join( server1.getServerId() );
semaphore.acquire();
LoggerFactory.getLogger( getClass() ).info( "Read value1" );
Assert.assertThat( value, CoreMatchers.equalTo( "bar1" ) );
map2.put( "foo2", "666" );
LoggerFactory.getLogger( getClass() ).info( "Read value2:" + map2.get( "foo1" ) );
LoggerFactory.getLogger( getClass() ).info( "Read value3:" + map2.get( "foo2" ) );
LoggerFactory.getLogger( getClass() ).info( "Read value4:" + map3.get( "foo1" ) );
LoggerFactory.getLogger( getClass() ).info( "Read value5:" + map3.get( "foo99" ) );
Assert.assertThat( map3.get( "foo1" ), CoreMatchers.equalTo( "bar1" ) );
Assert.assertThat( map3.get( "foo99" ), CoreMatchers.equalTo( "bar99" ) );
map.close();
map2.close();
map3.close();
life.stop();
}
}