mirror of https://github.com/grpc/grpc-go.git
resolver/manual: support restarts, required for channel idleness (#6638)
This commit is contained in:
parent
9deee9ba5f
commit
1880bd6ff3
|
|
@ -1058,9 +1058,8 @@ func (s) TestUpdateAddresses_NoopIfCalledWithSameAddresses(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s) TestDefaultServiceConfig(t *testing.T) {
|
func (s) TestDefaultServiceConfig(t *testing.T) {
|
||||||
r := manual.NewBuilderWithScheme("whatever")
|
const defaultSC = `
|
||||||
addr := r.Scheme() + ":///non.existent"
|
{
|
||||||
js := `{
|
|
||||||
"methodConfig": [
|
"methodConfig": [
|
||||||
{
|
{
|
||||||
"name": [
|
"name": [
|
||||||
|
|
@ -1073,10 +1072,40 @@ func (s) TestDefaultServiceConfig(t *testing.T) {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`
|
}`
|
||||||
testInvalidDefaultServiceConfig(t)
|
tests := []struct {
|
||||||
testDefaultServiceConfigWhenResolverServiceConfigDisabled(t, r, addr, js)
|
name string
|
||||||
testDefaultServiceConfigWhenResolverDoesNotReturnServiceConfig(t, r, addr, js)
|
testF func(t *testing.T, r *manual.Resolver, addr, sc string)
|
||||||
testDefaultServiceConfigWhenResolverReturnInvalidServiceConfig(t, r, addr, js)
|
sc string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "invalid-service-config",
|
||||||
|
testF: testInvalidDefaultServiceConfig,
|
||||||
|
sc: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "resolver-service-config-disabled",
|
||||||
|
testF: testDefaultServiceConfigWhenResolverServiceConfigDisabled,
|
||||||
|
sc: defaultSC,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "resolver-does-not-return-service-config",
|
||||||
|
testF: testDefaultServiceConfigWhenResolverDoesNotReturnServiceConfig,
|
||||||
|
sc: defaultSC,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "resolver-returns-invalid-service-config",
|
||||||
|
testF: testDefaultServiceConfigWhenResolverReturnInvalidServiceConfig,
|
||||||
|
sc: defaultSC,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.name, func(t *testing.T) {
|
||||||
|
r := manual.NewBuilderWithScheme(test.name)
|
||||||
|
addr := r.Scheme() + ":///non.existent"
|
||||||
|
test.testF(t, r, addr, test.sc)
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyWaitForReadyEqualsTrue(cc *ClientConn) bool {
|
func verifyWaitForReadyEqualsTrue(cc *ClientConn) bool {
|
||||||
|
|
@ -1091,8 +1120,8 @@ func verifyWaitForReadyEqualsTrue(cc *ClientConn) bool {
|
||||||
return i != 10
|
return i != 10
|
||||||
}
|
}
|
||||||
|
|
||||||
func testInvalidDefaultServiceConfig(t *testing.T) {
|
func testInvalidDefaultServiceConfig(t *testing.T, r *manual.Resolver, addr, sc string) {
|
||||||
_, err := Dial("fake.com", WithTransportCredentials(insecure.NewCredentials()), WithDefaultServiceConfig(""))
|
_, err := Dial(addr, WithTransportCredentials(insecure.NewCredentials()), WithResolvers(r), WithDefaultServiceConfig(sc))
|
||||||
if !strings.Contains(err.Error(), invalidDefaultServiceConfigErrPrefix) {
|
if !strings.Contains(err.Error(), invalidDefaultServiceConfigErrPrefix) {
|
||||||
t.Fatalf("Dial got err: %v, want err contains: %v", err, invalidDefaultServiceConfigErrPrefix)
|
t.Fatalf("Dial got err: %v, want err contains: %v", err, invalidDefaultServiceConfigErrPrefix)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,9 @@ import (
|
||||||
"google.golang.org/grpc/resolver"
|
"google.golang.org/grpc/resolver"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewBuilderWithScheme creates a new test resolver builder with the given scheme.
|
// NewBuilderWithScheme creates a new manual resolver builder with the given
|
||||||
|
// scheme. Every instance of the manual resolver may only ever be used with a
|
||||||
|
// single grpc.ClientConn. Otherwise, bad things will happen.
|
||||||
func NewBuilderWithScheme(scheme string) *Resolver {
|
func NewBuilderWithScheme(scheme string) *Resolver {
|
||||||
return &Resolver{
|
return &Resolver{
|
||||||
BuildCallback: func(resolver.Target, resolver.ClientConn, resolver.BuildOptions) {},
|
BuildCallback: func(resolver.Target, resolver.ClientConn, resolver.BuildOptions) {},
|
||||||
|
|
@ -58,30 +60,34 @@ type Resolver struct {
|
||||||
scheme string
|
scheme string
|
||||||
|
|
||||||
// Fields actually belong to the resolver.
|
// Fields actually belong to the resolver.
|
||||||
mu sync.Mutex // Guards access to CC.
|
// Guards access to below fields.
|
||||||
CC resolver.ClientConn
|
mu sync.Mutex
|
||||||
bootstrapState *resolver.State
|
CC resolver.ClientConn
|
||||||
|
// Storing the most recent state update makes this resolver resilient to
|
||||||
|
// restarts, which is possible with channel idleness.
|
||||||
|
lastSeenState *resolver.State
|
||||||
}
|
}
|
||||||
|
|
||||||
// InitialState adds initial state to the resolver so that UpdateState doesn't
|
// InitialState adds initial state to the resolver so that UpdateState doesn't
|
||||||
// need to be explicitly called after Dial.
|
// need to be explicitly called after Dial.
|
||||||
func (r *Resolver) InitialState(s resolver.State) {
|
func (r *Resolver) InitialState(s resolver.State) {
|
||||||
r.bootstrapState = &s
|
r.lastSeenState = &s
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build returns itself for Resolver, because it's both a builder and a resolver.
|
// Build returns itself for Resolver, because it's both a builder and a resolver.
|
||||||
func (r *Resolver) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
|
func (r *Resolver) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions) (resolver.Resolver, error) {
|
||||||
|
r.BuildCallback(target, cc, opts)
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
r.CC = cc
|
r.CC = cc
|
||||||
r.mu.Unlock()
|
if r.lastSeenState != nil {
|
||||||
r.BuildCallback(target, cc, opts)
|
err := r.CC.UpdateState(*r.lastSeenState)
|
||||||
if r.bootstrapState != nil {
|
go r.UpdateStateCallback(err)
|
||||||
r.UpdateState(*r.bootstrapState)
|
|
||||||
}
|
}
|
||||||
|
r.mu.Unlock()
|
||||||
return r, nil
|
return r, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scheme returns the test scheme.
|
// Scheme returns the manual resolver's scheme.
|
||||||
func (r *Resolver) Scheme() string {
|
func (r *Resolver) Scheme() string {
|
||||||
return r.scheme
|
return r.scheme
|
||||||
}
|
}
|
||||||
|
|
@ -100,6 +106,7 @@ func (r *Resolver) Close() {
|
||||||
func (r *Resolver) UpdateState(s resolver.State) {
|
func (r *Resolver) UpdateState(s resolver.State) {
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
err := r.CC.UpdateState(s)
|
err := r.CC.UpdateState(s)
|
||||||
|
r.lastSeenState = &s
|
||||||
r.mu.Unlock()
|
r.mu.Unlock()
|
||||||
r.UpdateStateCallback(err)
|
r.UpdateStateCallback(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue