...

Source file src/github.com/googleapis/gax-go/v2/example_test.go

Documentation: github.com/googleapis/gax-go/v2

     1  // Copyright 2019, Google Inc.
     2  // All rights reserved.
     3  //
     4  // Redistribution and use in source and binary forms, with or without
     5  // modification, are permitted provided that the following conditions are
     6  // met:
     7  //
     8  //     * Redistributions of source code must retain the above copyright
     9  // notice, this list of conditions and the following disclaimer.
    10  //     * Redistributions in binary form must reproduce the above
    11  // copyright notice, this list of conditions and the following disclaimer
    12  // in the documentation and/or other materials provided with the
    13  // distribution.
    14  //     * Neither the name of Google Inc. nor the names of its
    15  // contributors may be used to endorse or promote products derived from
    16  // this software without specific prior written permission.
    17  //
    18  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    19  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    20  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    21  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    22  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    23  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    24  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    25  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    26  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    27  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    28  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    29  
    30  package gax_test
    31  
    32  import (
    33  	"context"
    34  	"io"
    35  	"net/http"
    36  	"time"
    37  
    38  	gax "github.com/googleapis/gax-go/v2"
    39  	"google.golang.org/grpc/codes"
    40  	"google.golang.org/grpc/status"
    41  	"google.golang.org/protobuf/reflect/protoreflect"
    42  	"google.golang.org/protobuf/types/known/structpb"
    43  )
    44  
    45  // Some result that the client might return.
    46  type fakeResponse struct{}
    47  
    48  // Some client that can perform RPCs.
    49  type fakeClient struct{}
    50  
    51  // PerformSomeRPC is a fake RPC that a client might perform.
    52  func (c *fakeClient) PerformSomeRPC(ctx context.Context) (*fakeResponse, error) {
    53  	// An actual client would return something meaningful here.
    54  	return nil, nil
    55  }
    56  
    57  func ExampleOnErrorFunc() {
    58  	ctx := context.Background()
    59  	c := &fakeClient{}
    60  
    61  	shouldRetryUnavailableUnKnown := func(err error) bool {
    62  		st, ok := status.FromError(err)
    63  		if !ok {
    64  			return false
    65  		}
    66  
    67  		return st.Code() == codes.Unavailable || st.Code() == codes.Unknown
    68  	}
    69  	retryer := gax.OnErrorFunc(gax.Backoff{
    70  		Initial:    time.Second,
    71  		Max:        32 * time.Second,
    72  		Multiplier: 2,
    73  	}, shouldRetryUnavailableUnKnown)
    74  
    75  	performSomeRPCWithRetry := func(ctx context.Context) (*fakeResponse, error) {
    76  		for {
    77  			resp, err := c.PerformSomeRPC(ctx)
    78  			if err != nil {
    79  				if delay, shouldRetry := retryer.Retry(err); shouldRetry {
    80  					if err := gax.Sleep(ctx, delay); err != nil {
    81  						return nil, err
    82  					}
    83  					continue
    84  				}
    85  				return nil, err
    86  			}
    87  			return resp, err
    88  		}
    89  	}
    90  
    91  	// It's recommended to set deadlines on RPCs and around retrying. This is
    92  	// also usually preferred over setting some fixed number of retries: one
    93  	// advantage this has is that backoff settings can be changed independently
    94  	// of the deadline, whereas with a fixed number of retries the deadline
    95  	// would be a constantly-shifting goalpost.
    96  	ctxWithTimeout, cancel := context.WithDeadline(ctx, time.Now().Add(5*time.Minute))
    97  	defer cancel()
    98  
    99  	resp, err := performSomeRPCWithRetry(ctxWithTimeout)
   100  	if err != nil {
   101  		// TODO: handle err
   102  	}
   103  	_ = resp // TODO: use resp if err is nil
   104  }
   105  
   106  func ExampleOnCodes() {
   107  	ctx := context.Background()
   108  	c := &fakeClient{}
   109  
   110  	// UNKNOWN and UNAVAILABLE are typically safe to retry for idempotent RPCs.
   111  	retryer := gax.OnCodes([]codes.Code{codes.Unknown, codes.Unavailable}, gax.Backoff{
   112  		Initial:    time.Second,
   113  		Max:        32 * time.Second,
   114  		Multiplier: 2,
   115  	})
   116  
   117  	performSomeRPCWithRetry := func(ctx context.Context) (*fakeResponse, error) {
   118  		for {
   119  			resp, err := c.PerformSomeRPC(ctx)
   120  			if err != nil {
   121  				if delay, shouldRetry := retryer.Retry(err); shouldRetry {
   122  					if err := gax.Sleep(ctx, delay); err != nil {
   123  						return nil, err
   124  					}
   125  					continue
   126  				}
   127  				return nil, err
   128  			}
   129  			return resp, err
   130  		}
   131  	}
   132  
   133  	// It's recommended to set deadlines on RPCs and around retrying. This is
   134  	// also usually preferred over setting some fixed number of retries: one
   135  	// advantage this has is that backoff settings can be changed independently
   136  	// of the deadline, whereas with a fixed number of retries the deadline
   137  	// would be a constantly-shifting goalpost.
   138  	ctxWithTimeout, cancel := context.WithDeadline(ctx, time.Now().Add(5*time.Minute))
   139  	defer cancel()
   140  
   141  	resp, err := performSomeRPCWithRetry(ctxWithTimeout)
   142  	if err != nil {
   143  		// TODO: handle err
   144  	}
   145  	_ = resp // TODO: use resp if err is nil
   146  }
   147  
   148  func ExampleOnHTTPCodes() {
   149  	ctx := context.Background()
   150  	c := &fakeClient{}
   151  
   152  	retryer := gax.OnHTTPCodes(gax.Backoff{
   153  		Initial:    time.Second,
   154  		Max:        32 * time.Second,
   155  		Multiplier: 2,
   156  	}, http.StatusBadGateway, http.StatusServiceUnavailable)
   157  
   158  	performSomeRPCWithRetry := func(ctx context.Context) (*fakeResponse, error) {
   159  		for {
   160  			resp, err := c.PerformSomeRPC(ctx)
   161  			if err != nil {
   162  				if delay, shouldRetry := retryer.Retry(err); shouldRetry {
   163  					if err := gax.Sleep(ctx, delay); err != nil {
   164  						return nil, err
   165  					}
   166  					continue
   167  				}
   168  				return nil, err
   169  			}
   170  			return resp, err
   171  		}
   172  	}
   173  
   174  	// It's recommended to set deadlines on RPCs and around retrying. This is
   175  	// also usually preferred over setting some fixed number of retries: one
   176  	// advantage this has is that backoff settings can be changed independently
   177  	// of the deadline, whereas with a fixed number of retries the deadline
   178  	// would be a constantly-shifting goalpost.
   179  	ctxWithTimeout, cancel := context.WithDeadline(ctx, time.Now().Add(5*time.Minute))
   180  	defer cancel()
   181  
   182  	resp, err := performSomeRPCWithRetry(ctxWithTimeout)
   183  	if err != nil {
   184  		// TODO: handle err
   185  	}
   186  	_ = resp // TODO: use resp if err is nil
   187  }
   188  
   189  func ExampleBackoff() {
   190  	ctx := context.Background()
   191  
   192  	bo := gax.Backoff{
   193  		Initial:    time.Second,
   194  		Max:        time.Minute, // Maximum amount of time between retries.
   195  		Multiplier: 2,
   196  	}
   197  
   198  	performHTTPCallWithRetry := func(ctx context.Context, doHTTPCall func(ctx context.Context) (*http.Response, error)) (*http.Response, error) {
   199  		for {
   200  			resp, err := doHTTPCall(ctx)
   201  			if err != nil {
   202  				// Retry 503 UNAVAILABLE.
   203  				if resp.StatusCode == http.StatusServiceUnavailable {
   204  					if err := gax.Sleep(ctx, bo.Pause()); err != nil {
   205  						return nil, err
   206  					}
   207  					continue
   208  				}
   209  				return nil, err
   210  			}
   211  			return resp, err
   212  		}
   213  	}
   214  
   215  	// It's recommended to set deadlines on HTTP calls and around retrying. This
   216  	// is also usually preferred over setting some fixed number of retries: one
   217  	// advantage this has is that backoff settings can be changed independently
   218  	// of the deadline, whereas with a fixed number of retries the deadline
   219  	// would be a constantly-shifting goalpost.
   220  	ctxWithTimeout, cancel := context.WithDeadline(ctx, time.Now().Add(5*time.Minute))
   221  	defer cancel()
   222  
   223  	resp, err := performHTTPCallWithRetry(ctxWithTimeout, func(ctx context.Context) (*http.Response, error) {
   224  		req, err := http.NewRequest("some-method", "example.com", nil)
   225  		if err != nil {
   226  			return nil, err
   227  		}
   228  		req = req.WithContext(ctx)
   229  		return http.DefaultClient.Do(req)
   230  	})
   231  	if err != nil {
   232  		// TODO: handle err
   233  	}
   234  	_ = resp // TODO: use resp if err is nil
   235  }
   236  
   237  func ExampleProtoJSONStream() {
   238  	var someHTTPCall func() (http.Response, error)
   239  
   240  	res, err := someHTTPCall()
   241  	if err != nil {
   242  		// TODO: handle err
   243  	}
   244  
   245  	// The type of message expected in the stream.
   246  	var typ protoreflect.MessageType = (&structpb.Struct{}).ProtoReflect().Type()
   247  
   248  	stream := gax.NewProtoJSONStreamReader(res.Body, typ)
   249  	defer stream.Close()
   250  
   251  	for {
   252  		m, err := stream.Recv()
   253  		if err != nil {
   254  			break
   255  		}
   256  		// TODO: use resp
   257  		_ = m.(*structpb.Struct)
   258  	}
   259  	if err != io.EOF {
   260  		// TODO: handle err
   261  	}
   262  }
   263  
   264  func ExampleInvoke_grpc() {
   265  	ctx := context.Background()
   266  	c := &fakeClient{}
   267  	opt := gax.WithRetry(func() gax.Retryer {
   268  		return gax.OnCodes([]codes.Code{codes.Unknown, codes.Unavailable}, gax.Backoff{
   269  			Initial:    time.Second,
   270  			Max:        32 * time.Second,
   271  			Multiplier: 2,
   272  		})
   273  	})
   274  
   275  	var resp *fakeResponse
   276  	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
   277  		var err error
   278  		resp, err = c.PerformSomeRPC(ctx)
   279  		return err
   280  	}, opt)
   281  	if err != nil {
   282  		// TODO: handle err
   283  	}
   284  	_ = resp // TODO: use resp if err is nil
   285  }
   286  
   287  func ExampleInvoke_http() {
   288  	ctx := context.Background()
   289  	c := &fakeClient{}
   290  	opt := gax.WithRetry(func() gax.Retryer {
   291  		return gax.OnHTTPCodes(gax.Backoff{
   292  			Initial:    time.Second,
   293  			Max:        32 * time.Second,
   294  			Multiplier: 2,
   295  		}, http.StatusBadGateway, http.StatusServiceUnavailable)
   296  	})
   297  
   298  	var resp *fakeResponse
   299  	err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error {
   300  		var err error
   301  		resp, err = c.PerformSomeRPC(ctx)
   302  		return err
   303  	}, opt)
   304  	if err != nil {
   305  		// TODO: handle err
   306  	}
   307  	_ = resp // TODO: use resp if err is nil
   308  }
   309  

View as plain text