/*
* 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 java.net.ConnectException;
import java.net.NoRouteToHostException;
import java.net.UnknownHostException;
import java.nio.channels.UnresolvedAddressException;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import voldemort.client.ClientConfig;
import voldemort.cluster.Cluster;
import voldemort.cluster.Node;
import voldemort.server.VoldemortConfig;
import voldemort.utils.SystemTime;
import voldemort.utils.Time;
import voldemort.utils.Utils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
/**
* FailureDetectorConfig simply holds all the data that was available to it upon
* construction. A FailureDetectorConfig is usually passed to
* {@link FailureDetectorUtils}'s {@link FailureDetectorUtils#create create}
* method to create a full-blown {@link FailureDetector} instance.
*
*/
public class FailureDetectorConfig {
public static final String DEFAULT_IMPLEMENTATION_CLASS_NAME = ThresholdFailureDetector.class.getName();
public static final long DEFAULT_BANNAGE_PERIOD = 30000;
public static final long DEFAULT_THRESHOLD_INTERVAL = 300000;
public static final int DEFAULT_THRESHOLD = 95;
public static final int DEFAULT_THRESHOLD_COUNT_MINIMUM = 30;
public static final long DEFAULT_ASYNC_RECOVERY_INTERVAL = 10000;
public static final List<String> DEFAULT_CATASTROPHIC_ERROR_TYPES = ImmutableList.of(ConnectException.class.getName(),
UnknownHostException.class.getName(),
NoRouteToHostException.class.getName(),
UnresolvedAddressException.class.getName());
public static final long DEFAULT_REQUEST_LENGTH_THRESHOLD = 5000;
public static final int DEFAULT_MAX_TOLERABLE_FATAL_FAILURES = 10;
protected String implementationClassName = DEFAULT_IMPLEMENTATION_CLASS_NAME;
protected long bannagePeriod = DEFAULT_BANNAGE_PERIOD;
protected int threshold = DEFAULT_THRESHOLD;
protected int thresholdCountMinimum = DEFAULT_THRESHOLD_COUNT_MINIMUM;
protected long thresholdInterval = DEFAULT_THRESHOLD_INTERVAL;
protected long asyncRecoveryInterval = DEFAULT_ASYNC_RECOVERY_INTERVAL;
protected List<String> catastrophicErrorTypes = DEFAULT_CATASTROPHIC_ERROR_TYPES;
protected long requestLengthThreshold = DEFAULT_REQUEST_LENGTH_THRESHOLD;
protected Collection<Node> nodes;
protected ConnectionVerifier connectionVerifier;
protected Time time = SystemTime.INSTANCE;
protected int maximumTolerableFatalFailures = DEFAULT_MAX_TOLERABLE_FATAL_FAILURES;
// TODO: athirupa, this is messy. Cluster definition changes because of
// adding/removing zones or nodes. Having a direct reference the object will
// be stale it should have a reference to the interface which can return
// cluster, the reference should be authority for the cluster on both server
// (MetadataStore) and client (AbstractStoreClientFactory) . But it involved
// changing too much code and touching many files, saving it for later.
private Cluster cluster = null;
/**
* Constructs a new FailureDetectorConfig using all the defaults. This is
* usually used in the case of unit tests.
*
* <p/>
*
* <b>Note</b>: the {@link #setNodes(Collection)} and
* {@link #setConnectionVerifier(ConnectionVerifier)} methods must be called to ensure
* <i>complete</i> configuration.
*/
public FailureDetectorConfig() {
}
/**
* Constructs a new FailureDetectorConfig from a server perspective (via
* {@link VoldemortConfig}).
*
* <p/>
*
* <b>Note</b>: the {@link #setNodes(Collection)} and
* {@link #setConnectionVerifier(ConnectionVerifier)} methods must be called to ensure
* <i>complete</i> configuration.
*
* @param config {@link VoldemortConfig} instance
*/
public FailureDetectorConfig(VoldemortConfig config) {
setImplementationClassName(config.getFailureDetectorImplementation());
setBannagePeriod(config.getFailureDetectorBannagePeriod());
setThreshold(config.getFailureDetectorThreshold());
setThresholdCountMinimum(config.getFailureDetectorThresholdCountMinimum());
setThresholdInterval(config.getFailureDetectorThresholdInterval());
setAsyncRecoveryInterval(config.getFailureDetectorAsyncRecoveryInterval());
setCatastrophicErrorTypes(config.getFailureDetectorCatastrophicErrorTypes());
setRequestLengthThreshold(config.getFailureDetectorRequestLengthThreshold());
setMaximumTolerableFatalFailures(DEFAULT_MAX_TOLERABLE_FATAL_FAILURES);
}
/**
* Constructs a new FailureDetectorConfig from a client perspective (via
* {@link ClientConfig}).
*
* <p/>
*
* <b>Note</b>: the {@link #setNodes(Collection)} and
* {@link #setConnectionVerifier(ConnectionVerifier)} methods must be called to ensure
* <i>complete</i> configuration.
*
* @param config {@link ClientConfig} instance
*/
public FailureDetectorConfig(ClientConfig config) {
setImplementationClassName(config.getFailureDetectorImplementation());
setBannagePeriod(config.getFailureDetectorBannagePeriod());
setThreshold(config.getFailureDetectorThreshold());
setThresholdCountMinimum(config.getFailureDetectorThresholdCountMinimum());
setThresholdInterval(config.getFailureDetectorThresholdInterval());
setAsyncRecoveryInterval(config.getFailureDetectorAsyncRecoveryInterval());
setCatastrophicErrorTypes(config.getFailureDetectorCatastrophicErrorTypes());
setRequestLengthThreshold(config.getFailureDetectorRequestLengthThreshold());
setMaximumTolerableFatalFailures(config.getMaximumTolerableFatalFailures());
}
/**
* Returns the fully-qualified class name of the FailureDetector
* implementation.
*
* @return Class name to instantiate for the FailureDetector
*
* @see VoldemortConfig#getFailureDetectorImplementation
* @see ClientConfig#getFailureDetectorImplementation
*/
public String getImplementationClassName() {
return implementationClassName;
}
/**
* Assigns the fully-qualified class name of the FailureDetector
* implementation.
*
* @param implementationClassName Class name to instantiate for the
* FailureDetector
*
* @see VoldemortConfig#getFailureDetectorImplementation
* @see ClientConfig#getFailureDetectorImplementation
*/
public FailureDetectorConfig setImplementationClassName(String implementationClassName) {
this.implementationClassName = Utils.notNull(implementationClassName);
return this;
}
/**
* Returns the node bannage period (in milliseconds) as defined by the
* client or server configuration. Some FailureDetector implementations wait
* for a specified period of time before attempting to access the node again
* once it has become unavailable.
*
* <p/>
*
* <b>Note</b>: this is only used by the
* {@link BannagePeriodFailureDetector} implementation.
*
* @return Period of bannage of a node, in milliseconds
*
* @see BannagePeriodFailureDetector
* @see VoldemortConfig#getFailureDetectorBannagePeriod
* @see ClientConfig#getFailureDetectorBannagePeriod
*/
public long getBannagePeriod() {
return bannagePeriod;
}
/**
* Assigns the node bannage period (in milliseconds) as defined by the
* client or server configuration. Some FailureDetector implementations wait
* for a specified period of time before attempting to access the node again
* once it has become unavailable.
*
* <p/>
*
* <b>Note</b>: this is only used by the
* {@link BannagePeriodFailureDetector} implementation.
*
* @param bannagePeriod Period of bannage of a node, in milliseconds
*
* @see BannagePeriodFailureDetector
* @see VoldemortConfig#getFailureDetectorBannagePeriod
* @see ClientConfig#getFailureDetectorBannagePeriod
*/
public FailureDetectorConfig setBannagePeriod(long bannagePeriod) {
this.bannagePeriod = bannagePeriod;
return this;
}
/**
* Returns the success threshold percentage with an integer value between 0
* and 100. Some FailureDetector implementations will mark a node as
* unavailable if the ratio of successes to total requests for that node
* falls under this threshold.
*
* <p/>
*
* <b>Note</b>: this is only used by the {@link ThresholdFailureDetector}
* implementation.
*
* @return Integer percentage representing success threshold
*
* @see ThresholdFailureDetector
* @see VoldemortConfig#getFailureDetectorThreshold
* @see ClientConfig#getFailureDetectorThreshold
*/
public int getThreshold() {
return threshold;
}
/**
* Assigns the success threshold percentage with an integer value between 0
* and 100. Some FailureDetector implementations will mark a node as
* unavailable if the ratio of successes to total requests for that node
* falls under this threshold.
*
* <p/>
*
* <b>Note</b>: this is only used by the {@link ThresholdFailureDetector}
* implementation.
*
* @param threshold Integer percentage representing success threshold
*
* @exception IllegalArgumentException Thrown if the threshold parameter is
* outside the range [0..100]
*
* @see ThresholdFailureDetector
* @see VoldemortConfig#getFailureDetectorThreshold
* @see ClientConfig#getFailureDetectorThreshold
*/
public FailureDetectorConfig setThreshold(int threshold) {
if(threshold < 0 || threshold > 100)
throw new IllegalArgumentException("threshold must be in the range (0..100)");
this.threshold = threshold;
return this;
}
/**
* Returns the minimum number of failures that must occur before the success
* ratio is calculated to compare against the success threshold percentage.
*
* <p/>
*
* <b>Note</b>: this is only used by the {@link ThresholdFailureDetector}
* implementation.
*
* @return Integer representing the minimum number of failures (per node)
* that must be processed before the threshold is checked
*
* @see ThresholdFailureDetector
* @see VoldemortConfig#getFailureDetectorThreshold
* @see ClientConfig#getFailureDetectorThreshold
*/
public int getThresholdCountMinimum() {
return thresholdCountMinimum;
}
/**
* Assigns the minimum number of failures that must occur before the success
* ratio is calculated to compare against the success threshold percentage.
*
* <p/>
*
* <b>Note</b>: this is only used by the {@link ThresholdFailureDetector}
* implementation.
*
* @param thresholdCountMinimum Integer representing the minimum number of
* failures (per node) that must be processed before the threshold is
* checked
*
* @exception IllegalArgumentException Thrown if the thresholdCountMinimum
* parameter is outside the range [0..Integer.MAX_VALUE]
*
* @see ThresholdFailureDetector
* @see VoldemortConfig#getFailureDetectorThreshold
* @see ClientConfig#getFailureDetectorThreshold
*/
public FailureDetectorConfig setThresholdCountMinimum(int thresholdCountMinimum) {
if(thresholdCountMinimum < 0)
throw new IllegalArgumentException("thresholdCountMinimum must be greater than or equal to 0");
this.thresholdCountMinimum = thresholdCountMinimum;
return this;
}
/**
* Returns the interval of time for each the success ratio is valid. After
* this number of milliseconds passes, a new interval is started and all
* internal state of a node is cleared out. However, it may not necessarily
* be marked as available if it was unavailable in the previous interval.
*
* <p/>
*
* <b>Note</b>: this is only used by the {@link ThresholdFailureDetector}
* implementation.
*
* @return Millisecond interval for the success ratio
*
* @see ThresholdFailureDetector
* @see VoldemortConfig#getFailureDetectorThresholdInterval
* @see ClientConfig#getFailureDetectorThresholdInterval
*/
public long getThresholdInterval() {
return thresholdInterval;
}
/**
* Assigns the interval of time for each the success ratio is valid. After
* this number of milliseconds passes, a new interval is started and all
* internal state of a node is cleared out. However, it may not necessarily
* be marked as available if it was unavailable in the previous interval.
*
* <p/>
*
* <b>Note</b>: this is only used by the {@link ThresholdFailureDetector}
* implementation.
*
* @param thresholdInterval Millisecond interval for the success ratio
*
* @exception IllegalArgumentException Thrown if the thresholdInterval
* parameter is less than or equal to 0
*
* @see ThresholdFailureDetector
* @see VoldemortConfig#getFailureDetectorThresholdInterval
* @see ClientConfig#getFailureDetectorThresholdInterval
*/
public FailureDetectorConfig setThresholdInterval(long thresholdInterval) {
if(thresholdInterval <= 0)
throw new IllegalArgumentException("thresholdInterval must be greater than 0");
this.thresholdInterval = thresholdInterval;
return this;
}
/**
* Returns the interval of time (in milliseconds) that the thread will wait
* before checking if a given node has recovered.
*
* <p/>
*
* <b>Note</b>: this is only used by the
* {@link AsyncRecoveryFailureDetector} and {@link ThresholdFailureDetector}
* implementations.
*
* @return Integer representing the millisecond interval for the success
* ratio
*
* @see AsyncRecoveryFailureDetector
* @see ThresholdFailureDetector
* @see VoldemortConfig#getFailureDetectorAsyncRecoveryInterval
* @see ClientConfig#getFailureDetectorAsyncRecoveryInterval
*/
public long getAsyncRecoveryInterval() {
return asyncRecoveryInterval;
}
/**
* Assigns the interval of time (in milliseconds) that the thread will wait
* before checking if a given node has recovered.
*
* <p/>
*
* <b>Note</b>: this is only used by the
* {@link AsyncRecoveryFailureDetector} and {@link ThresholdFailureDetector}
* implementations.
*
* @param asyncRecoveryInterval Number of milliseconds to wait between
* recovery attempts
*
* @exception IllegalArgumentException Thrown if the thresholdInterval
* parameter is less than or equal to 0
*
* @see AsyncRecoveryFailureDetector
* @see ThresholdFailureDetector
* @see VoldemortConfig#getFailureDetectorAsyncRecoveryInterval
* @see ClientConfig#getFailureDetectorAsyncRecoveryInterval
*/
public FailureDetectorConfig setAsyncRecoveryInterval(long asyncRecoveryInterval) {
if(asyncRecoveryInterval <= 0)
throw new IllegalArgumentException("asyncRecoveryInterval must be greater than 0");
this.asyncRecoveryInterval = asyncRecoveryInterval;
return this;
}
/**
* Returns the list of Java Exception types that are considered
* catastrophic. Some FailureDetector implementations may not mark a given
* node as unavailable on each and every call to recordException. Instead
* they may apply logic to determine if such an exception should cause the
* node to be marked as unavailable. However, the list of so-called
* catastrophic errors provides such FailureDetector implementations a hint
* that receipt of such errors should cause the node to be marked as
* unavailable immediately, regardless of other logic.
*
* <p/>
*
* <b>Note</b>: this is only used by the {@link ThresholdFailureDetector}
* implementation.
*
* @return List of fully-qualified Java Exception class names against which
* to check the exception provided to recordException; this list
* should be immutable
*
* @see ThresholdFailureDetector
* @see VoldemortConfig#getFailureDetectorCatastrophicErrorTypes
* @see ClientConfig#getFailureDetectorCatastrophicErrorTypes
*/
public List<String> getCatastrophicErrorTypes() {
return catastrophicErrorTypes;
}
/**
* Assigns the list of Java Exception types that are considered
* catastrophic. Some FailureDetector implementations may not mark a given
* node as unavailable on each and every call to recordException. Instead
* they may apply logic to determine if such an exception should cause the
* node to be marked as unavailable. However, the list of so-called
* catastrophic errors provides such FailureDetector implementations a hint
* that receipt of such errors should cause the node to be marked as
* unavailable immediately, regardless of other logic.
*
* <p/>
*
* <b>Note</b>: this is only used by the {@link ThresholdFailureDetector}
* implementation.
*
* @param catastrophicErrorTypes List of fully-qualified Java Exception
* class names against which to check the exception provided to
* recordException; this list should be immutable and non-null
*
* @see ThresholdFailureDetector
* @see VoldemortConfig#getFailureDetectorCatastrophicErrorTypes
* @see ClientConfig#getFailureDetectorCatastrophicErrorTypes
*/
public FailureDetectorConfig setCatastrophicErrorTypes(List<String> catastrophicErrorTypes) {
this.catastrophicErrorTypes = Utils.notNull(catastrophicErrorTypes);
return this;
}
/**
* Returns the maximum time (in milliseconds) that a request (get, put,
* delete, etc.) can take before a given <i>successful</i> event is
* considered as a failure because the requests are--while
* successful--considered to be taking too long to complete.
*
* <p/>
*
* <b>Note</b>: this is only used by the {@link ThresholdFailureDetector}
* implementation.
*
* @return Number of milliseconds representing maximum amount of time the
* request should take before being considered as a failure
*
* @see ThresholdFailureDetector
* @see VoldemortConfig#getFailureDetectorRequestLengthThreshold
* @see ClientConfig#getFailureDetectorRequestLengthThreshold
*/
public long getRequestLengthThreshold() {
return requestLengthThreshold;
}
/**
* Assigns the value for the maximum time (in milliseconds) that a request
* (get, put, delete, etc.) can take before a given <i>successful</i> event
* is considered as a failure because the requests are--while
* successful--considered to be taking too long to complete.
*
* <p/>
*
* <b>Note</b>: this is only used by the {@link ThresholdFailureDetector}
* implementation.
*
* @param requestLengthThreshold Number of milliseconds representing maximum
* amount of time the request should take before being considered as
* a failure
*
* @exception IllegalArgumentException Thrown if the requestLengthThreshold
* parameter is less than 0
*
* @see ThresholdFailureDetector
* @see VoldemortConfig#getFailureDetectorRequestLengthThreshold
* @see ClientConfig#getFailureDetectorRequestLengthThreshold
*/
public FailureDetectorConfig setRequestLengthThreshold(long requestLengthThreshold) {
if(requestLengthThreshold <= 0)
throw new IllegalArgumentException("requestLengthThreshold must be positive");
this.requestLengthThreshold = requestLengthThreshold;
return this;
}
/**
* Returns a reference to the cluster object
*
* @return Cluster object which determines the source of truth for the
* topology
*/
// Look at the comments on cluster variable to see why this is problematic
public Cluster getCluster() {
return this.cluster;
}
/**
* Assigns a cluster which determines the source of truth for the topology
*
* @param cluster The Cluster object retrieved during bootstrap; must be
* non-null
*/
// Look at the comments on cluster variable to see why this is problematic
public FailureDetectorConfig setCluster(Cluster cluster) {
Utils.notNull(cluster);
this.cluster = cluster;
/*
* FIXME: this is the hacky way to refresh the admin connection
* verifier, but it'll just work. The clean way to do so is to have a
* centralized metadata management, and all references of cluster object
* point to that.
*/
if(this.connectionVerifier instanceof AdminConnectionVerifier) {
((AdminConnectionVerifier) connectionVerifier).setCluster(cluster);
}
return this;
}
/**
* Returns a list of nodes in the cluster represented by this failure
* detector configuration.
*
* @return Collection of Node instances, usually determined from the Cluster
*/
@Deprecated
public synchronized Collection<Node> getNodes() {
return ImmutableSet.copyOf(this.cluster.getNodes());
}
/**
* Assigns a list of nodes in the cluster represented by this failure
* detector configuration.
*
* @param nodes Collection of Node instances, usually determined from the
* Cluster; must be non-null
*/
@Deprecated
public synchronized FailureDetectorConfig setNodes(Collection<Node> nodes) {
Utils.notNull(nodes);
this.nodes = new HashSet<Node>(nodes);
return this;
}
public synchronized void addNode(Node node) {
Utils.notNull(node);
nodes.add(node);
}
public synchronized void removeNode(Node node) {
Utils.notNull(node);
nodes.remove(node);
}
public ConnectionVerifier getConnectionVerifier() {
return connectionVerifier;
}
public FailureDetectorConfig setConnectionVerifier(ConnectionVerifier verifier) {
this.connectionVerifier = Utils.notNull(verifier);
return this;
}
public Time getTime() {
return time;
}
public FailureDetectorConfig setTime(Time time) {
this.time = Utils.notNull(time);
return this;
}
/**
* Sets the maximum number of Fatal failures (connectivity failures)
* acceptable before the node is marked as unavailable (in case of
* ThresholdFailureDetector).
*
* @param maximumTolerableFatalFailures #fatal failures acceptable before
* node is marked as unavailable
*/
public FailureDetectorConfig setMaximumTolerableFatalFailures(int maximumTolerableFatalFailures) {
if(maximumTolerableFatalFailures <= 0) {
throw new IllegalArgumentException(" Catastrophic error limit should be greater than zero. Current value "
+ maximumTolerableFatalFailures);
}
this.maximumTolerableFatalFailures = maximumTolerableFatalFailures;
return this;
}
/**
* @return #fatal failures acceptable before node is marked as unavailable
*/
public int getMaximumTolerableFatalFailures() {
return maximumTolerableFatalFailures;
}
}