mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
clientv3: update eps if pinAddr is not included in updateAddrs
FIXES #7392
This commit is contained in:
parent
5193965005
commit
a23609efe6
@ -56,6 +56,9 @@ type simpleBalancer struct {
|
|||||||
// donec closes when all goroutines are exited
|
// donec closes when all goroutines are exited
|
||||||
donec chan struct{}
|
donec chan struct{}
|
||||||
|
|
||||||
|
// updateAddrsC notifies updateNotifyLoop to update addrs.
|
||||||
|
updateAddrsC chan struct{}
|
||||||
|
|
||||||
// grpc issues TLS cert checks using the string passed into dial so
|
// grpc issues TLS cert checks using the string passed into dial so
|
||||||
// that string must be the host. To recover the full scheme://host URL,
|
// that string must be the host. To recover the full scheme://host URL,
|
||||||
// have a map from hosts to the original endpoint.
|
// have a map from hosts to the original endpoint.
|
||||||
@ -76,14 +79,15 @@ func newSimpleBalancer(eps []string) *simpleBalancer {
|
|||||||
}
|
}
|
||||||
notifyCh <- addrs
|
notifyCh <- addrs
|
||||||
sb := &simpleBalancer{
|
sb := &simpleBalancer{
|
||||||
addrs: addrs,
|
addrs: addrs,
|
||||||
notifyCh: notifyCh,
|
notifyCh: notifyCh,
|
||||||
readyc: make(chan struct{}),
|
readyc: make(chan struct{}),
|
||||||
upc: make(chan struct{}),
|
upc: make(chan struct{}),
|
||||||
stopc: make(chan struct{}),
|
stopc: make(chan struct{}),
|
||||||
downc: make(chan struct{}),
|
downc: make(chan struct{}),
|
||||||
donec: make(chan struct{}),
|
donec: make(chan struct{}),
|
||||||
host2ep: getHost2ep(eps),
|
updateAddrsC: make(chan struct{}, 1),
|
||||||
|
host2ep: getHost2ep(eps),
|
||||||
}
|
}
|
||||||
go sb.updateNotifyLoop()
|
go sb.updateNotifyLoop()
|
||||||
return sb
|
return sb
|
||||||
@ -116,7 +120,6 @@ func (b *simpleBalancer) updateAddrs(eps []string) {
|
|||||||
np := getHost2ep(eps)
|
np := getHost2ep(eps)
|
||||||
|
|
||||||
b.mu.Lock()
|
b.mu.Lock()
|
||||||
defer b.mu.Unlock()
|
|
||||||
|
|
||||||
match := len(np) == len(b.host2ep)
|
match := len(np) == len(b.host2ep)
|
||||||
for k, v := range np {
|
for k, v := range np {
|
||||||
@ -127,6 +130,7 @@ func (b *simpleBalancer) updateAddrs(eps []string) {
|
|||||||
}
|
}
|
||||||
if match {
|
if match {
|
||||||
// same endpoints, so no need to update address
|
// same endpoints, so no need to update address
|
||||||
|
b.mu.Unlock()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,13 +141,30 @@ func (b *simpleBalancer) updateAddrs(eps []string) {
|
|||||||
addrs = append(addrs, grpc.Address{Addr: getHost(eps[i])})
|
addrs = append(addrs, grpc.Address{Addr: getHost(eps[i])})
|
||||||
}
|
}
|
||||||
b.addrs = addrs
|
b.addrs = addrs
|
||||||
|
|
||||||
// updating notifyCh can trigger new connections,
|
// updating notifyCh can trigger new connections,
|
||||||
// but balancer only expects new connections if all connections are down
|
// only update addrs if all connections are down
|
||||||
if b.pinAddr == "" {
|
// or addrs does not include pinAddr.
|
||||||
b.notifyCh <- addrs
|
update := !hasAddr(addrs, b.pinAddr)
|
||||||
|
b.mu.Unlock()
|
||||||
|
|
||||||
|
if update {
|
||||||
|
select {
|
||||||
|
case b.updateAddrsC <- struct{}{}:
|
||||||
|
case <-b.stopc:
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hasAddr(addrs []grpc.Address, targetAddr string) bool {
|
||||||
|
for _, addr := range addrs {
|
||||||
|
if targetAddr == addr.Addr {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (b *simpleBalancer) updateNotifyLoop() {
|
func (b *simpleBalancer) updateNotifyLoop() {
|
||||||
defer close(b.donec)
|
defer close(b.donec)
|
||||||
|
|
||||||
@ -170,21 +191,28 @@ func (b *simpleBalancer) updateNotifyLoop() {
|
|||||||
case <-b.stopc:
|
case <-b.stopc:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
case <-b.updateAddrsC:
|
||||||
|
b.notifyAddrs()
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case <-downc:
|
case <-downc:
|
||||||
b.mu.RLock()
|
b.notifyAddrs()
|
||||||
addrs := b.addrs
|
case <-b.updateAddrsC:
|
||||||
b.mu.RUnlock()
|
b.notifyAddrs()
|
||||||
select {
|
|
||||||
case b.notifyCh <- addrs:
|
|
||||||
case <-b.stopc:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case <-b.stopc:
|
case <-b.stopc:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *simpleBalancer) notifyAddrs() {
|
||||||
|
b.mu.RLock()
|
||||||
|
addrs := b.addrs
|
||||||
|
b.mu.RUnlock()
|
||||||
|
select {
|
||||||
|
case b.notifyCh <- addrs:
|
||||||
|
case <-b.stopc:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,6 +226,11 @@ func (b *simpleBalancer) Up(addr grpc.Address) func(error) {
|
|||||||
if b.closed {
|
if b.closed {
|
||||||
return func(err error) {}
|
return func(err error) {}
|
||||||
}
|
}
|
||||||
|
// gRPC might call Up on a stale address.
|
||||||
|
// Prevent updating pinAddr with a stale address.
|
||||||
|
if !hasAddr(b.addrs, addr.Addr) {
|
||||||
|
return func(err error) {}
|
||||||
|
}
|
||||||
|
|
||||||
if b.pinAddr == "" {
|
if b.pinAddr == "" {
|
||||||
// notify waiting Get()s and pin first connected address
|
// notify waiting Get()s and pin first connected address
|
||||||
|
@ -71,6 +71,27 @@ func testDialSetEndpoints(t *testing.T, setBefore bool) {
|
|||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestSwitchSetEndpoints ensures SetEndpoints can switch one endpoint
|
||||||
|
// with a new one that doesn't include original endpoint.
|
||||||
|
func TestSwitchSetEndpoints(t *testing.T) {
|
||||||
|
defer testutil.AfterTest(t)
|
||||||
|
clus := integration.NewClusterV3(t, &integration.ClusterConfig{Size: 3})
|
||||||
|
defer clus.Terminate(t)
|
||||||
|
|
||||||
|
// get non partitioned members endpoints
|
||||||
|
eps := []string{clus.Members[1].GRPCAddr(), clus.Members[2].GRPCAddr()}
|
||||||
|
|
||||||
|
cli := clus.Client(0)
|
||||||
|
clus.Members[0].InjectPartition(t, clus.Members[1:])
|
||||||
|
|
||||||
|
cli.SetEndpoints(eps...)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
if _, err := cli.Get(ctx, "foo"); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRejectOldCluster(t *testing.T) {
|
func TestRejectOldCluster(t *testing.T) {
|
||||||
defer testutil.AfterTest(t)
|
defer testutil.AfterTest(t)
|
||||||
// 2 endpoints to test multi-endpoint Status
|
// 2 endpoints to test multi-endpoint Status
|
||||||
|
Loading…
x
Reference in New Issue
Block a user