Merge "veyron/runtimes/google/ipc: Failed name resolution should be retryable."
diff --git a/runtimes/google/ipc/client.go b/runtimes/google/ipc/client.go
index 2788afb..91fc5bc 100644
--- a/runtimes/google/ipc/client.go
+++ b/runtimes/google/ipc/client.go
@@ -300,6 +300,9 @@
// Caller specified deadline.
deadline = time.Now().Add(time.Duration(r))
}
+
+ skipResolve := getNoResolveOpt(opts)
+
var lastErr verror.E
for retries := 0; ; retries++ {
if retries != 0 {
@@ -307,12 +310,26 @@
break
}
}
- call, err := c.tryCall(ctx, name, method, args, opts)
+ call, err := c.tryCall(ctx, name, method, args, skipResolve, opts)
if err == nil {
return call, nil
}
lastErr = err
- if time.Now().After(deadline) || err.Action() != verror.RetryConnection {
+
+ shouldRetry := true
+ switch {
+ case err.Action() != verror.RetryConnection && err.Action() != verror.RetryRefetch:
+ shouldRetry = false
+ case time.Now().After(deadline):
+ shouldRetry = false
+ case verror.Is(err, verror.NoServers.ID) && skipResolve:
+ // If we're skipping resolution and there are no servers for
+ // this call retrying is not going to help, we can't come up
+ // with new servers if there is no resolution.
+ shouldRetry = false
+ }
+
+ if !shouldRetry {
span.Annotatef("Cannot retry after error: %s", err)
break
}
@@ -345,15 +362,18 @@
}
// tryCall makes a single attempt at a call, against possibly multiple servers.
-func (c *client) tryCall(ctx context.T, name, method string, args []interface{}, opts []ipc.CallOpt) (ipc.Call, verror.E) {
+func (c *client) tryCall(ctx context.T, name, method string, args []interface{}, skipResolve bool, opts []ipc.CallOpt) (ipc.Call, verror.E) {
mtPattern, serverPattern, name := splitObjectName(name)
noDischarges := shouldNotFetchDischarges(opts)
// Resolve name unless told not to.
var servers []string
- if getNoResolveOpt(opts) {
+ if skipResolve {
servers = []string{name}
} else {
if resolved, err := c.ns.Resolve(ctx, name, naming.RootBlessingPatternOpt(mtPattern)); err != nil {
+ if verror.Is(err, naming.ErrNoSuchName.ID) {
+ return nil, verror.Make(verror.NoServers, ctx, name)
+ }
return nil, verror.Make(verror.NoExist, ctx, name, err)
} else {
if len(resolved) == 0 {
diff --git a/runtimes/google/ipc/context_test.go b/runtimes/google/ipc/context_test.go
index 515ee26..cdc7e0e 100644
--- a/runtimes/google/ipc/context_test.go
+++ b/runtimes/google/ipc/context_test.go
@@ -16,9 +16,13 @@
// so we use a fake one that panics if used. The runtime
// implementation should not ever use the Runtime from a context.
func testContext() context.T {
+ ctx, _ := testContextWithoutDeadline().WithTimeout(20 * time.Second)
+ return ctx
+}
+
+func testContextWithoutDeadline() context.T {
ctx := InternalNewContext(&runtime.PanicRuntime{})
ctx, _ = vtrace.WithNewRootSpan(ctx, nil, false)
- ctx, _ = ctx.WithDeadline(time.Now().Add(20 * time.Second))
return ctx
}
diff --git a/runtimes/google/ipc/full_test.go b/runtimes/google/ipc/full_test.go
index a5be1c5..82199f8 100644
--- a/runtimes/google/ipc/full_test.go
+++ b/runtimes/google/ipc/full_test.go
@@ -36,7 +36,7 @@
"veyron.io/veyron/veyron/runtimes/google/lib/publisher"
inaming "veyron.io/veyron/veyron/runtimes/google/naming"
tnaming "veyron.io/veyron/veyron/runtimes/google/testing/mocks/naming"
- "veyron.io/veyron/veyron/runtimes/google/vtrace"
+ ivtrace "veyron.io/veyron/veyron/runtimes/google/vtrace"
)
func init() {
@@ -424,11 +424,14 @@
}
)
+ ctx := testContextWithoutDeadline()
+
// Start the main server.
- dc, err := InternalNewDischargeClient(mgr, ns, testContext(), vc.LocalPrincipal{pserver})
+ dc, err := InternalNewDischargeClient(mgr, ns, ctx, vc.LocalPrincipal{pserver})
if err != nil {
t.Errorf("InternalNewDischargeClient failed: %v", err)
}
+
_, server := startServer(t, pserver, mgr, ns, serverName, testServerDisp{&testServer{}}, dc)
defer stopServer(t, server, ns, serverName)
@@ -456,7 +459,8 @@
t.Errorf("%s: failed to create client: %v", name, err)
continue
}
- call, err := client.StartCall(testContext(), fmt.Sprintf("[%s]%s/suffix", test.pattern, serverName), "Method", nil)
+ dctx, cancel := ctx.WithTimeout(10 * time.Second)
+ call, err := client.StartCall(dctx, fmt.Sprintf("[%s]%s/suffix", test.pattern, serverName), "Method", nil)
if !matchesErrorPattern(err, test.errID, test.err) {
t.Errorf(`%d: %s: client.StartCall: got error "%v", want to match "%v"`, i, name, err, test.err)
} else if call != nil {
@@ -468,9 +472,8 @@
t.Errorf("%s: %q.MatchedBy(%v) failed", name, test.pattern, blessings)
}
}
- vlog.Infof("\nC")
+ cancel()
client.Close()
-
}
}
@@ -682,7 +685,7 @@
func (s *dischargeTestServer) Discharge(ctx ipc.ServerContext, cav vdlutil.Any, impetus security.DischargeImpetus) (vdlutil.Any, error) {
s.impetus = append(s.impetus, impetus)
- s.traceid = append(s.traceid, vtrace.FromContext(ctx).Trace().ID())
+ s.traceid = append(s.traceid, ivtrace.FromContext(ctx).Trace().ID())
return nil, fmt.Errorf("discharges not issued")
}
@@ -807,7 +810,7 @@
}
defer client.Close()
ctx := testContext()
- tid := vtrace.FromContext(ctx).Trace().ID()
+ tid := ivtrace.FromContext(ctx).Trace().ID()
// StartCall should fetch the discharge, do not worry about finishing the RPC - do not care about that for this test.
if _, err := client.StartCall(ctx, object, "Method", []interface{}{"argument"}); err != nil {
t.Errorf("StartCall(%+v) failed: %v", test.Requirements, err)
diff --git a/runtimes/google/testing/mocks/naming/namespace.go b/runtimes/google/testing/mocks/naming/namespace.go
index f6b0ba0..e3403e5 100644
--- a/runtimes/google/testing/mocks/naming/namespace.go
+++ b/runtimes/google/testing/mocks/naming/namespace.go
@@ -8,7 +8,7 @@
"veyron.io/veyron/veyron2/context"
"veyron.io/veyron/veyron2/naming"
- "veyron.io/veyron/veyron2/verror"
+ verror "veyron.io/veyron/veyron2/verror2"
"veyron.io/veyron/veyron2/vlog"
)
@@ -74,7 +74,7 @@
return ret, nil
}
}
- return nil, verror.NoExistf("Resolve name %q not found in %v", name, ns.mounts)
+ return nil, verror.Make(naming.ErrNoSuchName, ctx, fmt.Sprintf("Resolve name %q not found in %v", name, ns.mounts))
}
func (ns *namespace) ResolveX(ctx context.T, name string, opts ...naming.ResolveOpt) (*naming.MountEntry, error) {
@@ -95,7 +95,7 @@
return e, nil
}
}
- return nil, verror.NoExistf("Resolve name %q not found in %v", name, ns.mounts)
+ return nil, verror.Make(naming.ErrNoSuchName, ctx, fmt.Sprintf("Resolve name %q not found in %v", name, ns.mounts))
}
func (ns *namespace) ResolveToMountTableX(ctx context.T, name string, opts ...naming.ResolveOpt) (*naming.MountEntry, error) {