/**
* 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.kernel.ha.cluster;
import java.net.URI;
/**
* This represents the different states that a cluster member
* can have internally.
*/
public enum ClusterMemberState
{
/**
* This state is the initial state, and is also the state used when leaving the cluster.
* <p/>
* Here we are waiting for events that transitions this member either to becoming a master or slave.
*/
PENDING
{
@Override
public ClusterMemberState masterIsElected( ClusterMemberContext context, URI masterURI )
{
assert context.getAvailableHaMaster() == null;
if ( masterURI.equals( context.getMyId() ) )
{
return TO_MASTER;
}
return PENDING;
}
@Override
public ClusterMemberState masterIsAvailable( ClusterMemberContext context, URI masterURI, URI
masterHaURI )
{
// assert context.getAvailableMaster() == null;
if ( masterURI.equals( context.getMyId() ) )
{
throw new RuntimeException( "this cannot be happening" );
}
return TO_SLAVE;
}
@Override
public ClusterMemberState slaveIsAvailable( ClusterMemberContext context, URI slaveUri )
{
if ( slaveUri.equals( context.getMyId() ) )
{
throw new RuntimeException( "cannot go from pending to slave" );
}
return this;
}
@Override
public boolean isAccessAllowed( ClusterMemberContext context )
{
return false;
}
},
/**
* Member now knows that a master is available, and is transitioning itself to become a slave to that master.
* It is performing the transition process here, and so is not yet available as a slave.
*/
TO_SLAVE
{
@Override
public ClusterMemberState masterIsElected( ClusterMemberContext context, URI masterURI )
{
if ( masterURI.equals( context.getElectedMasterId() ) )
{
// A member joined and we all got the same event
return this;
}
if ( masterURI.equals( context.getMyId() ) )
{
return TO_MASTER;
}
// This may mean the master changed from the time we transitioned here
return PENDING;
}
@Override
public ClusterMemberState masterIsAvailable( ClusterMemberContext context, URI masterURI,
URI masterHaURI )
{
if ( masterURI.equals( context.getMyId() ) )
{
throw new RuntimeException( "i (" + context.getMyId() + ") am trying to become a slave but " +
"someone said i am available as master" );
}
if ( masterHaURI.equals( context.getAvailableHaMaster() ) )
{
// A member joined and we all got the same event
return this;
}
throw new RuntimeException( "my (" + context.getMyId() + ") current master is " + context
.getAvailableHaMaster() + " (elected as " + context.getElectedMasterId() + " but i got a " +
"masterIsAvailable event for " + masterHaURI );
}
@Override
public ClusterMemberState slaveIsAvailable( ClusterMemberContext context, URI slaveUri )
{
if ( slaveUri.equals( context.getMyId() ) )
{
return SLAVE;
}
return this;
}
@Override
public boolean isAccessAllowed( ClusterMemberContext context )
{
return false;
}
},
/**
* The cluster member knows that it has been elected as master, and starts the transitioning process.
*/
TO_MASTER
{
@Override
public ClusterMemberState masterIsElected( ClusterMemberContext context, URI masterURI )
{
assert context.getAvailableHaMaster() == null;
if ( masterURI.equals( context.getMyId() ) )
{
return this;
}
return PENDING; // everything still goes
}
@Override
public ClusterMemberState masterIsAvailable( ClusterMemberContext context, URI masterURI,
URI masterHaURI )
{
if ( masterURI.equals( context.getMyId() ) )
{
return MASTER;
}
throw new RuntimeException( "i probably missed a masterIsElected event - not really that good" );
}
@Override
public ClusterMemberState slaveIsAvailable( ClusterMemberContext context, URI slaveUri )
{
if ( slaveUri.equals( context.getMyId() ) )
{
throw new RuntimeException( "cannot be transitioning to master and slave at the same time" );
}
return this;
}
@Override
public boolean isAccessAllowed( ClusterMemberContext context )
{
return false;
}
},
/**
* Cluster member is available as master for other cluster members to use.
*/
MASTER
{
@Override
public ClusterMemberState masterIsElected( ClusterMemberContext context, URI masterURI )
{
if ( masterURI.equals( context.getMyId() ) )
{
return this;
}
// This means we (probably) were disconnected and got back in the cluster
// and we find out that we are not the master anymore.
return PENDING;
}
@Override
public ClusterMemberState masterIsAvailable( ClusterMemberContext context, URI masterURI,
URI masterHaURI )
{
if ( masterURI.equals( context.getMyId() ) )
{
return this;
}
throw new RuntimeException( "I, " + context.getMyId() + " got a masterIsAvailable for " +
masterHaURI + " (id is " + masterURI + " ) while being master. That should not happen" );
}
@Override
public ClusterMemberState slaveIsAvailable( ClusterMemberContext context, URI slaveUri )
{
if ( slaveUri.equals( context.getMyId() ) )
{
throw new RuntimeException( "cannot be master and transition to slave at the same time" );
}
return this;
}
@Override
public boolean isAccessAllowed( ClusterMemberContext context )
{
return true;
}
},
/**
* Cluster member is ready as a slave
*/
SLAVE
{
@Override
public ClusterMemberState masterIsElected( ClusterMemberContext context, URI masterURI )
{
if ( masterURI.equals( context.getMyId() ) )
{
return TO_MASTER;
}
if ( masterURI.equals( context.getElectedMasterId() ) )
{
return this;
}
else
{
return PENDING;
}
}
@Override
public ClusterMemberState masterIsAvailable( ClusterMemberContext context, URI masterURI,
URI masterHaURI )
{
if ( masterURI.equals( context.getMyId() ) )
{
throw new RuntimeException( "master? i don't think so" );
}
else if ( masterHaURI.equals( context.getAvailableHaMaster() ) )
{
// this is just someone else that joined the cluster
return this;
}
throw new RuntimeException( "i prolly missed a masterIsElected event, we're not looking good" );
}
@Override
public ClusterMemberState slaveIsAvailable( ClusterMemberContext context, URI slaveUri )
{
if ( slaveUri.equals( context.getMyId() ) )
{
throw new RuntimeException( "cannot transition from slave to slave" );
}
return this;
}
@Override
public boolean isAccessAllowed( ClusterMemberContext context )
{
return true;
}
};
public abstract ClusterMemberState masterIsElected( ClusterMemberContext context, URI masterUri );
public abstract ClusterMemberState masterIsAvailable( ClusterMemberContext context, URI masterClusterUri,
URI masterHaURI );
public abstract ClusterMemberState slaveIsAvailable( ClusterMemberContext context, URI slaveUri );
public abstract boolean isAccessAllowed( ClusterMemberContext context );
}