github.com/Jeffail/benthos/v3@v3.65.0/public/service/config_backoff.go (about)

     1  package service
     2  
     3  import (
     4  	"github.com/cenkalti/backoff/v4"
     5  )
     6  
     7  // NewBackOffField defines a new object type config field that describes an
     8  // exponential back off policy, often used for timing retry attempts. It is then
     9  // possible to extract a *backoff.ExponentialBackOff from the resulting parsed
    10  // config with the method FieldBackOff.
    11  //
    12  // It is possible to configure a back off policy that has no upper bound (no
    13  // maximum elapsed time set). In cases where this would be problematic the field
    14  // allowUnbounded should be set `false` in order to add linting rules that
    15  // ensure an upper bound is set.
    16  //
    17  // The defaults struct is optional, and if provided will be used to establish
    18  // default values for time interval fields. Otherwise the chosen defaults result
    19  // in one minute of retry attempts, starting at 500ms intervals.
    20  func NewBackOffField(name string, allowUnbounded bool, defaults *backoff.ExponentialBackOff) *ConfigField {
    21  	var (
    22  		initDefault       = "500ms"
    23  		maxDefault        = "10s"
    24  		maxElapsedDefault = "1m"
    25  	)
    26  	if defaults != nil {
    27  		initDefault = defaults.InitialInterval.String()
    28  		maxDefault = defaults.MaxInterval.String()
    29  		maxElapsedDefault = defaults.MaxElapsedTime.String()
    30  	}
    31  
    32  	maxElapsedTime := NewDurationField("max_elapsed_time").
    33  		Description("The maximum overall period of time to spend on retry attempts before the request is aborted.").
    34  		Default(maxElapsedDefault).Example("1m").Example("1h")
    35  	if allowUnbounded {
    36  		maxElapsedTime.field.Description += " Setting this value to a zeroed duration (such as `0s`) will result in unbounded retries."
    37  	}
    38  
    39  	// TODO: Add linting rule to ensure we aren't unbounded if necessary.
    40  	return NewObjectField(name,
    41  		NewDurationField("initial_interval").
    42  			Description("The initial period to wait between retry attempts.").
    43  			Default(initDefault).Example("50ms").Example("1s"),
    44  		NewDurationField("max_interval").
    45  			Description("The maximum period to wait between retry attempts").
    46  			Default(maxDefault).Example("5s").Example("1m"),
    47  		maxElapsedTime,
    48  	).Description("Determine time intervals and cut offs for retry attempts.")
    49  }
    50  
    51  // FieldBackOff accesses a field from a parsed config that was defined with
    52  // NewBackoffField and returns a *backoff.ExponentialBackOff, or an error if the
    53  // configuration was invalid.
    54  func (p *ParsedConfig) FieldBackOff(path ...string) (*backoff.ExponentialBackOff, error) {
    55  	b := backoff.NewExponentialBackOff()
    56  
    57  	var err error
    58  	if b.InitialInterval, err = p.FieldDuration(append(path, "initial_interval")...); err != nil {
    59  		return nil, err
    60  	}
    61  	if b.MaxInterval, err = p.FieldDuration(append(path, "max_interval")...); err != nil {
    62  		return nil, err
    63  	}
    64  	if b.MaxElapsedTime, err = p.FieldDuration(append(path, "max_elapsed_time")...); err != nil {
    65  		return nil, err
    66  	}
    67  
    68  	return b, nil
    69  }
    70  
    71  // NewBackOffToggledField defines a new object type config field that describes
    72  // an exponential back off policy, often used for timing retry attempts. It is
    73  // then possible to extract a *backoff.ExponentialBackOff from the resulting
    74  // parsed config with the method FieldBackOff. This Toggled variant includes a
    75  // field `enabled` that is `false` by default.
    76  //
    77  // It is possible to configure a back off policy that has no upper bound (no
    78  // maximum elapsed time set). In cases where this would be problematic the field
    79  // allowUnbounded should be set `false` in order to add linting rules that
    80  // ensure an upper bound is set.
    81  //
    82  // The defaults struct is optional, and if provided will be used to establish
    83  // default values for time interval fields. Otherwise the chosen defaults result
    84  // in one minute of retry attempts, starting at 500ms intervals.
    85  func NewBackOffToggledField(name string, allowUnbounded bool, defaults *backoff.ExponentialBackOff) *ConfigField {
    86  	var (
    87  		initDefault       = "500ms"
    88  		maxDefault        = "10s"
    89  		maxElapsedDefault = "1m"
    90  	)
    91  	if defaults != nil {
    92  		initDefault = defaults.InitialInterval.String()
    93  		maxDefault = defaults.MaxInterval.String()
    94  		maxElapsedDefault = defaults.MaxElapsedTime.String()
    95  	}
    96  
    97  	maxElapsedTime := NewDurationField("max_elapsed_time").
    98  		Description("The maximum overall period of time to spend on retry attempts before the request is aborted.").
    99  		Default(maxElapsedDefault).Example("1m").Example("1h")
   100  	if allowUnbounded {
   101  		maxElapsedTime.field.Description += " Setting this value to a zeroed duration (such as `0s`) will result in unbounded retries."
   102  	}
   103  
   104  	// TODO: Add linting rule to ensure we aren't unbounded if necessary.
   105  	return NewObjectField(name,
   106  		NewBoolField("enabled").
   107  			Description("Whether retries should be enabled.").
   108  			Default(false),
   109  		NewDurationField("initial_interval").
   110  			Description("The initial period to wait between retry attempts.").
   111  			Default(initDefault).Example("50ms").Example("1s"),
   112  		NewDurationField("max_interval").
   113  			Description("The maximum period to wait between retry attempts").
   114  			Default(maxDefault).Example("5s").Example("1m"),
   115  		maxElapsedTime,
   116  	).Description("Determine time intervals and cut offs for retry attempts.")
   117  }
   118  
   119  // FieldBackOffToggled accesses a field from a parsed config that was defined
   120  // with NewBackOffField and returns a *backoff.ExponentialBackOff and a boolean
   121  // flag indicating whether retries are explicitly enabled, or an error if the
   122  // configuration was invalid.
   123  func (p *ParsedConfig) FieldBackOffToggled(path ...string) (boff *backoff.ExponentialBackOff, enabled bool, err error) {
   124  	boff = backoff.NewExponentialBackOff()
   125  
   126  	if enabled, err = p.FieldBool(append(path, "enabled")...); err != nil {
   127  		return
   128  	}
   129  	if boff.InitialInterval, err = p.FieldDuration(append(path, "initial_interval")...); err != nil {
   130  		return
   131  	}
   132  	if boff.MaxInterval, err = p.FieldDuration(append(path, "max_interval")...); err != nil {
   133  		return
   134  	}
   135  	if boff.MaxElapsedTime, err = p.FieldDuration(append(path, "max_elapsed_time")...); err != nil {
   136  		return
   137  	}
   138  
   139  	return
   140  }