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 Apache Cassandra 1.2+ that works exclusively with the Cassandra Query Language version 3 (CQL3) and Cassandra's binary protocol.

The newest version!
/*
 *      Copyright (C) 2012 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 java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

import com.google.common.util.concurrent.Uninterruptibles;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.datastax.driver.core.exceptions.AuthenticationException;
import com.datastax.driver.core.policies.ReconnectionPolicy;

abstract class AbstractReconnectionHandler implements Runnable {

    private static final Logger logger = LoggerFactory.getLogger(AbstractReconnectionHandler.class);

    private final ScheduledExecutorService executor;
    private final ReconnectionPolicy.ReconnectionSchedule schedule;
    private final AtomicReference> currentAttempt;

    private volatile boolean readyForNext;
    private volatile ScheduledFuture localFuture;

    public AbstractReconnectionHandler(ScheduledExecutorService executor, ReconnectionPolicy.ReconnectionSchedule schedule, AtomicReference> currentAttempt) {
        this.executor = executor;
        this.schedule = schedule;
        this.currentAttempt = currentAttempt;
    }

    protected abstract Connection tryReconnect() throws ConnectionException, InterruptedException, UnsupportedProtocolVersionException;
    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 or unsupported protocol version error is unlikely to work
    protected boolean onAuthenticationException(AuthenticationException e, long nextDelayMs) { return false; }
    protected boolean onUnsupportedProtocolVersionException(UnsupportedProtocolVersionException e, long nextDelayMs) { return false; }

    public void start() {
        long firstDelay = schedule.nextDelayMs();
        logger.debug("First reconnection scheduled in {}ms", firstDelay);
        try {
            localFuture = executor.schedule(this, firstDelay, TimeUnit.MILLISECONDS);

            // If there a previous task, cancel it, so only one reconnection handler runs.
            while (true) {
                ScheduledFuture previous = currentAttempt.get();
                if (currentAttempt.compareAndSet(previous, localFuture)) {
                    if (previous != null)
                        previous.cancel(false);
                    break;
                }
            }
            readyForNext = true;
        } 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() {
        // We shouldn't arrive here if the future is cancelled but better safe than sorry
        if (localFuture.isCancelled())
            return;

        // Don't run before ready, otherwise our cancel business might end up removing all connection attempts.
        while (!readyForNext)
            Uninterruptibles.sleepUninterruptibly(5, TimeUnit.MILLISECONDS);

        try {
            onReconnection(tryReconnect());
            currentAttempt.compareAndSet(localFuture, null);
        } catch (ConnectionException e) {
            long nextDelay = schedule.nextDelayMs();
            if (onConnectionException(e, nextDelay))
                reschedule(nextDelay);
            else
                currentAttempt.compareAndSet(localFuture, null);
        } catch (AuthenticationException e) {
            logger.error(e.getMessage());
            long nextDelay = schedule.nextDelayMs();
            if (onAuthenticationException(e, nextDelay)) {
                reschedule(nextDelay);
            } else {
                logger.error("Retry against {} have been suspended. It won't be retried unless the node is restarted.", e.getHost());
                currentAttempt.compareAndSet(localFuture, null);
            }
        } catch (InterruptedException e) {
            // If interrupted, skip this attempt but still skip scheduling reconnections
            Thread.currentThread().interrupt();
            reschedule(schedule.nextDelayMs());
        } catch (UnsupportedProtocolVersionException e) {
            logger.error(e.getMessage());
            long nextDelay = schedule.nextDelayMs();
            if (onUnsupportedProtocolVersionException(e, nextDelay)) {
                reschedule(nextDelay);
            } else {
                logger.error("Retry against {} have been suspended. It won't be retried unless the node is restarted.", e.address);
                currentAttempt.compareAndSet(localFuture, null);
            }
        } catch (Exception e) {
            long nextDelay = schedule.nextDelayMs();
            if (onUnknownException(e, nextDelay))
                reschedule(nextDelay);
            else
                currentAttempt.compareAndSet(localFuture, null);
        }
    }

    private void reschedule(long nextDelay) {
        readyForNext = false;
        ScheduledFuture newFuture = executor.schedule(this, nextDelay, TimeUnit.MILLISECONDS);
        assert localFuture != null;
        // If it's not our future the current one, then we've been canceled
        if (!currentAttempt.compareAndSet(localFuture, newFuture)) {
            newFuture.cancel(false);
        }
        localFuture = newFuture;
        readyForNext = true;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy