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;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.List;
18 import java.util.Optional;
20 import java.util.concurrent.ConcurrentHashMap;
21 import java.util.function.Function;
23 import org.opendaylight.ovsdb.lib.OvsdbClient;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
28 * StalePassiveConnectionService provides functionalities to clean up stale passive connections
29 * from the same node before new connection request arrives, especially for connection flapping scenarios.
31 * <p>When new connection arrives all connections from the same node are pinged. The pings cause
32 * the stale netty connections to close due to IOException. Those have not been closed after a timeout
33 * will be closed programmatically. New connection request handling is then proceeded after all
34 * stale connections are cleaned up in the OvsdbConnectionService
36 * @author Vinh Nguyen (vinh.nguyen@hcl.com) on 6/10/16.
38 public class StalePassiveConnectionService implements AutoCloseable {
40 private static final Logger LOG = LoggerFactory.getLogger(StalePassiveConnectionService.class);
42 private final Map<OvsdbClient, Set<OvsdbClient>> pendingClients = new ConcurrentHashMap<>();
43 private final Function<OvsdbClient, Void> clientNotificationCallback;
45 public StalePassiveConnectionService(Function<OvsdbClient, Void> clientNotificationCallback) {
46 this.clientNotificationCallback = clientNotificationCallback;
49 public Map<OvsdbClient, Set<OvsdbClient>> getPendingClients() {
50 return new HashMap<>(pendingClients);
54 * This method makes sure that all stale connections from the same node are properly cleaned up before processing
55 * new connection request.
57 * @param newOvsdbClient the connecting OvsdbClient
58 * @param clientsFromSameNode list of existing OvsdbClients from the same node as the new OvsdbClient
60 public void handleNewPassiveConnection(final OvsdbClient newOvsdbClient,
61 final List<OvsdbClient> clientsFromSameNode) {
62 LOG.info("Adding client to pending list {}", newOvsdbClient.getConnectionInfo());
63 pendingClients.put(newOvsdbClient, new HashSet<>());
65 if old client echo succeeds
66 do not notify new client as it has to wait
68 if all old clients got disconnected/echo failed notify the new client
70 for (final OvsdbClient oldClient : clientsFromSameNode) {
71 pendingClients.get(newOvsdbClient).add(oldClient);
72 LOG.info("Echo testing client {}", oldClient.getConnectionInfo());
73 Futures.addCallback(oldClient.echo(),
74 new FutureCallback<List<String>>() {
76 public void onSuccess(List<String> result) {
77 //old client still active
78 LOG.info("Echo testing of old client {} succeeded", oldClient.getConnectionInfo());
82 public void onFailure(Throwable throwable) {
83 LOG.info("Echo testing of old client {} failed, disconnect and notify clients",
84 oldClient.getConnectionInfo());
85 //disconnect the old client to cleanup, so that new connection can proceed
86 oldClient.disconnect();
87 onInactiveClient(oldClient);
89 }, MoreExecutors.directExecutor());
94 * Notify the service that the given client has disconnected.
95 * @param disconnectedClient the client just disconnected
97 public synchronized void clientDisconnected(OvsdbClient disconnectedClient) {
98 LOG.info("Client disconnected {}", disconnectedClient.getConnectionInfo());
99 onInactiveClient(disconnectedClient);
102 public synchronized void onInactiveClient(OvsdbClient disconnectedClient) {
103 pendingClients.remove(disconnectedClient);
104 pendingClients.entrySet().stream().forEach(entry -> entry.getValue().remove(disconnectedClient));
105 Optional<OvsdbClient> clientOptional = pendingClients.entrySet().stream()
106 .filter(entry -> entry.getValue().isEmpty())
107 .map(entry -> entry.getKey())
109 if (clientOptional.isPresent()) {
110 LOG.info("Sending notification for client {}", clientOptional.get().getConnectionInfo());
111 pendingClients.remove(clientOptional.get());
112 clientNotificationCallback.apply(clientOptional.get());
117 public void close() {