Skip to content

Instantly share code, notes, and snippets.

@mikemadisonweb
Forked from huttotw/requests.go
Created August 17, 2017 07:22
Show Gist options
  • Save mikemadisonweb/d27ec0a679c287646e2e75bddde63943 to your computer and use it in GitHub Desktop.
Save mikemadisonweb/d27ec0a679c287646e2e75bddde63943 to your computer and use it in GitHub Desktop.
Concurrent requests, with max retries and error handling
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