/*
* Copyright 2009 Mustard Grain, Inc., 2009-2010 LinkedIn, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package voldemort.cluster.failuredetector;
import voldemort.cluster.Node;
import voldemort.store.UnreachableStoreException;
/**
* The FailureDetector API is used to determine a cluster's node availability.
* Machines and servers can go down at any time and usage of this API can be
* used by request routing in an attempt to avoid unavailable servers.
*
* <p/>
*
* A FailureDetector is specific to a given cluster and as such there should
* only be one instance per cluster per JVM.
*
* <p/>
*
* Implementations can differ dramatically in how they approach the problem of
* determining node availability. Some implementations may rely heavily on
* invocations of recordException and recordSuccess to determine availability.
* The result is that such a FailureDetector implementation performs little
* logic other than bookkeeping, implicitly trusting users of the API. However,
* other implementations may be more selective in using results of any external
* users' calls to the recordException and recordSuccess methods.
* Implementations may use these error/success calls as "hints" or may ignore
* them outright.
*
* <p/>
*
* To contrast the two approaches to implementing:
*
* <ol>
* <li><b>Externally-based implementations</b> use algorithms that rely heavily
* on users for correctness. For example, let's say a user attempts to contact a
* node which then fails. A responsible caller should invoke the recordException
* API to inform the FailureDetector that an error has taken place for the node.
* The FailureDetector itself hasn't really determined availability itself. So
* if the caller is incorrect or buggy, the FailureDetector's accuracy is
* compromised.</li>
* <li><b>Internally-based implementations</b> rely on their own determination
* of node availability. For example, a heartbeat style implementation may pay
* only a modicum of attention when its recordException and/or recordSuccess
* methods are invoked by outside callers.</li>
* </ol>
*
* Naturally there is a spectrum of implementations and external calls to
* recordException and recordSuccess should (not must) provide some input to the
* internal algorithm.
*
* @see voldemort.store.routed.RoutedStore
*/
public interface FailureDetector {
/**
* Determines if the node is available or offline.
*
* The isAvailable method is a simple boolean operation to determine if the
* node in question is available. As expected, the result of this call is an
* approximation given race conditions. However, the FailureDetector should
* do its best to determine the then-current state of the cluster to produce
* a minimum of false negatives and false positives.
*
* <p/>
*
* <b>Note</b>: this determination is approximate and differs based upon the
* algorithm used by the implementation.
*
* @param node Node to check
*
* @return True if available, false otherwise
*/
public boolean isAvailable(Node node);
/**
* Returns the number of milliseconds since the node was last checked for
* availability. Because of its lack of precision, this should really only
* be used for status/reporting.
*
* @param node Node to check
*
* @return Number of milliseconds since the node was last checked for
* availability
*/
public long getLastChecked(Node node);
/**
* Allows external callers to provide input to the FailureDetector that an
* access to the node succeeded. As with recordException, the implementation
* is free to use or ignore this input. It can be considered a "hint" to the
* FailureDetector rather than gospel truth.
*
* <p/>
*
* <b>Note for implementors</b>: because of threading issues it's possible
* for multiple threads to attempt access to a node and some fail and some
* succeed. In a classic last-one-in-wins scenario, it's possible for the
* failures to be recorded first and then the successes. It would be prudent
* for implementations not to immediately assume that the node is then
* available.
*
* @param node Node to check
* @param requestTime Length of time (in milliseconds) to perform request
*/
public void recordSuccess(Node node, long requestTime);
/**
* Allows external callers to provide input to the FailureDetector that an
* error occurred when trying to access the node. The implementation is free
* to use or ignore this input. It can be considered a "hint" to the
* FailureDetector rather than an absolute truth. For example, it is
* possible to call recordException for a given node and have an immediate
* call to isAvailable return true, depending on the implementation.
*
* @param node Node to check
* @param requestTime Length of time (in milliseconds) to perform request
* @param e Exception that occurred when trying to access the node
*/
public void recordException(Node node, long requestTime, UnreachableStoreException e);
/**
* Adds a FailureDetectorListener instance that can receive event callbacks
* about node availability state changes.
*
* <p/>
*
* <b>Notes</b>:
* <ol>
* <li>Make sure to clean up the listener by invoking
* removeFailureDetectorListener
* <li>Make sure that the FailureDetectorListener implementation properly
* implements the hashCode/equals methods
* <ol>
*
* <p/>
*
* <b>Note for implementors</b>: When adding a FailureDetectorListener that
* has already been added, this should not add a second instance but should
* effectively be a no-op.
*
* @param failureDetectorListener FailureDetectorListener that receives
* events
*
* @see #removeFailureDetectorListener
*/
public void addFailureDetectorListener(FailureDetectorListener failureDetectorListener);
/**
* Removes a FailureDetectorListener instance from the event listener list.
*
* <p/>
*
* <b>Note for implementors</b>: When removing a FailureDetectorListener
* that has already been removed or was never in the list, this should not
* raise any errors but should effectively be a no-op.
*
* @param failureDetectorListener FailureDetectorListener that was receiving
* events
*
* @see #addFailureDetectorListener
*/
public void removeFailureDetectorListener(FailureDetectorListener failureDetectorListener);
/**
* Retrieves the FailureDetectorConfig instance with which this
* FailureDetector was constructed.
*
* @return FailureDetectorConfig
*/
public FailureDetectorConfig getConfig();
/**
* Returns the number of nodes that are considered to be available at the
* time of calling. Letting <code>n</code> = the results of
* <code>getNodeCount()</code>, the return value is bounded in the range
* <code>[0..n]</code>.
*
* @return Number of available nodes
*
* @see #getNodeCount()
*/
public int getAvailableNodeCount();
/**
* Returns the number of nodes that are in the set of all nodes at the time
* of calling.
*
* @return Number of nodes
*
* @see #getAvailableNodeCount()
*/
public int getNodeCount();
/**
* waitForAvailability causes the calling thread to block until the given
* Node is available. If the node is <i>already</i> available, this will
* simply return.
*
* @param node Node on which to wait
*
* @throws InterruptedException Thrown if the thread is interrupted
*/
public void waitForAvailability(Node node) throws InterruptedException;
/**
* Cleans up any open resources in preparation for shutdown.
*
* <p/>
*
* <b>Note for implementors</b>: After this method is called it is assumed
* that attempts to call the other methods will either silently fail, throw
* errors, or return stale information.
*/
public void destroy();
}