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.SettableFuture;
13 import java.util.List;
15 import java.util.Map.Entry;
16 import java.util.concurrent.ConcurrentHashMap;
17 import java.util.concurrent.Future;
18 import java.util.concurrent.ScheduledExecutorService;
19 import java.util.concurrent.ScheduledFuture;
20 import java.util.concurrent.TimeUnit;
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 {
37 private static final Logger LOG = LoggerFactory.getLogger(StalePassiveConnectionService.class);
39 private static Map<OvsdbClient, Map<OvsdbClient, SettableFuture<List<String>>>> pendingConnectionClients =
40 new ConcurrentHashMap<>();
42 private final ScheduledExecutorService executorService;
43 private static final int ECHO_TIMEOUT = 10;
45 public StalePassiveConnectionService(final ScheduledExecutorService executorService) {
46 this.executorService = executorService;
50 * This method makes sure that all stale connections from the same node are properly cleaned up before processing
51 * new connection request.
53 * @param newOvsdbClient the connecting OvsdbClient
54 * @param clientsFromSameNode list of existing OvsdbClients from the same node as the new OvsdbClient
56 public void handleNewPassiveConnection(final OvsdbClient newOvsdbClient,
57 final List<OvsdbClient> clientsFromSameNode) {
58 final Map<OvsdbClient, SettableFuture<List<String>>> clientFutureMap = new ConcurrentHashMap<>();
59 pendingConnectionClients.put(newOvsdbClient, clientFutureMap);
61 // scheduled task for ping response timeout. Connections that don't response to the
62 // ping or haven't disconnected after the timeout will be closed
63 final ScheduledFuture<?> echoTimeoutFuture =
64 executorService.schedule(() -> {
65 for (Entry<OvsdbClient, SettableFuture<List<String>>> entry : clientFutureMap.entrySet()) {
66 OvsdbClient client = entry.getKey();
67 Future<?> clientFuture = entry.getValue();
68 if (!clientFuture.isDone() && !clientFuture.isCancelled()) {
69 clientFuture.cancel(true);
71 if (client.isActive()) {
75 }, ECHO_TIMEOUT, TimeUnit.SECONDS);
77 // for every connection create a SettableFuture, save it to 'clientFutureMap', and send a ping (echo).
78 // The ping results in either:
79 // 1. ping response returns - the connection is active
80 // 2. the netty connection is closed due to IO exception -
81 // The future is removed from the 'clientFutureMap' when the onSuccess event for each future arrives
82 // If the map is empty we proceed with new connection process
83 for (final OvsdbClient client : clientsFromSameNode) {
84 SettableFuture<List<String>> clientFuture = SettableFuture.create();
85 clientFutureMap.put(client, clientFuture);
86 Futures.addCallback(clientFuture,
87 createStaleConnectionFutureCallback(client, newOvsdbClient, clientFutureMap, echoTimeoutFuture));
88 Futures.addCallback(client.echo(),
89 createStaleConnectionFutureCallback(client, newOvsdbClient, clientFutureMap, echoTimeoutFuture));
94 * Notify the service that the given client has disconnected.
95 * @param disconnectedClient the client just disconnected
97 public void clientDisconnected(OvsdbClient disconnectedClient) {
98 for (Entry<OvsdbClient, Map<OvsdbClient, SettableFuture<List<String>>>> entry :
99 pendingConnectionClients.entrySet()) {
100 OvsdbClient pendingClient = entry.getKey();
102 // set the future result for pending connections that wait for this client to be disconnected
103 if (pendingClient.getConnectionInfo().getRemoteAddress()
104 .equals(disconnectedClient.getConnectionInfo().getRemoteAddress())) {
105 Map<OvsdbClient, SettableFuture<List<String>>> clientFutureMap = entry.getValue();
106 SettableFuture<List<String>> future = clientFutureMap.get(disconnectedClient);
107 if (future != null) {
115 public void close() {
118 private FutureCallback<List<String>> createStaleConnectionFutureCallback(
119 final OvsdbClient cbForClient, final OvsdbClient newClient,
120 final Map<OvsdbClient, SettableFuture<List<String>>> clientFutureMap,
121 final ScheduledFuture<?> echoTimeoutFuture) {
122 return new FutureCallback<List<String>>() {
124 public void onSuccess(List<String> result) {
125 // The future is removed from the 'clientFutureMap' when the onSuccess event for each future arrives
126 // If the map is empty we proceed with new connection process
127 clientFutureMap.remove(cbForClient);
128 if (clientFutureMap.isEmpty()) {
129 if (!echoTimeoutFuture.isDone() && !echoTimeoutFuture.isCancelled()) {
130 echoTimeoutFuture.cancel(true);
132 OvsdbConnectionService.notifyListenerForPassiveConnection(newClient);
133 pendingConnectionClients.remove(newClient);
138 public void onFailure(Throwable throwable) {
139 LOG.error("Error in checking stale connections)", throwable);