Bug-6662: On connection reset by peer, sometimes re-connection attempt stops after... 19/45519/1
authorAjay <ajayl.bro@gmail.com>
Tue, 13 Sep 2016 02:16:23 +0000 (02:16 +0000)
committerAjay <ajayl.bro@gmail.com>
Tue, 13 Sep 2016 02:18:01 +0000 (02:18 +0000)
This issue happens when the neighbor config is removed from peer router while retaining the BGP router config.
Under this scenario, since BGP router config is still present, router is listening on BGP port. So when ODL
tries to connect, the connection succeeds. However the BGP negotiation fails as neighbor config has been removed
from router.

BGPReconnectPromise.ClosedChannelHandler#channelInactive triggers the reconnect, but the actual connection
retries as per the connection stragegy are done from BGPProtocolSessionPromise.BootstrapConnectListener#operationComplete

In failure scenario, since connection succeeds but negotiation fails, failure is not detected in BGPProtocolSessionPromise.BootstrapConnectListener#operationComplete
and so no reconnection attempts are made.

When negotiation fails, AbstractBGPSessionNegotiator#negotiationFailedCloseChannel is called, which causes BGPReconnectPromise.ClosedChannelHandler#channelInactive
to get called. Fix involves detecting negotiation failure here, and calling BGPProtocolSessionPromise#reconnect.

The scheduled job in AbstractBGPSessionNegotiator to detect hold-timer expiry during negotiation is also being cancelled
from AbstractBGPSessionNegotiator#negotiationFailedCloseChannel as the job is not needed when channel is closed.

Change-Id: Ica4add08bc5e9e44f1321d139a56cf9157cfdd69
Signed-off-by: Ajay <ajayl.bro@gmail.com>
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/AbstractBGPSessionNegotiator.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/protocol/BGPProtocolSessionPromise.java
bgp/rib-impl/src/main/java/org/opendaylight/protocol/bgp/rib/impl/protocol/BGPReconnectPromise.java

index 9ba191ac136616d44faf62d6f5426ecb4d82baea..e6fcd664fb5f37994be35be91826b287e9bd5854 100644 (file)
@@ -16,6 +16,7 @@ import io.netty.channel.ChannelFutureListener;
 import io.netty.channel.ChannelHandlerContext;
 import io.netty.channel.ChannelInboundHandlerAdapter;
 import io.netty.util.concurrent.Promise;
+import io.netty.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
 import javax.annotation.concurrent.GuardedBy;
 import org.opendaylight.protocol.bgp.parser.BGPDocumentedException;
@@ -78,6 +79,8 @@ public abstract class AbstractBGPSessionNegotiator extends ChannelInboundHandler
     private final Channel channel;
     @GuardedBy("this")
     private State state = State.IDLE;
+    @GuardedBy("this")
+    private ScheduledFuture pending;
 
     @GuardedBy("this")
     private BGPSessionImpl session;
@@ -114,13 +117,16 @@ public abstract class AbstractBGPSessionNegotiator extends ChannelInboundHandler
         if (this.state != State.FINISHED) {
             this.state = State.OPEN_SENT;
 
-            this.channel.eventLoop().schedule(new Runnable() {
+            this.pending = this.channel.eventLoop().schedule(new Runnable() {
                 @Override
                 public void run() {
-                    if (AbstractBGPSessionNegotiator.this.state != State.FINISHED) {
-                        AbstractBGPSessionNegotiator.this.sendMessage(buildErrorNotify(BGPError.HOLD_TIMER_EXPIRED));
-                        negotiationFailed(new BGPDocumentedException("HoldTimer expired", BGPError.FSM_ERROR));
-                        AbstractBGPSessionNegotiator.this.state = State.FINISHED;
+                    synchronized (AbstractBGPSessionNegotiator.this) {
+                        AbstractBGPSessionNegotiator.this.pending = null;
+                        if (AbstractBGPSessionNegotiator.this.state != State.FINISHED) {
+                            AbstractBGPSessionNegotiator.this.sendMessage(buildErrorNotify(BGPError.HOLD_TIMER_EXPIRED));
+                            negotiationFailed(new BGPDocumentedException("HoldTimer expired", BGPError.FSM_ERROR));
+                            AbstractBGPSessionNegotiator.this.state = State.FINISHED;
+                        }
                     }
                 }
             }, INITIAL_HOLDTIMER, TimeUnit.MINUTES);
@@ -245,7 +251,12 @@ public abstract class AbstractBGPSessionNegotiator extends ChannelInboundHandler
     private void negotiationFailedCloseChannel(final Throwable cause) {
         LOG.debug("Negotiation on channel {} failed", this.channel, cause);
         this.channel.close();
-        this.promise.setFailure(cause);
+        synchronized (AbstractBGPSessionNegotiator.this) {
+            if (this.pending != null && this.pending.isCancellable()) {
+                this.pending.cancel(true);
+                this.pending = null;
+            }
+        }
     }
 
     private void sendMessage(final Notification msg) {
index b017962eeaa9237ced8d02352d66ceb88a1d2846..b679067d32e6f4e0b72a7900da331d61c92235b4 100644 (file)
@@ -61,6 +61,13 @@ public class BGPProtocolSessionPromise<S extends BGPSession> extends DefaultProm
 
     }
 
+    public synchronized void reconnect() {
+        final BGPProtocolSessionPromise lock = this;
+        final Future reconnectFuture = this.strategy.scheduleReconnect(this.pending.cause());
+        reconnectFuture.addListener(new BGPProtocolSessionPromise.BootstrapConnectListener(lock).new ReconnectingStrategyListener());
+        BGPProtocolSessionPromise.this.pending = reconnectFuture;
+    }
+
     @Override
     public synchronized boolean cancel(final boolean mayInterruptIfRunning) {
         if (super.cancel(mayInterruptIfRunning)) {
@@ -100,9 +107,7 @@ public class BGPProtocolSessionPromise<S extends BGPSession> extends DefaultProm
                     BGPProtocolSessionPromise.LOG.debug("Promise {} connection successful", this.lock);
                 } else {
                     BGPProtocolSessionPromise.LOG.debug("Attempt to connect to {} failed", BGPProtocolSessionPromise.this.address, channelFuture.cause());
-                    final Future reconnectFuture = BGPProtocolSessionPromise.this.strategy.scheduleReconnect(channelFuture.cause());
-                    reconnectFuture.addListener(new BGPProtocolSessionPromise.BootstrapConnectListener.ReconnectingStrategyListener());
-                    BGPProtocolSessionPromise.this.pending = reconnectFuture;
+                    BGPProtocolSessionPromise.this.reconnect();
                 }
             }
         }
index bc250687cab9c5d86a7af37ed158b7317f9ed175..4a3305b5a8953c62dd634d73285b0c6899436368 100644 (file)
@@ -34,7 +34,7 @@ public class BGPReconnectPromise<S extends BGPSession> extends DefaultPromise<Vo
     private final ReconnectStrategyFactory strategyFactory;
     private final Bootstrap bootstrap;
     private final ChannelPipelineInitializer initializer;
-    private Future<S> pending;
+    private BGPProtocolSessionPromise<S> pending;
 
     public BGPReconnectPromise(final EventExecutor executor, final InetSocketAddress address,
                                final ReconnectStrategyFactory connectStrategyFactory, final Bootstrap bootstrap,
@@ -72,7 +72,7 @@ public class BGPReconnectPromise<S extends BGPSession> extends DefaultPromise<Vo
         });
     }
 
-    public Future<S> connectSessionPromise(final InetSocketAddress address, final ReconnectStrategy strategy, final Bootstrap bootstrap,
+    public BGPProtocolSessionPromise<S> connectSessionPromise(final InetSocketAddress address, final ReconnectStrategy strategy, final Bootstrap bootstrap,
                                   final ChannelPipelineInitializer initializer) {
         final BGPProtocolSessionPromise sessionPromise = new BGPProtocolSessionPromise(address, strategy, bootstrap);
         final ChannelHandler chInit = new ChannelInitializer<SocketChannel>() {
@@ -125,7 +125,8 @@ public class BGPReconnectPromise<S extends BGPSession> extends DefaultPromise<Vo
             }
 
             if (!this.promise.isInitialConnectFinished()) {
-                LOG.debug("Connection to {} was dropped during negotiation", this.promise.address);
+                LOG.debug("Connection to {} was dropped during negotiation, reattempting", this.promise.address);
+                this.promise.pending.reconnect();
                 return;
             }