Skip to content

Commit

Permalink
add examples
Browse files Browse the repository at this point in the history
  • Loading branch information
kamilsk committed Feb 17, 2019
1 parent c167d26 commit c80cde8
Show file tree
Hide file tree
Showing 7 changed files with 156 additions and 129 deletions.
152 changes: 52 additions & 100 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@

## Important news

The **[master][legacy]** is a feature frozen branch for versions _3.3.x_ and no longer maintained.
The **[master][legacy]** is a feature frozen branch for versions **3.3.x** and no longer maintained.

```bash
$ dep ensure -add github.com/kamilsk/[email protected]
```

The **[v3][]** branch is a continuation of the **[master][legacy]** branch for versions _v3.4.x_
The **[v3][]** branch is a continuation of the **[master][legacy]** branch for versions **v3.4.x**
to better integration with [Go Modules][gomod].

```bash
Expand All @@ -29,136 +29,87 @@ The **[v4][]** branch is an actual development branch.
$ go get -u github.com/kamilsk/retry/v4
```

Version **v4.x.y** focus on integration with the 🚧 [breaker][] package.

## Usage

### Quick start

#### retry.Retry

```go
var (
response *http.Response
action retry.Action = func(_ uint) error {
var err error
response, err = http.Get("https://github.com/kamilsk/retry")
return err
}
)
var response *http.Response

action := func(uint) error {
var err error
response, err = http.Get("https://github.com/kamilsk/retry")
return err
}

if err := retry.Retry(retry.WithTimeout(time.Minute), action, strategy.Limit(10)); err != nil {
if err := retry.Retry(breaker.BreakByTimeout(time.Minute), action, strategy.Limit(3)); err != nil {
// handle error
return
}
// work with response
```

### Console tool for command execution with retries

This example shows how to repeat console command until successful.

```bash
$ retry --infinite -timeout 10m -backoff=lin:500ms -- /bin/sh -c 'echo "trying..."; exit $((1 + RANDOM % 10 > 5))'
```

[![asciicast](https://asciinema.org/a/150367.png)](https://asciinema.org/a/150367)

See more details [here](cmd/retry).

### Create HTTP client with retry

This example shows how to extend standard http.Client with retry under the hood.
#### retry.Try

```go
type client struct {
base *http.Client
strategies []strategy.Strategy
}
var response *http.Response

func New(timeout time.Duration, strategies ...strategy.Strategy) *client {
return &client{
base: &http.Client{Timeout: timeout},
strategies: strategies,
}
action := func(uint) error {
var err error
response, err = http.Get("https://github.com/kamilsk/retry")
return err
}
interrupter := breaker.MultiplexTwo(
breaker.BreakByTimeout(time.Minute),
breaker.BreakBySignal(os.Interrupt),
)
defer interrupter.Close()

func (c *client) Get(deadline <-chan struct{}, url string) (*http.Response, error) {
var response *http.Response
err := retry.Retry(deadline, func(uint) error {
resp, err := c.base.Get(url)
if err != nil {
return err
}
response = resp
return nil
}, c.strategies...)
return response, err
if err := retry.Try(interrupter, action, strategy.Limit(3)); err != nil {
// handle error
}
// work with response
```

### Control database connection

This example shows how to use retry to restore database connection by `database/sql/driver.Pinger`.
#### retry.TryContext

```go
MustOpen := func() *sql.DB {
db, err := sql.Open("stub", "stub://test")
var response *http.Response

action := func(ctx context.Context, _ uint) error {
req, err := http.NewRequest(http.MethodGet, "https://github.com/kamilsk/retry", nil)
if err != nil {
panic(err)
return err
}
return db
req = req.WithContext(ctx)
response, err = http.DefaultClient.Do(req)
return err
}
ctx := breaker.WithContext(
context.Background(),
breaker.BreakBySignal(os.Interrupt),
)

go func(db *sql.DB, ctx context.Context, shutdown chan<- struct{}, frequency time.Duration,
strategies ...strategy.Strategy) {

defer func() {
if r := recover(); r != nil {
shutdown <- struct{}{}
}
}()

ping := func(uint) error {
return db.Ping()
}

for {
if err := retry.Retry(ctx.Done(), ping, strategies...); err != nil {
panic(err)
}
time.Sleep(frequency)
}
}(MustOpen(), context.Background(), shutdown, time.Millisecond, strategy.Limit(1))
if err := retry.TryContext(ctx, action, strategy.Limit(3)); err != nil {
// handle error
}
// work with response
```

### Use context for cancellation

This example shows how to use context and retry together.

```go
communication := make(chan error)
### Console tool for command execution with retries

go service.Listen(communication)
This example shows how to repeat console command until successful.

action := func(uint) error {
communication <- nil // ping
return <-communication // pong
}
ctx := retry.WithContext(context.Background(), retry.WithTimeout(time.Second))
if err := retry.Retry(ctx.Done(), action, strategy.Delay(time.Millisecond)); err != nil {
// the service does not respond within one second
}
```bash
$ retry --infinite -timeout 10m -backoff=lin:500ms -- /bin/sh -c 'echo "trying..."; exit $((1 + RANDOM % 10 > 5))'
```

### Interrupt execution
[![asciicast](https://asciinema.org/a/150367.png)](https://asciinema.org/a/150367)

```go
interrupter := retry.Multiplex(
retry.WithTimeout(time.Second),
retry.WithSignal(os.Interrupt),
)
if err := retry.Retry(interrupter, func(uint) error { time.Sleep(time.Second); return nil }); err == nil {
panic("press Ctrl+C")
}
// successful interruption
```
See more details [here](cmd/retry).

## Installation

Expand Down Expand Up @@ -199,6 +150,7 @@ made with ❤️ by [OctoLab][octolab]
[v4]: https://github.com/kamilsk/retry/projects/4

[egg]: https://github.com/kamilsk/egg
[breaker]: https://github.com/kamilsk/breaker
[gomod]: https://github.com/golang/go/wiki/Modules

[author]: https://twitter.com/ikamilsk
Expand Down
89 changes: 89 additions & 0 deletions cmd/retry/examples/example_quickstart_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package examples

import (
"context"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"time"

"github.com/kamilsk/breaker"
"github.com/kamilsk/retry/v4"
"github.com/kamilsk/retry/v4/strategy"
)

func ExampleRetryQuickStart() {
var response *http.Response

action := func(uint) error {
var err error
response, err = http.Get("https://github.com/kamilsk/retry")
return err
}

if err := retry.Retry(breaker.BreakByTimeout(time.Minute), action, strategy.Limit(3)); err != nil {
log.Fatal(err)
}

_, _ = io.Copy(ioutil.Discard, response.Body)
_ = response.Body.Close()

fmt.Println(response.Status)
// Output: 200 OK
}

func ExampleTryQuickStart() {
var response *http.Response

action := func(uint) error {
var err error
response, err = http.Get("https://github.com/kamilsk/retry")
return err
}
interrupter := breaker.MultiplexTwo(
breaker.BreakByTimeout(time.Minute),
breaker.BreakBySignal(os.Interrupt),
)
defer interrupter.Close()

if err := retry.Try(interrupter, action, strategy.Limit(3)); err != nil {
log.Fatal(err)
}

_, _ = io.Copy(ioutil.Discard, response.Body)
_ = response.Body.Close()

fmt.Println(response.Status)
// Output: 200 OK
}

func ExampleTryContextQuickStart() {
var response *http.Response

action := func(ctx context.Context, _ uint) error {
req, err := http.NewRequest(http.MethodGet, "https://github.com/kamilsk/retry", nil)
if err != nil {
return err
}
req = req.WithContext(ctx)
response, err = http.DefaultClient.Do(req)
return err
}
ctx := breaker.WithContext(
context.Background(),
breaker.BreakBySignal(os.Interrupt),
)

if err := retry.TryContext(ctx, action, strategy.Limit(3)); err != nil {
log.Fatal(err)
}

_, _ = io.Copy(ioutil.Discard, response.Body)
_ = response.Body.Close()

fmt.Println(response.Status)
// Output: 200 OK
}
3 changes: 2 additions & 1 deletion cmd/retry/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ require (
github.com/briandowns/spinner v0.0.0-20190212173954-5cf08d0ac778
github.com/fatih/color v1.7.0
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/kamilsk/breaker v1.0.0
github.com/kamilsk/go-kit v0.0.0-20190129050637-e2e228a63f53
github.com/kamilsk/retry/v4 v4.0.0-20190216085718-fdc0bc8bdaa4
github.com/kamilsk/retry/v4 v4.0.0-20190217101433-c167d2615827
github.com/mattn/go-colorable v0.1.0 // indirect
github.com/mattn/go-isatty v0.0.4 // indirect
github.com/pkg/errors v0.8.1
Expand Down
6 changes: 4 additions & 2 deletions cmd/retry/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/kamilsk/breaker v1.0.0 h1:5c1qEzpnlqhZtbuHzAb//Pgqe7C3sDAwI2+qbjpsQPw=
github.com/kamilsk/breaker v1.0.0/go.mod h1:v9ySMER3LyAafxjouLF3PLF2bueyFIMXSTWEMa16hdE=
github.com/kamilsk/go-kit v0.0.0-20190129050637-e2e228a63f53 h1:6aZQZCuXNZpWfGa2yjMRU83msPNdlg3sP8pzxHUCQH8=
github.com/kamilsk/go-kit v0.0.0-20190129050637-e2e228a63f53/go.mod h1:h4x6B1xj2nw89zH4cRlNFfH1B1VF67fx/sL/F3ASpNA=
github.com/kamilsk/retry/v4 v4.0.0-20190216085718-fdc0bc8bdaa4 h1:NA5r1n+tOYvaA2mrOnHiq8AdovnyTyUKrrhvmlnlG5Q=
github.com/kamilsk/retry/v4 v4.0.0-20190216085718-fdc0bc8bdaa4/go.mod h1:7Be8o6Y0GLE/BBg0P3+9jA/WLJsY5QovbT5gHS0+xe4=
github.com/kamilsk/retry/v4 v4.0.0-20190217101433-c167d2615827 h1:IlHjdJcAYsR266hdAfYYIovac3j7Hb1Dn8X/hw/Noe0=
github.com/kamilsk/retry/v4 v4.0.0-20190217101433-c167d2615827/go.mod h1:resXTqZ5IUwMFSW/JG2YJ6vp3nf1cX+i6vpjMTZEKXg=
github.com/mattn/go-colorable v0.1.0 h1:v2XXALHHh6zHfYTJ+cSkwtyffnaOyR1MXaA91mTrb8o=
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.4 h1:bnP0vzxcAdeI1zdubAl5PjU6zsERjGZb7raWodagDYs=
Expand Down
10 changes: 6 additions & 4 deletions cmd/retry/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (

"github.com/briandowns/spinner"
"github.com/fatih/color"
"github.com/kamilsk/breaker"
"github.com/kamilsk/retry/v4"
"github.com/pkg/errors"
)
Expand Down Expand Up @@ -114,11 +115,12 @@ func (app tool) Run() {
cmd.Stderr, cmd.Stdout = stderr, stdout
return errors.WithStack(cmd.Run())
}
deadline := retry.Multiplex(
retry.WithTimeout(result.Timeout),
retry.WithSignal(os.Interrupt),
interrupter := breaker.MultiplexTwo(
breaker.BreakByTimeout(result.Timeout),
breaker.BreakBySignal(os.Interrupt),
)
if err = retry.Retry(deadline, action, result.Strategies...); err != nil {

if err = retry.Retry(interrupter, action, result.Strategies...); err != nil {
app.Shutdown(failed)
return
}
Expand Down
19 changes: 0 additions & 19 deletions cmd/retry/main_test.go

This file was deleted.

6 changes: 3 additions & 3 deletions cmd/retry/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ type Command struct {
Debug bool
Notify bool
Args []string
Strategies []strategy.Strategy
Strategies []func(attempt uint, err error) bool
}

var (
Expand Down Expand Up @@ -94,8 +94,8 @@ func parse(output io.Writer, binary string, arguments ...string) (Command, error
return cmd, nil
}

func handle(flags []*flag.Flag) ([]strategy.Strategy, error) {
strategies := make([]strategy.Strategy, 0, len(flags))
func handle(flags []*flag.Flag) ([]func(attempt uint, err error) bool, error) {
strategies := make([]func(attempt uint, err error) bool, 0, len(flags))

for _, f := range flags {
if c, ok := compliance[f.Name]; ok {
Expand Down

0 comments on commit c80cde8

Please sign in to comment.