blob: dcdb8d66b37774e716bacb6eeddb93833144ec96 [file] [log] [blame]
/*
* Copyright 2019-present Open Networking Foundation
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package afrouter
import (
"context"
"github.com/opencord/voltha-go/common/log"
"google.golang.org/grpc"
"google.golang.org/grpc/connectivity"
"time"
)
// connection represents a connection to a single backend
type connection struct {
backend *backend
name string
addr string
port string
ctx context.Context
close context.CancelFunc
}
func (cn *connection) connect() {
for {
log.Infof("Connecting to %s with addr: %s and port %s", cn.name, cn.addr, cn.port)
// Dial doesn't block, it just returns and continues connecting in the background.
// Check back later to confirm and increase the connection count.
var err error
conn, err := grpc.Dial(cn.addr+":"+cn.port, grpc.WithCodec(Codec()), grpc.WithInsecure(), grpc.WithBackoffMaxDelay(time.Second*15))
if err != nil {
log.Fatalf("Dialing connection %v:%v", cn, err)
}
log.Debugf("Starting the connection monitor for '%s'", cn.name)
cn.monitor(conn)
conn.Close()
select {
case <-cn.ctx.Done():
return
default:
}
}
}
func (cn *connection) monitor(conn *grpc.ClientConn) {
be := cn.backend
log.Debugf("Setting up monitoring for backend %s", be.name)
state := connectivity.Idle
monitorLoop:
for {
if !conn.WaitForStateChange(cn.ctx, state) {
log.Debugf("Context canceled for connection '%s' on backend '%s'", cn.name, be.name)
break monitorLoop // connection closed
}
if newState := conn.GetState(); newState != state {
previousState := state
state = newState
if previousState == connectivity.Ready {
be.decConn(cn)
log.Infof("Lost connection '%s' on backend '%s'", cn.name, be.name)
}
switch state {
case connectivity.Ready:
log.Infof("Connection '%s' on backend '%s' becomes ready", cn.name, be.name)
be.incConn(cn, conn)
case connectivity.TransientFailure, connectivity.Connecting:
// we don't log these, to avoid spam
case connectivity.Shutdown:
// the connection was closed
log.Infof("Shutdown for connection '%s' on backend '%s'", cn.name, be.name)
break monitorLoop
case connectivity.Idle:
// This can only happen if the server sends a GoAway. This can
// only happen if the server has modified MaxConnectionIdle from
// its default of infinity. The only solution here is to close the
// connection and keepTrying()?
//TODO: Read the grpc source code to see if there's a different approach
log.Errorf("Server sent 'GoAway' on connection '%s' on backend '%s'", cn.name, be.name)
break monitorLoop
}
}
}
}