From 424e0cf73c4dc6c719c9e682b39ec2d17f63820c Mon Sep 17 00:00:00 2001 From: Umputun Date: Fri, 8 Jun 2018 03:12:22 -0500 Subject: [PATCH] proxy client with retry --- app/rest/proxy/avatar.go | 20 ++++++++++++++++++-- app/rest/proxy/avatar_store.go | 6 +++--- app/rest/proxy/avatar_test.go | 22 ++++++++++++++++++++++ app/rest/proxy/image.go | 8 +++++++- 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/app/rest/proxy/avatar.go b/app/rest/proxy/avatar.go index 8b4ec746..0c085a33 100644 --- a/app/rest/proxy/avatar.go +++ b/app/rest/proxy/avatar.go @@ -40,10 +40,16 @@ func (p *Avatar) Put(u store.User) (avatarURL string, err error) { // load avatar from remote location client := http.Client{Timeout: 10 * time.Second} - resp, err := client.Get(u.Picture) + var resp *http.Response + err = retry(5, time.Second, func() error { + var e error + resp, e = client.Get(u.Picture) + return e + }) if err != nil { - return "", errors.Wrapf(err, "failed to get avatar for user %s from %s", u.ID, u.Picture) + return "", errors.Wrap(err, "failed to fetch avatar from the orig") } + defer func() { if e := resp.Body.Close(); e != nil { log.Printf("[WARN] can't close response body, %s", e) @@ -106,3 +112,13 @@ func (p *Avatar) Routes(middlewares ...func(http.Handler) http.Handler) (string, return p.RoutePath, router } + +func retry(retries int, delay time.Duration, fn func() error) (err error) { + for i := 0; i < retries; i++ { + if err = fn(); err == nil { + return nil + } + time.Sleep(delay) + } + return errors.Wrap(err, "retry failed") +} diff --git a/app/rest/proxy/avatar_store.go b/app/rest/proxy/avatar_store.go index 2db744ea..783544a4 100644 --- a/app/rest/proxy/avatar_store.go +++ b/app/rest/proxy/avatar_store.go @@ -17,8 +17,8 @@ import ( // AvatarStore defines interface to store and serve avatars type AvatarStore interface { - Put(userID string, reader io.Reader) (avatarURL string, err error) - Get(userID string) (reader io.ReadCloser, size int, err error) + Put(userID string, reader io.Reader) (avatar string, err error) + Get(avatar string) (reader io.ReadCloser, size int, err error) } // FSAvatarStore implements AvatarStore for local file system @@ -33,7 +33,7 @@ func NewFSAvatarStore(storePath string) *FSAvatarStore { return &FSAvatarStore{storePath: storePath} } -// Put avatar for userID to file and return avatar name +// Put avatar for userID to file and return avatar's file name (base), like 12345678.image func (fs *FSAvatarStore) Put(userID string, reader io.Reader) (avatar string, err error) { id := store.EncodeID(userID) diff --git a/app/rest/proxy/avatar_test.go b/app/rest/proxy/avatar_test.go index 8da97a92..bd8d6c25 100644 --- a/app/rest/proxy/avatar_test.go +++ b/app/rest/proxy/avatar_test.go @@ -2,6 +2,7 @@ package proxy import ( "bytes" + "errors" "fmt" "io" "log" @@ -9,6 +10,7 @@ import ( "net/http/httptest" "os" "testing" + "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -118,3 +120,23 @@ func TestAvatar_Routes(t *testing.T) { assert.Equal(t, int64(21), sz) assert.Equal(t, "some picture bin data", bb.String()) } + +func TestAvatar_Retry(t *testing.T) { + i := 0 + err := retry(5, time.Millisecond, func() error { + if i == 3 { + return nil + } + i++ + return errors.New("err") + }) + assert.Nil(t, err) + assert.Equal(t, 3, i) + + st := time.Now() + err = retry(5, time.Millisecond, func() error { + return errors.New("err") + }) + assert.NotNil(t, err) + assert.True(t, time.Since(st) >= time.Microsecond*5) +} diff --git a/app/rest/proxy/image.go b/app/rest/proxy/image.go index 3b211aa4..087c235e 100644 --- a/app/rest/proxy/image.go +++ b/app/rest/proxy/image.go @@ -48,8 +48,14 @@ func (p Image) Routes() chi.Router { rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't decode image url") return } + client := http.Client{Timeout: 30 * time.Second} - resp, err := client.Get(string(src)) + var resp *http.Response + err = retry(5, time.Second, func() error { + var e error + resp, e = client.Get(string(src)) + return e + }) if err != nil { rest.SendErrorJSON(w, r, http.StatusBadRequest, err, "can't get image "+string(src)) return