All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.datastax.driver.core.AbstractReconnectionHandler Maven / Gradle / Ivy

Go to download

A driver for DataStax Enterprise (DSE) and Apache Cassandra 1.2+ clusters that works exclusively with the Cassandra Query Language version 3 (CQL3) and Cassandra's binary protocol, supporting DSE-specific features such as geospatial types, DSE Graph and DSE authentication.

There is a newer version: 2.4.0
Show newest version
/*
 *      Copyright (C) 2012-2017 DataStax Inc.
 *
 *      This software can be used solely with DataStax Enterprise. Please consult the license at
 *      http://www.datastax.com/terms/datastax-dse-driver-license-terms
 */
package com.datastax.driver.core;

import com.datastax.driver.core.exceptions.AuthenticationException;
import com.datastax.driver.core.exceptions.ConnectionException;
import com.datastax.driver.core.exceptions.UnsupportedProtocolVersionException;
import com.datastax.driver.core.policies.ReconnectionPolicy;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.util.concurrent.AbstractFuture;
import com.google.common.util.concurrent.ListenableFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Manages periodic reconnection attempts after a host has been marked down.
 * 

* Concurrent attempts are handled via the {@link #currentAttempt} reference passed to the constructor. * For a given reference, only one handler will run at a given time. Additional handlers will cancel * themselves if they find a previous handler running. *

* This class is designed for concurrency, but instances must not be shared: each thread creates and * starts its own private handler, all interactions happen through {@link #currentAttempt}. */ abstract class AbstractReconnectionHandler implements Runnable { private static final Logger logger = LoggerFactory.getLogger(AbstractReconnectionHandler.class); private final String name; private final ScheduledExecutorService executor; private final ReconnectionPolicy.ReconnectionSchedule schedule; /** * The future that is exposed to clients, representing completion of the current active handler */ private final AtomicReference> currentAttempt; @VisibleForTesting final HandlerFuture handlerFuture = new HandlerFuture(); private final long initialDelayMs; private final CountDownLatch ready = new CountDownLatch(1); public AbstractReconnectionHandler(String name, ScheduledExecutorService executor, ReconnectionPolicy.ReconnectionSchedule schedule, AtomicReference> currentAttempt) { this(name, executor, schedule, currentAttempt, -1); } public AbstractReconnectionHandler(String name, ScheduledExecutorService executor, ReconnectionPolicy.ReconnectionSchedule schedule, AtomicReference> currentAttempt, long initialDelayMs) { this.name = name; this.executor = executor; this.schedule = schedule; this.currentAttempt = currentAttempt; this.initialDelayMs = initialDelayMs; } protected abstract Connection tryReconnect() throws ConnectionException, InterruptedException, UnsupportedProtocolVersionException, ClusterNameMismatchException; protected abstract void onReconnection(Connection connection); protected boolean onConnectionException(ConnectionException e, long nextDelayMs) { return true; } protected boolean onUnknownException(Exception e, long nextDelayMs) { return true; } // Retrying on authentication errors makes sense for applications that can update the credentials at runtime, we don't want to force them // to restart. protected boolean onAuthenticationException(AuthenticationException e, long nextDelayMs) { return true; } // Retrying on these errors is unlikely to work protected boolean onUnsupportedProtocolVersionException(UnsupportedProtocolVersionException e, long nextDelayMs) { return false; } protected boolean onClusterNameMismatchException(ClusterNameMismatchException e, long nextDelayMs) { return false; } public void start() { long firstDelay = (initialDelayMs >= 0) ? initialDelayMs : schedule.nextDelayMs(); logger.debug("First reconnection scheduled in {}ms", firstDelay); try { handlerFuture.nextTry = executor.schedule(this, firstDelay, TimeUnit.MILLISECONDS); while (true) { ListenableFuture previous = currentAttempt.get(); if (previous != null && !previous.isCancelled()) { logger.debug("Found another already active handler, cancelling"); handlerFuture.cancel(false); break; } if (currentAttempt.compareAndSet(previous, handlerFuture)) { Host.statesLogger.debug("[{}] starting reconnection attempt", name); break; } } ready.countDown(); } catch (RejectedExecutionException e) { // The executor has been shutdown, fair enough, just ignore logger.debug("Aborting reconnection handling since the cluster is shutting down"); } } @Override public void run() { // Just make sure we don't start the first try too fast, in case we find out in start() that we need to cancel ourselves try { ready.await(); } catch (InterruptedException e) { // This can happen at shutdown Thread.currentThread().interrupt(); return; } if (handlerFuture.isCancelled()) { logger.debug("Got cancelled, stopping"); return; } try { onReconnection(tryReconnect()); handlerFuture.markAsDone(); currentAttempt.compareAndSet(handlerFuture, null); logger.debug("Reconnection successful, cleared the future"); } catch (ConnectionException e) { long nextDelay = schedule.nextDelayMs(); if (onConnectionException(e, nextDelay)) reschedule(nextDelay); else currentAttempt.compareAndSet(handlerFuture, null); } catch (AuthenticationException e) { logger.error(e.getMessage()); long nextDelay = schedule.nextDelayMs(); if (onAuthenticationException(e, nextDelay)) { reschedule(nextDelay); } else { logger.error("Retries against {} have been suspended. It won't be retried unless the node is restarted.", e.getHost()); currentAttempt.compareAndSet(handlerFuture, null); } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } catch (UnsupportedProtocolVersionException e) { logger.error(e.getMessage()); long nextDelay = schedule.nextDelayMs(); if (onUnsupportedProtocolVersionException(e, nextDelay)) { reschedule(nextDelay); } else { logger.error("Retries against {} have been suspended. It won't be retried unless the node is restarted.", e.getHost()); currentAttempt.compareAndSet(handlerFuture, null); } } catch (ClusterNameMismatchException e) { logger.error(e.getMessage()); long nextDelay = schedule.nextDelayMs(); if (onClusterNameMismatchException(e, nextDelay)) { reschedule(nextDelay); } else { logger.error("Retries against {} have been suspended. It won't be retried unless the node is restarted.", e.address.getAddress()); currentAttempt.compareAndSet(handlerFuture, null); } } catch (Exception e) { long nextDelay = schedule.nextDelayMs(); if (onUnknownException(e, nextDelay)) reschedule(nextDelay); else currentAttempt.compareAndSet(handlerFuture, null); } } private void reschedule(long nextDelay) { // If we got cancelled during the failed reconnection attempt that lead here, don't reschedule if (handlerFuture.isCancelled()) { currentAttempt.compareAndSet(handlerFuture, null); return; } Host.statesLogger.debug("[{}] next reconnection attempt in {} ms", name, nextDelay); handlerFuture.nextTry = executor.schedule(this, nextDelay, TimeUnit.MILLISECONDS); } // The future that the handler exposes to its clients via currentAttempt @VisibleForTesting static class HandlerFuture extends AbstractFuture { // A future representing completion of the next task submitted to the executor volatile ScheduledFuture nextTry; @Override public boolean cancel(boolean mayInterruptIfRunning) { // This is a check-then-act, so we may race with the scheduling of the first try, but in that case // we'll re-check for cancellation when this first try starts running if (nextTry != null) { nextTry.cancel(mayInterruptIfRunning); } return super.cancel(mayInterruptIfRunning); } void markAsDone() { super.set(null); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy