org.opendaylight.sxp.route.ClusterSanityWatchdogInstance.java Source code

Java tutorial

Introduction

Here is the source code for org.opendaylight.sxp.route.ClusterSanityWatchdogInstance.java

Source

/*
 * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */

package org.opendaylight.sxp.route;

import com.google.common.collect.Sets;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningScheduledExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.annotation.Nullable;
import org.opendaylight.controller.config.yang.sxp.controller.conf.SxpControllerInstance;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonService;
import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceProvider;
import org.opendaylight.mdsal.singleton.common.api.ClusterSingletonServiceRegistration;
import org.opendaylight.mdsal.singleton.common.api.ServiceGroupIdentifier;
import org.opendaylight.sxp.controller.core.DatastoreAccess;
import org.opendaylight.sxp.route.core.FollowerSyncStatusTaskFactory;
import org.opendaylight.sxp.util.time.SxpTimerTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Purpose: provides workaround for closing {@link ClusterSingletonService}, if {@link ClusterSingletonServiceProvider} does not close them
 * TODO remove when cluster will always close its instances when switching
 */
public class ClusterSanityWatchdogInstance implements AutoCloseable, ClusterSingletonService {

    protected static final Logger LOG = LoggerFactory.getLogger(ClusterSanityWatchdogInstance.class);

    private final ListeningScheduledExecutorService scheduledExecutorService;
    private final FollowerSyncStatusTaskFactory taskFactory;
    private final AtomicBoolean isActive = new AtomicBoolean(false);
    private final Set<ClusterSingletonService> singletonServices = Sets.newConcurrentHashSet();
    private final DataBroker dataBroker;

    private DatastoreAccess datastoreAccess;
    private ClusterSingletonServiceRegistration clusterServiceRegistration;
    private ListenableFuture<Boolean> timer = Futures.immediateCancelledFuture();

    /**
     * @param broker                          service providing access to Datastore
     * @param clusterSingletonServiceProvider service used for registration
     * @param period                          period of watchdog feed
     * @param failLimit                       acceptable missed watchdog feeds
     */
    public ClusterSanityWatchdogInstance(final DataBroker broker,
            final ClusterSingletonServiceProvider clusterSingletonServiceProvider, final int period,
            final int failLimit) {
        LOG.info("Cluster sanity watchdog initiating ..");
        this.dataBroker = Objects.requireNonNull(broker);
        this.scheduledExecutorService = MoreExecutors.listeningDecorator(Executors.newScheduledThreadPool(1));
        taskFactory = new FollowerSyncStatusTaskFactory(period, failLimit);

        clusterServiceRegistration = Objects.requireNonNull(clusterSingletonServiceProvider)
                .registerClusterSingletonService(this);
        LOG.info("Cluster sanity watchdog initiated");
    }

    @Override
    public synchronized void close() throws Exception {
        if (clusterServiceRegistration != null) {
            clusterServiceRegistration.close();
            clusterServiceRegistration = null;
        } else {
            return;
        }
        scheduledExecutorService.shutdown();
        datastoreAccess.close();
        timer.cancel(true);
    }

    /**
     * @param task containing logic that determines if cluster is healthy
     */
    private synchronized void schedule(final SxpTimerTask<Boolean> task) {
        if (!timer.isDone()) {
            LOG.warn("double scheduling occurred!");
            return;
        }

        timer = scheduledExecutorService.schedule(Objects.requireNonNull(task), task.getPeriod(), TimeUnit.SECONDS);
        Futures.addCallback(timer, new FutureCallback<Boolean>() {

            @Override
            public void onSuccess(@Nullable final Boolean clusterHealthy) {
                if (clusterHealthy != null && clusterHealthy) {
                    if (isActive.get()) {
                        LOG.debug("Regular rescheduling of cluster health status task");
                        schedule(task);
                    }
                } else {
                    LOG.info("Detected cluster isolation condition");
                    singletonServices.forEach(ClusterSingletonService::closeServiceInstance);
                }
            }

            @Override
            public void onFailure(@Nullable final Throwable t) {
                LOG.info("Failed to track cluster health status", t);
                if (isActive.get() && !(t instanceof CancellationException)) {
                    LOG.debug("Rescheduling of cluster health status task after failure");
                    schedule(taskFactory.createFollowerSyncStatusTask(datastoreAccess));
                }
            }
        });
    }

    /**
     * Adds services that will be guarded by {@link ClusterSanityWatchdogInstance}
     *
     * @param services services that will be added
     */
    public void setServices(List<ClusterSingletonService> services) {
        singletonServices.addAll(Objects.requireNonNull(services));
    }

    @Override
    public synchronized void instantiateServiceInstance() {
        LOG.debug("Watched service woke up - firing scheduler");
        if (isActive.getAndSet(true)) {
            return;
        }
        datastoreAccess = DatastoreAccess.getInstance(dataBroker);
        schedule(taskFactory.createFollowerSyncStatusTask(datastoreAccess));
    }

    @Override
    public synchronized ListenableFuture<Void> closeServiceInstance() {
        LOG.debug("Watched service fall asleep - tearing down scheduler");
        datastoreAccess.close();
        timer.cancel(true);
        isActive.set(false);
        return Futures.immediateFuture(null);
    }

    @Override
    public ServiceGroupIdentifier getIdentifier() {
        return SxpControllerInstance.IDENTIFIER;
    }
}