Skip to content

Commit

Permalink
Fix panics on balancer and resolver updates (grpc#1684)
Browse files Browse the repository at this point in the history
 - NewAddress with empty list (addrConn with an empty address list)
 - NewServiceConfig to switch balancer before the first balancer is built
  • Loading branch information
menghanl authored Nov 22, 2017
1 parent 646f701 commit 10873b3
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 1 deletion.
8 changes: 8 additions & 0 deletions balancer_conn_wrappers.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package grpc

import (
"fmt"
"sync"

"google.golang.org/grpc/balancer"
Expand Down Expand Up @@ -183,6 +184,9 @@ func (ccb *ccBalancerWrapper) handleResolvedAddrs(addrs []resolver.Address, err
}

func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) {
if len(addrs) <= 0 {
return nil, fmt.Errorf("grpc: cannot create SubConn with empty address list")
}
ac, err := ccb.cc.newAddrConn(addrs)
if err != nil {
return nil, err
Expand Down Expand Up @@ -223,6 +227,10 @@ type acBalancerWrapper struct {
func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) {
acbw.mu.Lock()
defer acbw.mu.Unlock()
if len(addrs) <= 0 {
acbw.ac.tearDown(errConnDrain)
return
}
if !acbw.ac.tryUpdateAddrs(addrs) {
cc := acbw.ac.cc
acbw.ac.mu.Lock()
Expand Down
8 changes: 7 additions & 1 deletion clientconn.go
Original file line number Diff line number Diff line change
Expand Up @@ -642,6 +642,8 @@ func (cc *ClientConn) handleResolvedAddrs(addrs []resolver.Address, err error) {
}

// switchBalancer starts the switching from current balancer to the balancer with name.
//
// Caller must hold cc.mu.
func (cc *ClientConn) switchBalancer(name string) {
if cc.conns == nil {
return
Expand All @@ -659,7 +661,9 @@ func (cc *ClientConn) switchBalancer(name string) {

// TODO(bar switching) change this to two steps: drain and close.
// Keep track of sc in wrapper.
cc.balancerWrapper.close()
if cc.balancerWrapper != nil {
cc.balancerWrapper.close()
}

builder := balancer.Get(name)
if builder == nil {
Expand All @@ -684,6 +688,8 @@ func (cc *ClientConn) handleSubConnStateChange(sc balancer.SubConn, s connectivi
}

// newAddrConn creates an addrConn for addrs and adds it to cc.conns.
//
// Caller needs to make sure len(addrs) > 0.
func (cc *ClientConn) newAddrConn(addrs []resolver.Address) (*addrConn, error) {
ac := &addrConn{
cc: cc,
Expand Down
37 changes: 37 additions & 0 deletions clientconn_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ import (
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/keepalive"
"google.golang.org/grpc/naming"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/resolver/manual"
_ "google.golang.org/grpc/resolver/passthrough"
"google.golang.org/grpc/test/leakcheck"
"google.golang.org/grpc/testdata"
Expand Down Expand Up @@ -329,6 +331,41 @@ func TestNonblockingDialWithEmptyBalancer(t *testing.T) {
}
}

func TestResolverServiceConfigBeforeAddressNotPanic(t *testing.T) {
defer leakcheck.Check(t)
r, rcleanup := manual.GenerateAndRegisterManualResolver()
defer rcleanup()

cc, err := Dial(r.Scheme()+":///test.server", WithInsecure())
if err != nil {
t.Fatalf("failed to dial: %v", err)
}
defer cc.Close()

// SwitchBalancer before NewAddress. There was no balancer created, this
// makes sure we don't call close on nil balancerWrapper.
r.NewServiceConfig(`{"loadBalancingPolicy": "round_robin"}`) // This should not panic.

time.Sleep(time.Second) // Sleep to make sure the service config is handled by ClientConn.
}

func TestResolverEmptyUpdateNotPanic(t *testing.T) {
defer leakcheck.Check(t)
r, rcleanup := manual.GenerateAndRegisterManualResolver()
defer rcleanup()

cc, err := Dial(r.Scheme()+":///test.server", WithInsecure())
if err != nil {
t.Fatalf("failed to dial: %v", err)
}
defer cc.Close()

// This make sure we don't create addrConn with empty address list.
r.NewAddress([]resolver.Address{}) // This should not panic.

time.Sleep(time.Second) // Sleep to make sure the service config is handled by ClientConn.
}

func TestClientUpdatesParamsAfterGoAway(t *testing.T) {
defer leakcheck.Check(t)
lis, err := net.Listen("tcp", "localhost:0")
Expand Down

0 comments on commit 10873b3

Please sign in to comment.