-
-
Save mikemadisonweb/d27ec0a679c287646e2e75bddde63943 to your computer and use it in GitHub Desktop.
Concurrent requests, with max retries and error handling
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"errors" | |
"fmt" | |
"math/rand" | |
"sync" | |
"time" | |
) | |
// request describes the things we need to know about a request before trying | |
// to call it | |
type request struct { | |
handler func() error | |
maxRetries int | |
name string | |
} | |
// requestGoogle will simulate a request | |
func requestGoogle() error { | |
err := randomError() | |
if err != nil { | |
return err | |
} | |
time.Sleep(time.Second * 2) | |
fmt.Println("google finished") | |
return nil | |
} | |
// requestYahoo will simulate a request | |
func requestYahoo() error { | |
err := randomError() | |
if err != nil { | |
return err | |
} | |
time.Sleep(time.Second * 1) | |
fmt.Println("yahoo finished") | |
return nil | |
} | |
// requestBing will simulate a request | |
func requestBing() error { | |
err := randomError() | |
if err != nil { | |
return err | |
} | |
time.Sleep(time.Second * 4) | |
fmt.Println("bing finished") | |
return nil | |
} | |
func main() { | |
// Create a slice of all the requests we want to make | |
requests := []*request{ | |
&request{ | |
handler: requestGoogle, | |
maxRetries: 3, | |
name: "google", | |
}, | |
&request{ | |
handler: requestBing, | |
maxRetries: 3, | |
name: "bing", | |
}, | |
&request{ | |
handler: requestYahoo, | |
maxRetries: 3, | |
name: "yahoo", | |
}, | |
} | |
// We call our retry function for as many requests as we have, and wait until they were | |
// successful, or hit their max retries. | |
var wg sync.WaitGroup | |
for i := 0; i < len(requests); i++ { | |
wg.Add(1) | |
go func(index int) { | |
// retry if we are not successful | |
err := retry(requests[index].maxRetries, requests[index].handler, requests[index].name) | |
if err != nil { | |
fmt.Println(err) | |
} | |
// retry is finished, don't care whether or not we were successful at this point | |
wg.Done() | |
}(i) | |
} | |
// Wait until all the requests are finished, then exit | |
wg.Wait() | |
} | |
// retry will try up to attemps, and return the last error if it is never succesful | |
func retry(attempts int, callback func() error, name string) (err error) { | |
for i := 0; i < attempts; i++ { | |
err = callback() | |
if err == nil { | |
return nil | |
} | |
fmt.Printf("retrying %s ...\n", name) | |
} | |
return fmt.Errorf("%s failed after %d attempts, last error: %s", name, attempts, err) | |
} | |
// randomError spins the wheel and checks to see if you will return an error, or nil | |
func randomError() error { | |
rand.Seed(time.Now().UnixNano()) | |
n := rand.Intn(3) | |
if n == 1 { | |
return errors.New("you got unlucky") | |
} | |
return nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment