go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/common/retry/transient/transient.go (about) 1 // Copyright 2015 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 // Package transient allows you to tag and retry 'transient' errors (i.e. 16 // non-permanent errors which may resolve themselves by trying an operation 17 // again). This should be used on errors due to network flake, improperly 18 // responsive remote servers (e.g. status 500), unusual timeouts, etc. where 19 // there's no concrete knowledge that something is permanently wrong. 20 // 21 // Said another way, transient errors appear to resolve themselves with nothing 22 // other than the passage of time. 23 package transient 24 25 import ( 26 "context" 27 "time" 28 29 "go.chromium.org/luci/common/errors" 30 "go.chromium.org/luci/common/retry" 31 ) 32 33 // transientOnlyIterator is an Iterator implementation that only retries errors 34 // if they are tagged with `transient.Tag`. 35 type transientOnlyIterator struct { 36 retry.Iterator // The wrapped Iterator. 37 } 38 39 func (i *transientOnlyIterator) Next(ctx context.Context, err error) time.Duration { 40 if !Tag.In(err) { 41 return retry.Stop 42 } 43 return i.Iterator.Next(ctx, err) 44 } 45 46 // Only returns a retry.Iterator that wraps another retry.Iterator. It 47 // will fall through to the wrapped Iterator ONLY if an error with the 48 // transient.Tag is encountered; otherwise, it will not retry. 49 // 50 // Returns nil if f is nil. 51 // 52 // Example: 53 // 54 // err := retry.Retry(c, transient.Only(retry.Default), func() error { 55 // if condition == "red" { 56 // // This error isn't transient, so it won't be retried. 57 // return errors.New("fatal bad condition") 58 // } elif condition == "green" { 59 // // This isn't an error, so it won't be retried. 60 // return nil 61 // } 62 // // This will get retried, because it's transient. 63 // return errors.New("dunno what's wrong", transient.Tag) 64 // }) 65 func Only(next retry.Factory) retry.Factory { 66 if next == nil { 67 return nil 68 } 69 return func() retry.Iterator { 70 if it := next(); it != nil { 71 return &transientOnlyIterator{it} 72 } 73 return nil 74 } 75 } 76 77 // Tag is used to indicate that an error is transient (i.e. something is 78 // temporarially wrong). 79 var Tag = errors.BoolTag{Key: errors.NewTagKey("this error is temporary")}