...

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

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

     1  // Copyright 2016, 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
    31  
    32  import (
    33  	"context"
    34  	"strings"
    35  	"time"
    36  
    37  	"github.com/googleapis/gax-go/v2/apierror"
    38  )
    39  
    40  // APICall is a user defined call stub.
    41  type APICall func(context.Context, CallSettings) error
    42  
    43  // Invoke calls the given APICall, performing retries as specified by opts, if
    44  // any.
    45  func Invoke(ctx context.Context, call APICall, opts ...CallOption) error {
    46  	var settings CallSettings
    47  	for _, opt := range opts {
    48  		opt.Resolve(&settings)
    49  	}
    50  	return invoke(ctx, call, settings, Sleep)
    51  }
    52  
    53  // Sleep is similar to time.Sleep, but it can be interrupted by ctx.Done() closing.
    54  // If interrupted, Sleep returns ctx.Err().
    55  func Sleep(ctx context.Context, d time.Duration) error {
    56  	t := time.NewTimer(d)
    57  	select {
    58  	case <-ctx.Done():
    59  		t.Stop()
    60  		return ctx.Err()
    61  	case <-t.C:
    62  		return nil
    63  	}
    64  }
    65  
    66  type sleeper func(ctx context.Context, d time.Duration) error
    67  
    68  // invoke implements Invoke, taking an additional sleeper argument for testing.
    69  func invoke(ctx context.Context, call APICall, settings CallSettings, sp sleeper) error {
    70  	var retryer Retryer
    71  
    72  	// Only use the value provided via WithTimeout if the context doesn't
    73  	// already have a deadline. This is important for backwards compatibility if
    74  	// the user already set a deadline on the context given to Invoke.
    75  	if _, ok := ctx.Deadline(); !ok && settings.timeout != 0 {
    76  		c, cc := context.WithTimeout(ctx, settings.timeout)
    77  		defer cc()
    78  		ctx = c
    79  	}
    80  
    81  	for {
    82  		err := call(ctx, settings)
    83  		if err == nil {
    84  			return nil
    85  		}
    86  		// Never retry permanent certificate errors. (e.x. if ca-certificates
    87  		// are not installed). We should only make very few, targeted
    88  		// exceptions: many (other) status=Unavailable should be retried, such
    89  		// as if there's a network hiccup, or the internet goes out for a
    90  		// minute. This is also why here we are doing string parsing instead of
    91  		// simply making Unavailable a non-retried code elsewhere.
    92  		if strings.Contains(err.Error(), "x509: certificate signed by unknown authority") {
    93  			return err
    94  		}
    95  		if apierr, ok := apierror.FromError(err); ok {
    96  			err = apierr
    97  		}
    98  		if settings.Retry == nil {
    99  			return err
   100  		}
   101  		if retryer == nil {
   102  			if r := settings.Retry(); r != nil {
   103  				retryer = r
   104  			} else {
   105  				return err
   106  			}
   107  		}
   108  		if d, ok := retryer.Retry(err); !ok {
   109  			return err
   110  		} else if err = sp(ctx, d); err != nil {
   111  			return err
   112  		}
   113  	}
   114  }
   115  

View as plain text