Skip to content

Commit

Permalink
Merge branch '2.3' into 3.0
Browse files Browse the repository at this point in the history
Conflicts:
	enterprise/cluster/src/main/java/org/neo4j/cluster/protocol/cluster/ClusterConfiguration.java
  • Loading branch information
apcj committed Feb 23, 2016
2 parents 71bbef0 + 266f09d commit a698d93
Show file tree
Hide file tree
Showing 4 changed files with 257 additions and 11 deletions.
Expand Up @@ -34,7 +34,6 @@
import org.neo4j.logging.Log;
import org.neo4j.logging.LogProvider;

import static org.neo4j.helpers.collection.Iterables.limit;
import static org.neo4j.helpers.collection.Iterables.toList;

/**
Expand Down Expand Up @@ -106,9 +105,7 @@ public URI boundAt()
@Override
public List<URI> getAcceptors()
{
// Only use 2f+1 acceptors
return toList( limit( commonState.configuration()
.getAllowedFailures() * 2 + 1, commonState.configuration().getMemberURIs() ) );
return commonState.configuration().getMemberURIs();
}

@Override
Expand Down
Expand Up @@ -45,7 +45,6 @@ public class ClusterConfiguration
private final List<URI> candidateMembers;
private Map<InstanceId, URI> members;
private Map<String, InstanceId> roles = new HashMap<>();
private int allowedFailures = 1;

public ClusterConfiguration( String name, LogProvider logProvider, String... members )
{
Expand Down Expand Up @@ -175,7 +174,8 @@ public Map<String, InstanceId> getRoles()

public int getAllowedFailures()
{
return allowedFailures;
assert members.size() > 0;
return (members.size() - 1) / 2;
}

public void left()
Expand Down Expand Up @@ -245,10 +245,6 @@ public boolean equals( Object o )

ClusterConfiguration that = (ClusterConfiguration) o;

if ( allowedFailures != that.allowedFailures )
{
return false;
}
if ( !candidateMembers.equals( that.candidateMembers ) )
{
return false;
Expand Down Expand Up @@ -276,7 +272,6 @@ public int hashCode()
result = 31 * result + candidateMembers.hashCode();
result = 31 * result + members.hashCode();
result = 31 * result + roles.hashCode();
result = 31 * result + allowedFailures;
return result;
}
}
103 changes: 103 additions & 0 deletions enterprise/ha/src/test/java/org/neo4j/kernel/ha/TestFailover.java
@@ -0,0 +1,103 @@
/*
* Copyright (c) 2002-2016 "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.kernel.ha;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import java.util.Arrays;
import java.util.Collection;

import org.neo4j.cluster.ClusterSettings;
import org.neo4j.kernel.impl.ha.ClusterManager;
import org.neo4j.test.LoggerRule;
import org.neo4j.test.TargetDirectory;

import static org.neo4j.helpers.collection.MapUtil.stringMap;
import static org.neo4j.kernel.impl.ha.ClusterManager.clusterOfSize;

@RunWith( Parameterized.class )
public class TestFailover
{
@Rule
public LoggerRule logger = new LoggerRule();
@Rule
public TargetDirectory.TestDirectory dir = TargetDirectory.testDirForTest( getClass() );

// parameters
private int clusterSize;

@Parameters( name = "clusterSize:{0}")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{ 3 },
{ 4 },
{ 5 },
{ 6 },
{ 7 },
});
}

public TestFailover( int clusterSize )
{
this.clusterSize = clusterSize;
}

private void testFailOver( int clusterSize ) throws Throwable
{
// given
ClusterManager clusterManager = new ClusterManager.Builder().withRootDirectory( dir.cleanDirectory( "failover" ) ).
withProvider( ClusterManager.clusterOfSize( clusterSize ) )
.withSharedConfig( stringMap(
ClusterSettings.heartbeat_interval.name(), "1" ) )
.build();

clusterManager.start();
ClusterManager.ManagedCluster cluster = clusterManager.getDefaultCluster();

cluster.await( ClusterManager.allSeesAllAsAvailable() );
HighlyAvailableGraphDatabase oldMaster = cluster.getMaster();

// When
long start = System.nanoTime();
ClusterManager.RepairKit repairKit = cluster.fail( oldMaster );
logger.getLogger().warning( "Shut down master" );

// Then
cluster.await( ClusterManager.masterAvailable( oldMaster ) );
long end = System.nanoTime();

logger.getLogger().warning( "Failover took:" + (end - start) / 1000000 + "ms" );

repairKit.repair();
Thread.sleep( 3000 ); // give repaired instance chance to cleanly rejoin and exit faster

clusterManager.stop();
}

@Test
public void testFailOver() throws Throwable
{
testFailOver( clusterSize );
}
}
@@ -0,0 +1,151 @@
/*
* Copyright (c) 2002-2016 "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.kernel.ha;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;

import org.neo4j.cluster.ClusterSettings;
import org.neo4j.kernel.impl.ha.ClusterManager;
import org.neo4j.kernel.impl.ha.ClusterManager.NetworkFlag;
import org.neo4j.kernel.impl.ha.ClusterManager.RepairKit;
import org.neo4j.test.LoggerRule;
import org.neo4j.test.TargetDirectory;

import static org.neo4j.helpers.collection.MapUtil.stringMap;
import static org.neo4j.kernel.impl.ha.ClusterManager.allSeesAllAsAvailable;
import static org.neo4j.kernel.impl.ha.ClusterManager.masterAvailable;

@RunWith( Parameterized.class )
public class TestFailoverWithAdditionalSlaveFailures
{
@Rule
public LoggerRule logger = new LoggerRule();
@Rule
public TargetDirectory.TestDirectory dir = TargetDirectory.testDirForTest( getClass() );

// parameters
private int clusterSize;
private int[] slavesToFail;

@Parameters( name = "{index} clusterSize:{0}")
public static Collection<Object[]> data() {
return Arrays.asList(new Object[][] {
{5, new int[]{1}},
{5, new int[]{2}},
{5, new int[]{3}},
{5, new int[]{4}},

{6, new int[]{1}},
{6, new int[]{3}},
{6, new int[]{5}},

{7, new int[]{1, 2}},
{7, new int[]{3, 4}},
{7, new int[]{5, 6}},
});
}

public TestFailoverWithAdditionalSlaveFailures( int clusterSize, int[] slavesToFail )
{
this.clusterSize = clusterSize;
this.slavesToFail = slavesToFail;
}

@Test
public void testFailoverWithAdditionalSlave() throws Throwable
{
testFailoverWithAdditionalSlave( clusterSize, slavesToFail );
}

private void testFailoverWithAdditionalSlave( int clusterSize, int[] slaveIndexes ) throws Throwable
{
ClusterManager manager = new ClusterManager.Builder().withRootDirectory( dir.cleanDirectory( "testcluster" ) ).
withProvider( ClusterManager.clusterOfSize( clusterSize ) )
.withSharedConfig( stringMap(
ClusterSettings.heartbeat_interval.name(), "1" ) )
.build();

try
{
manager.start();
ClusterManager.ManagedCluster cluster = manager.getDefaultCluster();

cluster.await( allSeesAllAsAvailable() );
cluster.await( masterAvailable() );

Collection<HighlyAvailableGraphDatabase> failed = new ArrayList<>();
Collection<RepairKit> repairKits = new ArrayList<>();

for ( int slaveIndex : slaveIndexes )
{
HighlyAvailableGraphDatabase nthSlave = getNthSlave( cluster, slaveIndex );
failed.add( nthSlave );
RepairKit repairKit = cluster.fail( nthSlave, false, NetworkFlag.values() );
repairKits.add( repairKit );
}

HighlyAvailableGraphDatabase oldMaster = cluster.getMaster();
failed.add( oldMaster );
repairKits.add( cluster.fail( oldMaster, false, NetworkFlag.values() ) );

cluster.await( masterAvailable( toArray( failed ) ) );

for ( RepairKit repairKit : repairKits )
{
repairKit.repair();
}

Thread.sleep( 3000 ); // give repaired instances a chance to cleanly rejoin and exit faster
}
finally
{
manager.shutdown();
}
}

private HighlyAvailableGraphDatabase getNthSlave( ClusterManager.ManagedCluster cluster, int slaveOrder )
{
assert slaveOrder > 0;
HighlyAvailableGraphDatabase slave = null;

List<HighlyAvailableGraphDatabase> excluded = new ArrayList<>();
while( slaveOrder-->0 )
{
slave = cluster.getAnySlave( toArray( excluded ) );
excluded.add( slave );
}

return slave;
}

private HighlyAvailableGraphDatabase[] toArray( Collection<HighlyAvailableGraphDatabase> excluded )
{
return excluded.toArray( new HighlyAvailableGraphDatabase[excluded.size()] );
}
}

0 comments on commit a698d93

Please sign in to comment.