Merge "Add a retry loop to StartCall.  If a deadline is not set, use defaultCallTimeout."
diff --git a/runtimes/google/ipc/client.go b/runtimes/google/ipc/client.go
index 45626ee..3dd736a 100644
--- a/runtimes/google/ipc/client.go
+++ b/runtimes/google/ipc/client.go
@@ -3,6 +3,8 @@
 import (
 	"fmt"
 	"io"
+	"math/rand"
+	"strings"
 	"sync"
 	"time"
 
@@ -112,7 +114,32 @@
 }
 
 func (c *client) StartCall(ctx context.T, name, method string, args []interface{}, opts ...ipc.CallOpt) (ipc.Call, error) {
-	return c.startCall(ctx, name, method, args, opts...)
+	deadline, hasDeadline := ctx.Deadline()
+	if !hasDeadline {
+		// If no deadline is set, use a long but finite one.
+		deadline = time.Now().Add(defaultCallTimeout)
+	}
+	var lastErr verror.E
+	b := time.Duration(1.5 + (rand.Float32() / 2.0))
+	backoff := b
+	for deadline.After(time.Now()) {
+		call, err := c.startCall(ctx, name, method, args, opts...)
+		if err == nil {
+			return call, nil
+		}
+		lastErr = err
+		// TODO(p): replace these checks with m3b's retry bit when it exists.
+		if !strings.Contains(err.Error(), "ipc: Resolve") &&
+			!(strings.Contains(err.Error(), "ipc: couldn't connect") && strings.Contains(err.Error(), "errno")) {
+			break
+		}
+		time.Sleep(backoff)
+		backoff = backoff * b
+		if backoff > maxBackoff {
+			backoff = maxBackoff
+		}
+	}
+	return nil, lastErr
 }
 
 // startCall ensures StartCall always returns verror.E.
diff --git a/runtimes/google/ipc/consts.go b/runtimes/google/ipc/consts.go
index 6a013e3..279ca52 100644
--- a/runtimes/google/ipc/consts.go
+++ b/runtimes/google/ipc/consts.go
@@ -7,5 +7,10 @@
 	publishPeriod = time.Minute
 
 	// The server uses this timeout for incoming calls before the real timeout is known.
+	// The client uses this as the default max time for connecting to the server including
+	// name resolution.
 	defaultCallTimeout = time.Minute
+
+	// The client uses this as the maximum time between retry attempts when starting a call.
+	maxBackoff = time.Minute
 )