| Index: common/retry/transient/transient.go
|
| diff --git a/common/retry/transient/transient.go b/common/retry/transient/transient.go
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..40b29594947c906690d67e6c1743b62103ef93f3
|
| --- /dev/null
|
| +++ b/common/retry/transient/transient.go
|
| @@ -0,0 +1,68 @@
|
| +// Copyright 2015 The LUCI Authors. All rights reserved.
|
| +// Use of this source code is governed under the Apache License, Version 2.0
|
| +// that can be found in the LICENSE file.
|
| +
|
| +// Package transient allows you to tag and retry 'transient' errors (i.e.
|
| +// non-permanent errors which may resolve themselves by trying an operation
|
| +// again). This should be used on errors due to network flake, improperly
|
| +// responsive remote servers (e.g. status 500), unusual timeouts, etc. where
|
| +// there's no concrete knowledge that something is permanently wrong.
|
| +//
|
| +// Said another way, transient errors appear to resolve themselves with nothing
|
| +// other than the passage of time.
|
| +package transient
|
| +
|
| +import (
|
| + "time"
|
| +
|
| + "github.com/luci/luci-go/common/errors"
|
| + "github.com/luci/luci-go/common/retry"
|
| + "golang.org/x/net/context"
|
| +)
|
| +
|
| +// transientOnlyIterator is an Iterator implementation that only retries errors
|
| +// if they are tagged with `transient.Tag`.
|
| +type transientOnlyIterator struct {
|
| + retry.Iterator // The wrapped Iterator.
|
| +}
|
| +
|
| +func (i *transientOnlyIterator) Next(ctx context.Context, err error) time.Duration {
|
| + if !Tag.In(err) {
|
| + return retry.Stop
|
| + }
|
| + return i.Iterator.Next(ctx, err)
|
| +}
|
| +
|
| +// Only returns a retry.Iterator that wraps another retry.Iterator. It
|
| +// will fall through to the wrapped Iterator ONLY if an error with the
|
| +// transient.Tag is encountered; otherwise, it will not retry.
|
| +//
|
| +// Returns nil if f is nil.
|
| +//
|
| +// Example:
|
| +// err := retry.Retry(c, transient.Only(retry.Default), func() error {
|
| +// if condition == "red" {
|
| +// // This error isn't transient, so it won't be retried.
|
| +// return errors.New("fatal bad condition")
|
| +// } elif condition == "green" {
|
| +// // This isn't an error, so it won't be retried.
|
| +// return nil
|
| +// }
|
| +// // This will get retried, because it's transient.
|
| +// return errors.New("dunno what's wrong", transient.Tag)
|
| +// })
|
| +func Only(next retry.Factory) retry.Factory {
|
| + if next == nil {
|
| + return nil
|
| + }
|
| + return func() retry.Iterator {
|
| + if it := next(); it != nil {
|
| + return &transientOnlyIterator{it}
|
| + }
|
| + return nil
|
| + }
|
| +}
|
| +
|
| +// Tag is used to indicate that an error is transient (i.e. something is
|
| +// temporarially wrong).
|
| +var Tag = errors.BoolTag{Key: errors.NewTagKey("this error is temporary")}
|
|
|