com.datastax.driver.core.ConvictionPolicy Maven / Gradle / Ivy
/*
* Copyright DataStax, 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 com.datastax.driver.core;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import com.datastax.driver.core.policies.ReconnectionPolicy;
import java.util.concurrent.atomic.AtomicInteger;
/**
* The policy with which to decide whether a host should be considered down.
*
* TODO: this class is fully abstract (rather than an interface) because I'm not sure it's worth
* exposing (and if we do expose it, we need to expose ConnectionException). Maybe just exposing say
* a threshold of error before convicting a node is enough.
*/
abstract class ConvictionPolicy {
/**
* Called when new connections to the host are about to be created.
*
* @param count the number of connections
*/
abstract void signalConnectionsOpening(int count);
/** Called when a connection closed normally. */
abstract void signalConnectionClosed(Connection connection);
/**
* Called when a connection error occurs on a connection to the host this policy applies to.
*
* @return whether the host should be considered down.
*/
abstract boolean signalConnectionFailure(Connection connection, boolean decrement);
abstract boolean canReconnectNow();
abstract boolean hasActiveConnections();
/** Simple factory interface to allow creating {@link ConvictionPolicy} instances. */
interface Factory {
/**
* Creates a new ConvictionPolicy instance for {@code host}.
*
* @param host the host this policy applies to
* @return the newly created {@link ConvictionPolicy} instance.
*/
ConvictionPolicy create(Host host, ReconnectionPolicy reconnectionPolicy);
}
static class DefaultConvictionPolicy extends ConvictionPolicy {
private final Host host;
private final ReconnectionPolicy reconnectionPolicy;
private final AtomicInteger openConnections = new AtomicInteger();
private volatile long nextReconnectionTime = Long.MIN_VALUE;
private ReconnectionPolicy.ReconnectionSchedule reconnectionSchedule;
private DefaultConvictionPolicy(Host host, ReconnectionPolicy reconnectionPolicy) {
this.host = host;
this.reconnectionPolicy = reconnectionPolicy;
}
@Override
void signalConnectionsOpening(int count) {
int newTotal = openConnections.addAndGet(count);
Host.statesLogger.debug(
"[{}] preparing to open {} new connections, total = {}", host, count, newTotal);
resetReconnectionTime();
}
@Override
void signalConnectionClosed(Connection connection) {
int remaining = openConnections.decrementAndGet();
Host.statesLogger.debug("[{}] {} closed, remaining = {}", host, connection, remaining);
assert remaining >= 0;
}
@Override
boolean signalConnectionFailure(Connection connection, boolean decrement) {
int remaining;
if (decrement) {
if (host.state != Host.State.DOWN) updateReconnectionTime();
remaining = openConnections.decrementAndGet();
Host.statesLogger.debug("[{}] {} failed, remaining = {}", host, connection, remaining);
assert remaining >= 0;
} else {
remaining = openConnections.get();
}
return remaining == 0;
}
private synchronized void updateReconnectionTime() {
long now = System.nanoTime();
if (nextReconnectionTime > now)
// Someone else updated the time before us
return;
if (reconnectionSchedule == null) reconnectionSchedule = reconnectionPolicy.newSchedule();
long nextDelayMs = reconnectionSchedule.nextDelayMs();
Host.statesLogger.debug(
"[{}] preventing new connections for the next {} ms", host, nextDelayMs);
nextReconnectionTime = now + NANOSECONDS.convert(nextDelayMs, MILLISECONDS);
}
private synchronized void resetReconnectionTime() {
reconnectionSchedule = null;
nextReconnectionTime = Long.MIN_VALUE;
}
@Override
boolean canReconnectNow() {
boolean canReconnectNow =
nextReconnectionTime == Long.MIN_VALUE || System.nanoTime() >= nextReconnectionTime;
Host.statesLogger.trace("canReconnectNow={}", canReconnectNow);
return canReconnectNow;
}
@Override
boolean hasActiveConnections() {
return openConnections.get() > 0;
}
static class Factory implements ConvictionPolicy.Factory {
@Override
public ConvictionPolicy create(Host host, ReconnectionPolicy reconnectionPolicy) {
return new DefaultConvictionPolicy(host, reconnectionPolicy);
}
}
}
}