Skip to content

Commit

Permalink
fix(feg): Diameter closed connection recovery
Browse files Browse the repository at this point in the history
Signed-off-by: Evgeniy Makeev <evgeniym@fb.com>
  • Loading branch information
emakeev committed Mar 18, 2022
1 parent 863a37d commit d41cf6a
Showing 1 changed file with 41 additions and 2 deletions.
43 changes: 41 additions & 2 deletions feg/gateway/diameter/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"net"
"strings"
"sync"
"time"

"github.com/fiorix/go-diameter/v4/diam"
"github.com/fiorix/go-diameter/v4/diam/avp"
Expand All @@ -33,6 +34,9 @@ type messageTypeEnum uint8
const (
requestMessage messageTypeEnum = 1
answerMessage messageTypeEnum = 2

connectionRecoveryInterval = time.Second
connectionRecoveryattempts = 6
)

// Connection is representing a diameter connection that you can
Expand Down Expand Up @@ -155,16 +159,50 @@ func (c *Connection) getDiamConnection() (diam.Conn, *smpeer.Metadata, error) {
return nil, nil, errors.New("Could not obtain metadata from connection")
}
c.conn, c.metadata = conn, metadata
if cn, ok := conn.(diam.CloseNotifier); ok && cn != nil {
go c.connCloseNotify(cn.CloseNotify(), conn)
} else {
glog.Errorf("new diam conn (%T) %s -> %s is not CloseNotifier", conn, localAddr, c.server.Addr)
}
return conn, metadata, nil
}

func (c *Connection) connCloseNotify(cnc <-chan struct{}, conn diam.Conn) {
<-cnc // wait for close notifier
if glog.V(2) {
remoteAddress, localAddress := "nil", "nil"
if conn != nil {
if conn.LocalAddr() != nil {
localAddress = conn.LocalAddr().String()
}
if conn.LocalAddr() != nil {
remoteAddress = conn.RemoteAddr().String()
}
}
glog.Infof("notified of %s->%s connection closure", localAddress, remoteAddress)
}
if c != nil && conn != nil && c.destroyConnection(conn) {
// if connection was closed not by connection management functions, recover it
retryWaitTime := connectionRecoveryInterval
for i := 0; i < connectionRecoveryattempts; i++ {
_, _, err := c.getDiamConnection()
if err == nil {
break
}
time.Sleep(retryWaitTime)
retryWaitTime *= 2
}
}
}

// destroyConnection closes a bad connection. If the connection
// passed is the same as the one stored in the locked connection, it is nullified.
// If the passed diam connection is not the same, this probably means another go routine
// already created a new connection - just try to close it and return.
func (c *Connection) destroyConnection(conn diam.Conn) {
// destroyConnection returns true if connection matches and was closed
func (c *Connection) destroyConnection(conn diam.Conn) bool {
if conn == nil {
return
return false
}
c.mutex.Lock()
match := conn == c.conn
Expand All @@ -191,6 +229,7 @@ func (c *Connection) destroyConnection(conn diam.Conn) {
}
}
conn.Close()
return match
}

// cleanupConnection is similar to destroyConnection, but it closes & cleans up connection unconditionally
Expand Down

0 comments on commit d41cf6a

Please sign in to comment.