org.opendaylight.ovsdb.lib.impl.StalePassiveConnectionService Maven / Gradle / Ivy
/*
* Copyright © 2016, 2017 NEC Corporation 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.ovsdb.lib.impl;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import org.opendaylight.ovsdb.lib.OvsdbClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* StalePassiveConnectionService provides functionalities to clean up stale passive connections
* from the same node before new connection request arrives, especially for connection flapping scenarios.
*
* When new connection arrives all connections from the same node are pinged. The pings cause
* the stale netty connections to close due to IOException. Those have not been closed after a timeout
* will be closed programmatically. New connection request handling is then proceeded after all
* stale connections are cleaned up in the OvsdbConnectionService
*
* @author Vinh Nguyen ([email protected]) on 6/10/16.
*/
public class StalePassiveConnectionService implements AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(StalePassiveConnectionService.class);
private final Map> pendingClients = new ConcurrentHashMap<>();
private final Function clientNotificationCallback;
public StalePassiveConnectionService(Function clientNotificationCallback) {
this.clientNotificationCallback = clientNotificationCallback;
}
public Map> getPendingClients() {
return new HashMap<>(pendingClients);
}
/**
* This method makes sure that all stale connections from the same node are properly cleaned up before processing
* new connection request.
*
* @param newOvsdbClient the connecting OvsdbClient
* @param clientsFromSameNode list of existing OvsdbClients from the same node as the new OvsdbClient
*/
public void handleNewPassiveConnection(final OvsdbClient newOvsdbClient,
final List clientsFromSameNode) {
LOG.info("Adding client to pending list {}", newOvsdbClient.getConnectionInfo());
pendingClients.put(newOvsdbClient, new HashSet<>());
/*
if old client echo succeeds
do not notify new client as it has to wait
else
if all old clients got disconnected/echo failed notify the new client
*/
for (final OvsdbClient oldClient : clientsFromSameNode) {
pendingClients.get(newOvsdbClient).add(oldClient);
LOG.info("Echo testing client {}", oldClient.getConnectionInfo());
Futures.addCallback(oldClient.echo(),
new FutureCallback>() {
@Override
public void onSuccess(List result) {
//old client still active
LOG.info("Echo testing of old client {} succeeded", oldClient.getConnectionInfo());
}
@Override
public void onFailure(Throwable throwable) {
LOG.info("Echo testing of old client {} failed, disconnect and notify clients",
oldClient.getConnectionInfo());
//disconnect the old client to cleanup, so that new connection can proceed
oldClient.disconnect();
onInactiveClient(oldClient);
}
}, MoreExecutors.directExecutor());
}
}
/**
* Notify the service that the given client has disconnected.
* @param disconnectedClient the client just disconnected
*/
public synchronized void clientDisconnected(OvsdbClient disconnectedClient) {
LOG.info("Client disconnected {}", disconnectedClient.getConnectionInfo());
onInactiveClient(disconnectedClient);
}
public synchronized void onInactiveClient(OvsdbClient disconnectedClient) {
pendingClients.remove(disconnectedClient);
pendingClients.entrySet().stream().forEach(entry -> entry.getValue().remove(disconnectedClient));
Optional clientOptional = pendingClients.entrySet().stream()
.filter(entry -> entry.getValue().isEmpty())
.map(entry -> entry.getKey())
.findFirst();
if (clientOptional.isPresent()) {
OvsdbClient client = clientOptional.orElseThrow();
LOG.info("Sending notification for client {}", client.getConnectionInfo());
pendingClients.remove(client);
clientNotificationCallback.apply(client);
}
}
@Override
public void close() {
}
}