/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
licenses@blazegraph.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Jun 6, 2010
*/
package com.bigdata.quorum;
import java.rmi.Remote;
import java.util.UUID;
/**
* An interface that causes various changes in the distributed quorum state
* required to execute the intention of a {@link QuorumMember} service and its
* cognizant {@link AbstractQuorum}. The {@link QuorumActor} is responsible for
* making coherent changes in the distributed quorum state. However, when a
* service terminates abnormally, is partitioned from the distributed quorum,
* etc., then the distributed quorum state MAY temporary be inconsistent. For
* example, when a service loses its zookeeper connection, zookeeper will remove
* all <em>ephemeral</em> znodes for that service, but the order in which those
* tokens are removed is not specified.
* <p>
* The {@link QuorumActor} will reject operations whose preconditions are not
* met (on member add, pipeline add, vote cast, service join, set token, etc.)
* and for take additional operations as necessary to ensure that postconditions
* are satisfied (on remove). This asymmetry in the API is intended to make it
* easier for a service to "remove" itself from the various collections
* maintained in the distributed quorum state.
* <p>
* The preconditions for add are:
* <dl>
* <dt>{@link #memberAdd()}</dt>
* <dd>none.</dd>
* <dt>{@link #pipelineAdd()}</dt>
* <dd>member.</dd>
* <dt>{@link #castVote(long)}</dt>
* <dd>member (service implicitly joins pipeline if not present).</dd>
* <dt>{@link #serviceJoin()}</dt>
* <dd>member, pipeline, consensus around cast vote, predecessor in the vote
* order is joined</dd>
* <dt>{@link #setToken(long)}</dt>
* <dd>member, pipeline, consensus around cast vote, first in the vote order,
* new value is GT the lastValidToken.</dd>
* </dl>
* <p>
* The post-conditions for remove are:
* <dl>
* <dt>{@link #memberRemove()}</dt>
* <dd>not joined, no vote cast, not in pipeline, not member.</dd>
* <dt>{@link #pipelineRemove()}</dt>
* <dd>not in pipeline, vote withdrawn, service not joined.</dd>
* <dt>{@link #withdrawVote()}</dt>
* <dd>vote withdrawn, service not joined.</dd>
* <dt>{@link #serviceLeave()}</dt>
* <dd>vote withdrawn, not in pipeline, service not joined.</dd>
* <dt>{@link #clearToken()}</dt>
* <dd>This operation does not have any pre- or post-conditions. However,
* services will quickly recognize that the quorum token is no longer valid and
* will initiate local state changes required to prepare themselves to form a
* new quorum.</dd>
* </dl>
* <p>
* The methods on this interface must be atomic with respect to the observed
* state change reported by the paired {@link QuorumWatcher} and the update of
* the local {@link Quorum} object's internal state. Without this postcondition,
* a {@link QuorumActor} could issue two requests, such as
* <code>{@link #memberAdd()}; {@link #pipelineAdd()};</code> and have the
* second request rejected based on preconditions if the {@link QuorumWatcher}
* had not yet seen the state change for the first request.
*
* @author thompsonbry@users.sourceforge.net
* @see QuorumWatcher
*/
public interface QuorumActor<S extends Remote, C extends QuorumClient<S>> {
/**
* The {@link Quorum}.
*/
public Quorum<S, C> getQuourm();
/**
* The service on whose behalf this class is acting.
*/
public QuorumMember<S> getQuorumMember();
/**
* The {@link UUID} of the service on whose behalf this class is acting.
*/
public UUID getServiceId();
/**
* Add the service to the set of quorum members.
*/
void memberAdd();
/**
* Remove the service from the set of quorum members.
*/
void memberRemove();
/**
* Add the service to the write pipeline.
*/
void pipelineAdd();
/**
* Remove the service from the write pipeline.
*/
void pipelineRemove();
/**
* Cast a vote on the behalf of the associated service. If the service is
* not part of the pipeline, then it is implicitly added to the pipeline. If
* the service has already voted for some other lastCommitTime, then that
* vote is withdrawn before the new vote is cast. Services do not withdraw
* their cast votes until a quorum breaks and a new consensus needs to be
* established. When it does, then need to consult their root blocks and
* vote their then current lastCommitTime.
* <p>
* When a service needs to re-synchronize with a quorum, it initially votes
* its current lastCommitTime. Once the service is receiving writes from the
* write pipeline and has synchronized any historical delta, it will update
* its vote and join the quorum at the next commit point (or immediately if
* there are no outstanding writes against the quorum).
*
* @param lastCommitTime
* The lastCommitTime timestamp for which the service casts its
* vote.
*
* @throws IllegalArgumentException
* if the lastCommitTime is negative.
*/
void castVote(long lastCommitTime);
/**
* Withdraw the vote cast by the service (a service has only one vote).
*/
void withdrawVote();
/**
* Add the associated service to the set of joined services for the quorum.
*/
void serviceJoin();
/**
* Remove the associated service from the set of joined services for the
* quorum.
*/
void serviceLeave();
// /**
// * Set the lastValidToken on the quorum equal to the given token. When a new
// * leader will be elected, this method will be invoked to update the quorum
// * token, passing in <code>newToken := lastValidToken+1</code>.
// */
// void setLastValidToken(final long newToken);
//
// /**
// * Set the current token on the quorum equal to the lastValidToken. Note that
// * {@link #setLastValidToken(long)} will have been invoked as a precondition
// * so this has the effect of updating the current token to the recently
// * assigned newToken.
// */
// void setToken();
// /**
// * Set the lastValidToken on the quorum equal to the given token. When a new
// * leader will be elected, this method will be invoked to update the quorum
// * token, passing in <code>newToken := lastValidToken+1</code>.
// */
// void setToken(final long newToken);
/**
* Atomically set the lastValidToken and the current token on the quorum
* equal to the given token. This method is invoked when the leader is
* elected, passing in <code>newToken := lastValidToken + 1</code>.
*/
void setToken(long newToken);
// /**@deprecated*/
// void setLastValidToken(long x);
/**
* Clear the quorum token.
*/
void clearToken();
/**
* Remove the service from the quorum. This should be called when a problem
* with the service is reported to the quorum leader, for example as a
* result of a failed RMI request or failed socket level write replication.
* Such errors arise either from network connectivity or service death.
* These problems will generally be cured, but the heatbeat timeout to cure
* the problem can cause write replication to block. This method may be used
* to force the timely reordering of the pipeline in order to work around
* the replication problem. This is not a permenant disabling of the service
* - the service may be restarted or may recover and reenter the quorum at
* any time.
*
* @param serviceId
* The UUID of the service to be removed.
*
* @see <a href="https://sourceforge.net/apps/trac/bigdata/ticket/724" > HA
* wire pulling and sure kill testing </a>
*/
public void forceRemoveService(UUID serviceId);
}