2 * Copyright © 2016, 2017 NEC Corporation and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.ovsdb.lib.impl;
10 import com.google.common.util.concurrent.FutureCallback;
11 import com.google.common.util.concurrent.Futures;
12 import com.google.common.util.concurrent.MoreExecutors;
13 import java.util.HashMap;
14 import java.util.HashSet;
15 import java.util.List;
17 import java.util.Optional;
19 import java.util.concurrent.ConcurrentHashMap;
20 import java.util.function.Function;
21 import org.opendaylight.ovsdb.lib.OvsdbClient;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
26 * StalePassiveConnectionService provides functionalities to clean up stale passive connections
27 * from the same node before new connection request arrives, especially for connection flapping scenarios.
29 * <p>When new connection arrives all connections from the same node are pinged. The pings cause
30 * the stale netty connections to close due to IOException. Those have not been closed after a timeout
31 * will be closed programmatically. New connection request handling is then proceeded after all
32 * stale connections are cleaned up in the OvsdbConnectionService
34 * @author Vinh Nguyen (vinh.nguyen@hcl.com) on 6/10/16.
36 public class StalePassiveConnectionService implements AutoCloseable {
38 private static final Logger LOG = LoggerFactory.getLogger(StalePassiveConnectionService.class);
40 private final Map<OvsdbClient, Set<OvsdbClient>> pendingClients = new ConcurrentHashMap<>();
41 private final Function<OvsdbClient, Void> clientNotificationCallback;
43 public StalePassiveConnectionService(Function<OvsdbClient, Void> clientNotificationCallback) {
44 this.clientNotificationCallback = clientNotificationCallback;
47 public Map<OvsdbClient, Set<OvsdbClient>> getPendingClients() {
48 return new HashMap<>(pendingClients);
52 * This method makes sure that all stale connections from the same node are properly cleaned up before processing
53 * new connection request.
55 * @param newOvsdbClient the connecting OvsdbClient
56 * @param clientsFromSameNode list of existing OvsdbClients from the same node as the new OvsdbClient
58 public void handleNewPassiveConnection(final OvsdbClient newOvsdbClient,
59 final List<OvsdbClient> clientsFromSameNode) {
60 LOG.info("Adding client to pending list {}", newOvsdbClient.getConnectionInfo());
61 pendingClients.put(newOvsdbClient, new HashSet<>());
63 if old client echo succeeds
64 do not notify new client as it has to wait
66 if all old clients got disconnected/echo failed notify the new client
68 for (final OvsdbClient oldClient : clientsFromSameNode) {
69 pendingClients.get(newOvsdbClient).add(oldClient);
70 LOG.info("Echo testing client {}", oldClient.getConnectionInfo());
71 Futures.addCallback(oldClient.echo(),
72 new FutureCallback<List<String>>() {
74 public void onSuccess(List<String> result) {
75 //old client still active
76 LOG.info("Echo testing of old client {} succeeded", oldClient.getConnectionInfo());
80 public void onFailure(Throwable throwable) {
81 LOG.info("Echo testing of old client {} failed, disconnect and notify clients",
82 oldClient.getConnectionInfo());
83 //disconnect the old client to cleanup, so that new connection can proceed
84 oldClient.disconnect();
85 onInactiveClient(oldClient);
87 }, MoreExecutors.directExecutor());
92 * Notify the service that the given client has disconnected.
93 * @param disconnectedClient the client just disconnected
95 public synchronized void clientDisconnected(OvsdbClient disconnectedClient) {
96 LOG.info("Client disconnected {}", disconnectedClient.getConnectionInfo());
97 onInactiveClient(disconnectedClient);
100 public synchronized void onInactiveClient(OvsdbClient disconnectedClient) {
101 pendingClients.remove(disconnectedClient);
102 pendingClients.entrySet().stream().forEach(entry -> entry.getValue().remove(disconnectedClient));
103 Optional<OvsdbClient> clientOptional = pendingClients.entrySet().stream()
104 .filter(entry -> entry.getValue().isEmpty())
105 .map(entry -> entry.getKey())
107 if (clientOptional.isPresent()) {
108 LOG.info("Sending notification for client {}", clientOptional.get().getConnectionInfo());
109 pendingClients.remove(clientOptional.get());
110 clientNotificationCallback.apply(clientOptional.get());
115 public void close() {