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")} |