Skip to content

Commit

Permalink
make v1 a type alias of v2, add go.mod (#61)
Browse files Browse the repository at this point in the history
* make v1 a type alias of v2

This commit supercedes #58 and follows #59, and is the second of two in a
series of commits to add Go module support to gax.

This commit makes v1 code type aliases of v2 code. This commit will be tagged
v1.0.1.

Reasoning for making type aliases:

Users are directly exposed to exported types in this package such as
CallOption. google-cloud-go will rely on v2 of gax, but a user's codebase is
still going to have the non-v2/ gax import paths.

We could:

- Make google-cloud-go rely on v2 and simultaneously create a v2 of
google-cloud-go. All existing users of google-cloud-go continue using v1 of
gax and v1 of google-cloud-go. All folks migrating to v2 of google-cloud-go
must go update their gax import paths to use /v2. (this is obviously
infeasible)
- Make all gax v1 types aliases of gax v2 types, and all v1 functions shadows
of v2 functions. That way, google-cloud-go can rely on v2 but a user can pass
either a v1 or v2 gax type.

* add missing newline

* Add go.mod
  • Loading branch information
jeanbza committed Nov 16, 2018
1 parent b001040 commit d5fa34d
Show file tree
Hide file tree
Showing 10 changed files with 51 additions and 417 deletions.
104 changes: 8 additions & 96 deletions call_option.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,37 +30,21 @@
package gax

import (
"math/rand"
"time"

v2 "github.com/googleapis/gax-go/v2"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)

// CallOption is an option used by Invoke to control behaviors of RPC calls.
// CallOption works by modifying relevant fields of CallSettings.
type CallOption interface {
// Resolve applies the option by modifying cs.
Resolve(cs *CallSettings)
}
type CallOption = v2.CallOption

// Retryer is used by Invoke to determine retry behavior.
type Retryer interface {
// Retry reports whether a request should be retriedand how long to pause before retrying
// if the previous attempt returned with err. Invoke never calls Retry with nil error.
Retry(err error) (pause time.Duration, shouldRetry bool)
}

type retryerOption func() Retryer

func (o retryerOption) Resolve(s *CallSettings) {
s.Retry = o
}
type Retryer = v2.Retryer

// WithRetry sets CallSettings.Retry to fn.
func WithRetry(fn func() Retryer) CallOption {
return retryerOption(fn)
return v2.WithRetry(fn)
}

// OnCodes returns a Retryer that retries if and only if
Expand All @@ -69,89 +53,17 @@ func WithRetry(fn func() Retryer) CallOption {
//
// bo is only used for its parameters; each Retryer has its own copy.
func OnCodes(cc []codes.Code, bo Backoff) Retryer {
return &boRetryer{
backoff: bo,
codes: append([]codes.Code(nil), cc...),
}
}

type boRetryer struct {
backoff Backoff
codes []codes.Code
}

func (r *boRetryer) Retry(err error) (time.Duration, bool) {
st, ok := status.FromError(err)
if !ok {
return 0, false
}
c := st.Code()
for _, rc := range r.codes {
if c == rc {
return r.backoff.Pause(), true
}
}
return 0, false
return v2.OnCodes(cc, bo)
}

// Backoff implements exponential backoff.
// The wait time between retries is a random value between 0 and the "retry envelope".
// The envelope starts at Initial and increases by the factor of Multiplier every retry,
// but is capped at Max.
type Backoff struct {
// Initial is the initial value of the retry envelope, defaults to 1 second.
Initial time.Duration

// Max is the maximum value of the retry envelope, defaults to 30 seconds.
Max time.Duration

// Multiplier is the factor by which the retry envelope increases.
// It should be greater than 1 and defaults to 2.
Multiplier float64

// cur is the current retry envelope
cur time.Duration
}

func (bo *Backoff) Pause() time.Duration {
if bo.Initial == 0 {
bo.Initial = time.Second
}
if bo.cur == 0 {
bo.cur = bo.Initial
}
if bo.Max == 0 {
bo.Max = 30 * time.Second
}
if bo.Multiplier < 1 {
bo.Multiplier = 2
}
// Select a duration between zero and the current max. It might seem counterintuitive to
// have so much jitter, but https://www.awsarchitectureblog.com/2015/03/backoff.html
// argues that that is the best strategy.
d := time.Duration(rand.Int63n(int64(bo.cur)))
bo.cur = time.Duration(float64(bo.cur) * bo.Multiplier)
if bo.cur > bo.Max {
bo.cur = bo.Max
}
return d
}

type grpcOpt []grpc.CallOption

func (o grpcOpt) Resolve(s *CallSettings) {
s.GRPC = o
}
type Backoff = v2.Backoff

func WithGRPCOptions(opt ...grpc.CallOption) CallOption {
return grpcOpt(append([]grpc.CallOption(nil), opt...))
return v2.WithGRPCOptions(opt...)
}

type CallSettings struct {
// Retry returns a Retryer to be used to control retry logic of a method call.
// If Retry is nil or the returned Retryer is nil, the call will not be retried.
Retry func() Retryer

// CallOptions to be forwarded to GRPC.
GRPC []grpc.CallOption
}
type CallSettings = v2.CallSettings
88 changes: 0 additions & 88 deletions call_option_test.go

This file was deleted.

2 changes: 1 addition & 1 deletion gax.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,4 @@
// to simplify code generation and to provide more convenient and idiomatic API surfaces.
package gax

const Version = "2.0.0"
const Version = "1.0.1"
6 changes: 6 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module github.com/googleapis/gax-go

require (
github.com/googleapis/gax-go/v2 v2.0.2
google.golang.org/grpc v1.16.0
)
28 changes: 28 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/googleapis/gax-go/v2 v2.0.2 h1:/rNgUniLy2vDXiK2xyJOcirGpC3G99dtK1NWx26WZ8Y=
github.com/googleapis/gax-go/v2 v2.0.2/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d h1:g9qWBGx4puODJTMVyoPrpoxPFgVGd+z1DZwjfRu4d0I=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522 h1:Ve1ORMCxvRmSXBwJK+t3Oy+V2vRW2OetUQBq4rJIkZE=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v1.16.0 h1:dz5IJGuC2BB7qXR5AyHNwAUBhZscK2xVez7mznh72sY=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
17 changes: 2 additions & 15 deletions header.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,11 @@
package gax

import "bytes"
import v2 "github.com/googleapis/gax-go/v2"

// XGoogHeader is for use by the Google Cloud Libraries only.
//
// XGoogHeader formats key-value pairs.
// The resulting string is suitable for x-goog-api-client header.
func XGoogHeader(keyval ...string) string {
if len(keyval) == 0 {
return ""
}
if len(keyval)%2 != 0 {
panic("gax.Header: odd argument count")
}
var buf bytes.Buffer
for i := 0; i < len(keyval); i += 2 {
buf.WriteByte(' ')
buf.WriteString(keyval[i])
buf.WriteByte('/')
buf.WriteString(keyval[i+1])
}
return buf.String()[1:]
return v2.XGoogHeader(keyval...)
}
19 changes: 0 additions & 19 deletions header_test.go

This file was deleted.

47 changes: 5 additions & 42 deletions invoke.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,58 +32,21 @@ package gax
import (
"context"
"time"

v2 "github.com/googleapis/gax-go/v2"
)

// A user defined call stub.
type APICall func(context.Context, CallSettings) error
type APICall = v2.APICall

// Invoke calls the given APICall,
// performing retries as specified by opts, if any.
func Invoke(ctx context.Context, call APICall, opts ...CallOption) error {
var settings CallSettings
for _, opt := range opts {
opt.Resolve(&settings)
}
return invoke(ctx, call, settings, Sleep)
return v2.Invoke(ctx, call, opts...)
}

// Sleep is similar to time.Sleep, but it can be interrupted by ctx.Done() closing.
// If interrupted, Sleep returns ctx.Err().
func Sleep(ctx context.Context, d time.Duration) error {
t := time.NewTimer(d)
select {
case <-ctx.Done():
t.Stop()
return ctx.Err()
case <-t.C:
return nil
}
}

type sleeper func(ctx context.Context, d time.Duration) error

// invoke implements Invoke, taking an additional sleeper argument for testing.
func invoke(ctx context.Context, call APICall, settings CallSettings, sp sleeper) error {
var retryer Retryer
for {
err := call(ctx, settings)
if err == nil {
return nil
}
if settings.Retry == nil {
return err
}
if retryer == nil {
if r := settings.Retry(); r != nil {
retryer = r
} else {
return err
}
}
if d, ok := retryer.Retry(err); !ok {
return err
} else if err = sp(ctx, d); err != nil {
return err
}
}
return v2.Sleep(ctx, d)
}

0 comments on commit d5fa34d

Please sign in to comment.