Update vendor with go dep
This fix updates vendor with `go dep` Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
This commit is contained in:
parent
e08fb277fa
commit
6e3be7f3d5
8538 changed files with 2537051 additions and 81115 deletions
24
vendor/github.com/eapache/go-resiliency/.gitignore
generated
vendored
Normal file
24
vendor/github.com/eapache/go-resiliency/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
7
vendor/github.com/eapache/go-resiliency/.travis.yml
generated
vendored
Normal file
7
vendor/github.com/eapache/go-resiliency/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.1
|
||||
- 1.2
|
||||
- 1.3
|
||||
- 1.4
|
22
vendor/github.com/eapache/go-resiliency/LICENSE
generated
vendored
Normal file
22
vendor/github.com/eapache/go-resiliency/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Evan Huus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
20
vendor/github.com/eapache/go-resiliency/README.md
generated
vendored
Normal file
20
vendor/github.com/eapache/go-resiliency/README.md
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
go-resiliency
|
||||
=============
|
||||
|
||||
[](https://travis-ci.org/eapache/go-resiliency)
|
||||
[](https://godoc.org/github.com/eapache/go-resiliency)
|
||||
|
||||
Resiliency patterns for golang.
|
||||
Based in part on [Hystrix](https://github.com/Netflix/Hystrix),
|
||||
[Semian](https://github.com/Shopify/semian), and others.
|
||||
|
||||
Currently implemented patterns include:
|
||||
- circuit-breaker (in the `breaker` directory)
|
||||
- semaphore (in the `semaphore` directory)
|
||||
- deadline/timeout (in the `deadline` directory)
|
||||
- batching (in the `batcher` directory)
|
||||
- retriable (in the `retrier` directory)
|
||||
|
||||
Follows semantic versioning using https://gopkg.in/ - import from
|
||||
[`gopkg.in/eapache/go-resiliency.v1`](https://gopkg.in/eapache/go-resiliency.v1)
|
||||
for guaranteed API stability.
|
30
vendor/github.com/eapache/go-resiliency/batcher/README.md
generated
vendored
Normal file
30
vendor/github.com/eapache/go-resiliency/batcher/README.md
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
batcher
|
||||
=======
|
||||
|
||||
[](https://travis-ci.org/eapache/go-resiliency)
|
||||
[](https://godoc.org/github.com/eapache/go-resiliency/batcher)
|
||||
|
||||
The batching resiliency pattern for golang.
|
||||
|
||||
Creating a batcher takes two parameters:
|
||||
- the timeout to wait while collecting a batch
|
||||
- the function to run once a batch has been collected
|
||||
|
||||
You can also optionally set a prefilter to fail queries before they enter the
|
||||
batch.
|
||||
|
||||
```go
|
||||
b := batcher.New(10*time.Millisecond, func(params []interface{}) error {
|
||||
// do something with the batch of parameters
|
||||
return nil
|
||||
})
|
||||
|
||||
b.Prefilter(func(param interface{}) error {
|
||||
// do some sort of sanity check on the parameter, and return an error if it fails
|
||||
return nil
|
||||
})
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
go b.Run(i)
|
||||
}
|
||||
```
|
108
vendor/github.com/eapache/go-resiliency/batcher/batcher.go
generated
vendored
Normal file
108
vendor/github.com/eapache/go-resiliency/batcher/batcher.go
generated
vendored
Normal file
|
@ -0,0 +1,108 @@
|
|||
// Package batcher implements the batching resiliency pattern for Go.
|
||||
package batcher
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type work struct {
|
||||
param interface{}
|
||||
future chan error
|
||||
}
|
||||
|
||||
// Batcher implements the batching resiliency pattern
|
||||
type Batcher struct {
|
||||
timeout time.Duration
|
||||
prefilter func(interface{}) error
|
||||
|
||||
lock sync.Mutex
|
||||
submit chan *work
|
||||
doWork func([]interface{}) error
|
||||
}
|
||||
|
||||
// New constructs a new batcher that will batch all calls to Run that occur within
|
||||
// `timeout` time before calling doWork just once for the entire batch. The doWork
|
||||
// function must be safe to run concurrently with itself as this may occur, especially
|
||||
// when the timeout is small.
|
||||
func New(timeout time.Duration, doWork func([]interface{}) error) *Batcher {
|
||||
return &Batcher{
|
||||
timeout: timeout,
|
||||
doWork: doWork,
|
||||
}
|
||||
}
|
||||
|
||||
// Run runs the work function with the given parameter, possibly
|
||||
// including it in a batch with other calls to Run that occur within the
|
||||
// specified timeout. It is safe to call Run concurrently on the same batcher.
|
||||
func (b *Batcher) Run(param interface{}) error {
|
||||
if b.prefilter != nil {
|
||||
if err := b.prefilter(param); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if b.timeout == 0 {
|
||||
return b.doWork([]interface{}{param})
|
||||
}
|
||||
|
||||
w := &work{
|
||||
param: param,
|
||||
future: make(chan error, 1),
|
||||
}
|
||||
|
||||
b.submitWork(w)
|
||||
|
||||
return <-w.future
|
||||
}
|
||||
|
||||
// Prefilter specifies an optional function that can be used to run initial checks on parameters
|
||||
// passed to Run before being added to the batch. If the prefilter returns a non-nil error,
|
||||
// that error is returned immediately from Run and the batcher is not invoked. A prefilter
|
||||
// cannot safely be specified for a batcher if Run has already been invoked. The filter function
|
||||
// specified must be concurrency-safe.
|
||||
func (b *Batcher) Prefilter(filter func(interface{}) error) {
|
||||
b.prefilter = filter
|
||||
}
|
||||
|
||||
func (b *Batcher) submitWork(w *work) {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
if b.submit == nil {
|
||||
b.submit = make(chan *work, 4)
|
||||
go b.batch()
|
||||
}
|
||||
|
||||
b.submit <- w
|
||||
}
|
||||
|
||||
func (b *Batcher) batch() {
|
||||
var params []interface{}
|
||||
var futures []chan error
|
||||
input := b.submit
|
||||
|
||||
go b.timer()
|
||||
|
||||
for work := range input {
|
||||
params = append(params, work.param)
|
||||
futures = append(futures, work.future)
|
||||
}
|
||||
|
||||
ret := b.doWork(params)
|
||||
|
||||
for _, future := range futures {
|
||||
future <- ret
|
||||
close(future)
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Batcher) timer() {
|
||||
time.Sleep(b.timeout)
|
||||
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
close(b.submit)
|
||||
b.submit = nil
|
||||
}
|
123
vendor/github.com/eapache/go-resiliency/batcher/batcher_test.go
generated
vendored
Normal file
123
vendor/github.com/eapache/go-resiliency/batcher/batcher_test.go
generated
vendored
Normal file
|
@ -0,0 +1,123 @@
|
|||
package batcher
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var errSomeError = errors.New("errSomeError")
|
||||
|
||||
func returnsError(params []interface{}) error {
|
||||
return errSomeError
|
||||
}
|
||||
|
||||
func returnsSuccess(params []interface{}) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestBatcherSuccess(t *testing.T) {
|
||||
b := New(10*time.Millisecond, returnsSuccess)
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
if err := b.Run(nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
b = New(0, returnsSuccess)
|
||||
for i := 0; i < 10; i++ {
|
||||
if err := b.Run(nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBatcherError(t *testing.T) {
|
||||
b := New(10*time.Millisecond, returnsError)
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
if err := b.Run(nil); err != errSomeError {
|
||||
t.Error(err)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestBatcherPrefilter(t *testing.T) {
|
||||
b := New(1*time.Millisecond, returnsSuccess)
|
||||
|
||||
b.Prefilter(func(param interface{}) error {
|
||||
if param == nil {
|
||||
return errSomeError
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := b.Run(nil); err != errSomeError {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err := b.Run(1); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBatcherMultipleBatches(t *testing.T) {
|
||||
var iters uint32
|
||||
|
||||
b := New(10*time.Millisecond, func(params []interface{}) error {
|
||||
atomic.AddUint32(&iters, 1)
|
||||
return nil
|
||||
})
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
|
||||
for group := 0; group < 5; group++ {
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
if err := b.Run(nil); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
wg.Done()
|
||||
}()
|
||||
}
|
||||
time.Sleep(15 * time.Millisecond)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
if iters != 5 {
|
||||
t.Error("Wrong number of iters:", iters)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleBatcher() {
|
||||
b := New(10*time.Millisecond, func(params []interface{}) error {
|
||||
// do something with the batch of parameters
|
||||
return nil
|
||||
})
|
||||
|
||||
b.Prefilter(func(param interface{}) error {
|
||||
// do some sort of sanity check on the parameter, and return an error if it fails
|
||||
return nil
|
||||
})
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
go b.Run(i)
|
||||
}
|
||||
}
|
33
vendor/github.com/eapache/go-resiliency/breaker/README.md
generated
vendored
Normal file
33
vendor/github.com/eapache/go-resiliency/breaker/README.md
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
circuit-breaker
|
||||
===============
|
||||
|
||||
[](https://travis-ci.org/eapache/go-resiliency)
|
||||
[](https://godoc.org/github.com/eapache/go-resiliency/breaker)
|
||||
|
||||
The circuit-breaker resiliency pattern for golang.
|
||||
|
||||
Creating a breaker takes three parameters:
|
||||
- error threshold (for opening the breaker)
|
||||
- success threshold (for closing the breaker)
|
||||
- timeout (how long to keep the breaker open)
|
||||
|
||||
```go
|
||||
b := breaker.New(3, 1, 5*time.Second)
|
||||
|
||||
for {
|
||||
result := b.Run(func() error {
|
||||
// communicate with some external service and
|
||||
// return an error if the communication failed
|
||||
return nil
|
||||
})
|
||||
|
||||
switch result {
|
||||
case nil:
|
||||
// success!
|
||||
case breaker.ErrBreakerOpen:
|
||||
// our function wasn't run because the breaker was open
|
||||
default:
|
||||
// some other error
|
||||
}
|
||||
}
|
||||
```
|
161
vendor/github.com/eapache/go-resiliency/breaker/breaker.go
generated
vendored
Normal file
161
vendor/github.com/eapache/go-resiliency/breaker/breaker.go
generated
vendored
Normal file
|
@ -0,0 +1,161 @@
|
|||
// Package breaker implements the circuit-breaker resiliency pattern for Go.
|
||||
package breaker
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ErrBreakerOpen is the error returned from Run() when the function is not executed
|
||||
// because the breaker is currently open.
|
||||
var ErrBreakerOpen = errors.New("circuit breaker is open")
|
||||
|
||||
const (
|
||||
closed uint32 = iota
|
||||
open
|
||||
halfOpen
|
||||
)
|
||||
|
||||
// Breaker implements the circuit-breaker resiliency pattern
|
||||
type Breaker struct {
|
||||
errorThreshold, successThreshold int
|
||||
timeout time.Duration
|
||||
|
||||
lock sync.Mutex
|
||||
state uint32
|
||||
errors, successes int
|
||||
lastError time.Time
|
||||
}
|
||||
|
||||
// New constructs a new circuit-breaker that starts closed.
|
||||
// From closed, the breaker opens if "errorThreshold" errors are seen
|
||||
// without an error-free period of at least "timeout". From open, the
|
||||
// breaker half-closes after "timeout". From half-open, the breaker closes
|
||||
// after "successThreshold" consecutive successes, or opens on a single error.
|
||||
func New(errorThreshold, successThreshold int, timeout time.Duration) *Breaker {
|
||||
return &Breaker{
|
||||
errorThreshold: errorThreshold,
|
||||
successThreshold: successThreshold,
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// Run will either return ErrBreakerOpen immediately if the circuit-breaker is
|
||||
// already open, or it will run the given function and pass along its return
|
||||
// value. It is safe to call Run concurrently on the same Breaker.
|
||||
func (b *Breaker) Run(work func() error) error {
|
||||
state := atomic.LoadUint32(&b.state)
|
||||
|
||||
if state == open {
|
||||
return ErrBreakerOpen
|
||||
}
|
||||
|
||||
return b.doWork(state, work)
|
||||
}
|
||||
|
||||
// Go will either return ErrBreakerOpen immediately if the circuit-breaker is
|
||||
// already open, or it will run the given function in a separate goroutine.
|
||||
// If the function is run, Go will return nil immediately, and will *not* return
|
||||
// the return value of the function. It is safe to call Go concurrently on the
|
||||
// same Breaker.
|
||||
func (b *Breaker) Go(work func() error) error {
|
||||
state := atomic.LoadUint32(&b.state)
|
||||
|
||||
if state == open {
|
||||
return ErrBreakerOpen
|
||||
}
|
||||
|
||||
// errcheck complains about ignoring the error return value, but
|
||||
// that's on purpose; if you want an error from a goroutine you have to
|
||||
// get it over a channel or something
|
||||
go b.doWork(state, work)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Breaker) doWork(state uint32, work func() error) error {
|
||||
var panicValue interface{}
|
||||
|
||||
result := func() error {
|
||||
defer func() {
|
||||
panicValue = recover()
|
||||
}()
|
||||
return work()
|
||||
}()
|
||||
|
||||
if result == nil && panicValue == nil && state == closed {
|
||||
// short-circuit the normal, success path without contending
|
||||
// on the lock
|
||||
return nil
|
||||
}
|
||||
|
||||
// oh well, I guess we have to contend on the lock
|
||||
b.processResult(result, panicValue)
|
||||
|
||||
if panicValue != nil {
|
||||
// as close as Go lets us come to a "rethrow" although unfortunately
|
||||
// we lose the original panicing location
|
||||
panic(panicValue)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func (b *Breaker) processResult(result error, panicValue interface{}) {
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
if result == nil && panicValue == nil {
|
||||
if b.state == halfOpen {
|
||||
b.successes++
|
||||
if b.successes == b.successThreshold {
|
||||
b.closeBreaker()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if b.errors > 0 {
|
||||
expiry := b.lastError.Add(b.timeout)
|
||||
if time.Now().After(expiry) {
|
||||
b.errors = 0
|
||||
}
|
||||
}
|
||||
|
||||
switch b.state {
|
||||
case closed:
|
||||
b.errors++
|
||||
if b.errors == b.errorThreshold {
|
||||
b.openBreaker()
|
||||
} else {
|
||||
b.lastError = time.Now()
|
||||
}
|
||||
case halfOpen:
|
||||
b.openBreaker()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *Breaker) openBreaker() {
|
||||
b.changeState(open)
|
||||
go b.timer()
|
||||
}
|
||||
|
||||
func (b *Breaker) closeBreaker() {
|
||||
b.changeState(closed)
|
||||
}
|
||||
|
||||
func (b *Breaker) timer() {
|
||||
time.Sleep(b.timeout)
|
||||
|
||||
b.lock.Lock()
|
||||
defer b.lock.Unlock()
|
||||
|
||||
b.changeState(halfOpen)
|
||||
}
|
||||
|
||||
func (b *Breaker) changeState(newState uint32) {
|
||||
b.errors = 0
|
||||
b.successes = 0
|
||||
atomic.StoreUint32(&b.state, newState)
|
||||
}
|
196
vendor/github.com/eapache/go-resiliency/breaker/breaker_test.go
generated
vendored
Normal file
196
vendor/github.com/eapache/go-resiliency/breaker/breaker_test.go
generated
vendored
Normal file
|
@ -0,0 +1,196 @@
|
|||
package breaker
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var errSomeError = errors.New("errSomeError")
|
||||
|
||||
func alwaysPanics() error {
|
||||
panic("foo")
|
||||
}
|
||||
|
||||
func returnsError() error {
|
||||
return errSomeError
|
||||
}
|
||||
|
||||
func returnsSuccess() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestBreakerErrorExpiry(t *testing.T) {
|
||||
breaker := New(2, 1, 1*time.Second)
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
if err := breaker.Run(returnsError); err != errSomeError {
|
||||
t.Error(err)
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
|
||||
for i := 0; i < 3; i++ {
|
||||
if err := breaker.Go(returnsError); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
time.Sleep(1 * time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBreakerPanicsCountAsErrors(t *testing.T) {
|
||||
breaker := New(3, 2, 1*time.Second)
|
||||
|
||||
// three errors opens the breaker
|
||||
for i := 0; i < 3; i++ {
|
||||
func() {
|
||||
defer func() {
|
||||
val := recover()
|
||||
if val.(string) != "foo" {
|
||||
t.Error("incorrect panic")
|
||||
}
|
||||
}()
|
||||
if err := breaker.Run(alwaysPanics); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Error("shouldn't get here")
|
||||
}()
|
||||
}
|
||||
|
||||
// breaker is open
|
||||
for i := 0; i < 5; i++ {
|
||||
if err := breaker.Run(returnsError); err != ErrBreakerOpen {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestBreakerStateTransitions(t *testing.T) {
|
||||
breaker := New(3, 2, 1*time.Second)
|
||||
|
||||
// three errors opens the breaker
|
||||
for i := 0; i < 3; i++ {
|
||||
if err := breaker.Run(returnsError); err != errSomeError {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// breaker is open
|
||||
for i := 0; i < 5; i++ {
|
||||
if err := breaker.Run(returnsError); err != ErrBreakerOpen {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// wait for it to half-close
|
||||
time.Sleep(2 * time.Second)
|
||||
// one success works, but is not enough to fully close
|
||||
if err := breaker.Run(returnsSuccess); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
// error works, but re-opens immediately
|
||||
if err := breaker.Run(returnsError); err != errSomeError {
|
||||
t.Error(err)
|
||||
}
|
||||
// breaker is open
|
||||
if err := breaker.Run(returnsError); err != ErrBreakerOpen {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// wait for it to half-close
|
||||
time.Sleep(2 * time.Second)
|
||||
// two successes is enough to close it for good
|
||||
for i := 0; i < 2; i++ {
|
||||
if err := breaker.Run(returnsSuccess); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
// error works
|
||||
if err := breaker.Run(returnsError); err != errSomeError {
|
||||
t.Error(err)
|
||||
}
|
||||
// breaker is still closed
|
||||
if err := breaker.Run(returnsSuccess); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBreakerAsyncStateTransitions(t *testing.T) {
|
||||
breaker := New(3, 2, 1*time.Second)
|
||||
|
||||
// three errors opens the breaker
|
||||
for i := 0; i < 3; i++ {
|
||||
if err := breaker.Go(returnsError); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// just enough to yield the scheduler and let the goroutines work off
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
|
||||
// breaker is open
|
||||
for i := 0; i < 5; i++ {
|
||||
if err := breaker.Go(returnsError); err != ErrBreakerOpen {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// wait for it to half-close
|
||||
time.Sleep(2 * time.Second)
|
||||
// one success works, but is not enough to fully close
|
||||
if err := breaker.Go(returnsSuccess); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
// error works, but re-opens immediately
|
||||
if err := breaker.Go(returnsError); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
// just enough to yield the scheduler and let the goroutines work off
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
// breaker is open
|
||||
if err := breaker.Go(returnsError); err != ErrBreakerOpen {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
// wait for it to half-close
|
||||
time.Sleep(2 * time.Second)
|
||||
// two successes is enough to close it for good
|
||||
for i := 0; i < 2; i++ {
|
||||
if err := breaker.Go(returnsSuccess); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
// just enough to yield the scheduler and let the goroutines work off
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
// error works
|
||||
if err := breaker.Go(returnsError); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
// just enough to yield the scheduler and let the goroutines work off
|
||||
time.Sleep(1 * time.Millisecond)
|
||||
// breaker is still closed
|
||||
if err := breaker.Go(returnsSuccess); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleBreaker() {
|
||||
breaker := New(3, 1, 5*time.Second)
|
||||
|
||||
for {
|
||||
result := breaker.Run(func() error {
|
||||
// communicate with some external service and
|
||||
// return an error if the communication failed
|
||||
return nil
|
||||
})
|
||||
|
||||
switch result {
|
||||
case nil:
|
||||
// success!
|
||||
case ErrBreakerOpen:
|
||||
// our function wasn't run because the breaker was open
|
||||
default:
|
||||
// some other error
|
||||
}
|
||||
}
|
||||
}
|
26
vendor/github.com/eapache/go-resiliency/deadline/README.md
generated
vendored
Normal file
26
vendor/github.com/eapache/go-resiliency/deadline/README.md
generated
vendored
Normal file
|
@ -0,0 +1,26 @@
|
|||
deadline
|
||||
========
|
||||
|
||||
[](https://travis-ci.org/eapache/go-resiliency)
|
||||
[](https://godoc.org/github.com/eapache/go-resiliency/deadline)
|
||||
|
||||
The deadline/timeout resiliency pattern for golang.
|
||||
|
||||
Creating a deadline takes one parameter: how long to wait.
|
||||
|
||||
```go
|
||||
dl := deadline.New(1 * time.Second)
|
||||
|
||||
err := dl.Run(func(stopper <-chan struct{}) error {
|
||||
// do something possibly slow
|
||||
// check stopper function and give up if timed out
|
||||
return nil
|
||||
})
|
||||
|
||||
switch err {
|
||||
case deadline.ErrTimedOut:
|
||||
// execution took too long, oops
|
||||
default:
|
||||
// some other error
|
||||
}
|
||||
```
|
45
vendor/github.com/eapache/go-resiliency/deadline/deadline.go
generated
vendored
Normal file
45
vendor/github.com/eapache/go-resiliency/deadline/deadline.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
// Package deadline implements the deadline (also known as "timeout") resiliency pattern for Go.
|
||||
package deadline
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ErrTimedOut is the error returned from Run when the deadline expires.
|
||||
var ErrTimedOut = errors.New("timed out waiting for function to finish")
|
||||
|
||||
// Deadline implements the deadline/timeout resiliency pattern.
|
||||
type Deadline struct {
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// New constructs a new Deadline with the given timeout.
|
||||
func New(timeout time.Duration) *Deadline {
|
||||
return &Deadline{
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// Run runs the given function, passing it a stopper channel. If the deadline passes before
|
||||
// the function finishes executing, Run returns ErrTimeOut to the caller and closes the stopper
|
||||
// channel so that the work function can attempt to exit gracefully. It does not (and cannot)
|
||||
// simply kill the running function, so if it doesn't respect the stopper channel then it may
|
||||
// keep running after the deadline passes. If the function finishes before the deadline, then
|
||||
// the return value of the function is returned from Run.
|
||||
func (d *Deadline) Run(work func(<-chan struct{}) error) error {
|
||||
result := make(chan error)
|
||||
stopper := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
result <- work(stopper)
|
||||
}()
|
||||
|
||||
select {
|
||||
case ret := <-result:
|
||||
return ret
|
||||
case <-time.After(d.timeout):
|
||||
close(stopper)
|
||||
return ErrTimedOut
|
||||
}
|
||||
}
|
65
vendor/github.com/eapache/go-resiliency/deadline/deadline_test.go
generated
vendored
Normal file
65
vendor/github.com/eapache/go-resiliency/deadline/deadline_test.go
generated
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
package deadline
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func takesFiveMillis(stopper <-chan struct{}) error {
|
||||
time.Sleep(5 * time.Millisecond)
|
||||
return nil
|
||||
}
|
||||
|
||||
func takesTwentyMillis(stopper <-chan struct{}) error {
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
return nil
|
||||
}
|
||||
|
||||
func returnsError(stopper <-chan struct{}) error {
|
||||
return errors.New("foo")
|
||||
}
|
||||
|
||||
func TestDeadline(t *testing.T) {
|
||||
dl := New(10 * time.Millisecond)
|
||||
|
||||
if err := dl.Run(takesFiveMillis); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err := dl.Run(takesTwentyMillis); err != ErrTimedOut {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if err := dl.Run(returnsError); err.Error() != "foo" {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
done := make(chan struct{})
|
||||
err := dl.Run(func(stopper <-chan struct{}) error {
|
||||
<-stopper
|
||||
close(done)
|
||||
return nil
|
||||
})
|
||||
if err != ErrTimedOut {
|
||||
t.Error(err)
|
||||
}
|
||||
<-done
|
||||
}
|
||||
|
||||
func ExampleDeadline() {
|
||||
dl := New(1 * time.Second)
|
||||
|
||||
err := dl.Run(func(stopper <-chan struct{}) error {
|
||||
// do something possibly slow
|
||||
// check stopper function and give up if timed out
|
||||
return nil
|
||||
})
|
||||
|
||||
switch err {
|
||||
case ErrTimedOut:
|
||||
// execution took too long, oops
|
||||
default:
|
||||
// some other error
|
||||
}
|
||||
}
|
25
vendor/github.com/eapache/go-resiliency/retrier/README.md
generated
vendored
Normal file
25
vendor/github.com/eapache/go-resiliency/retrier/README.md
generated
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
retrier
|
||||
=======
|
||||
|
||||
[](https://travis-ci.org/eapache/go-resiliency)
|
||||
[](https://godoc.org/github.com/eapache/go-resiliency/retrier)
|
||||
|
||||
The retriable resiliency pattern for golang.
|
||||
|
||||
Creating a retrier takes two parameters:
|
||||
- the times to back-off between retries (and implicitly the number of times to
|
||||
retry)
|
||||
- the classifier that determines which errors to retry
|
||||
|
||||
```go
|
||||
r := retrier.New(retrier.ConstantBackoff(3, 100*time.Millisecond), nil)
|
||||
|
||||
err := r.Run(func() error {
|
||||
// do some work
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
// handle the case where the work failed three times
|
||||
}
|
||||
```
|
24
vendor/github.com/eapache/go-resiliency/retrier/backoffs.go
generated
vendored
Normal file
24
vendor/github.com/eapache/go-resiliency/retrier/backoffs.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
package retrier
|
||||
|
||||
import "time"
|
||||
|
||||
// ConstantBackoff generates a simple back-off strategy of retrying 'n' times, and waiting 'amount' time after each one.
|
||||
func ConstantBackoff(n int, amount time.Duration) []time.Duration {
|
||||
ret := make([]time.Duration, n)
|
||||
for i := range ret {
|
||||
ret[i] = amount
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// ExponentialBackoff generates a simple back-off strategy of retrying 'n' times, and doubling the amount of
|
||||
// time waited after each one.
|
||||
func ExponentialBackoff(n int, initialAmount time.Duration) []time.Duration {
|
||||
ret := make([]time.Duration, n)
|
||||
next := initialAmount
|
||||
for i := range ret {
|
||||
ret[i] = next
|
||||
next *= 2
|
||||
}
|
||||
return ret
|
||||
}
|
55
vendor/github.com/eapache/go-resiliency/retrier/backoffs_test.go
generated
vendored
Normal file
55
vendor/github.com/eapache/go-resiliency/retrier/backoffs_test.go
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
package retrier
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestConstantBackoff(t *testing.T) {
|
||||
b := ConstantBackoff(1, 10*time.Millisecond)
|
||||
if len(b) != 1 {
|
||||
t.Error("incorrect length")
|
||||
}
|
||||
for i := range b {
|
||||
if b[i] != 10*time.Millisecond {
|
||||
t.Error("incorrect value at", i)
|
||||
}
|
||||
}
|
||||
|
||||
b = ConstantBackoff(10, 250*time.Hour)
|
||||
if len(b) != 10 {
|
||||
t.Error("incorrect length")
|
||||
}
|
||||
for i := range b {
|
||||
if b[i] != 250*time.Hour {
|
||||
t.Error("incorrect value at", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExponentialBackoff(t *testing.T) {
|
||||
b := ExponentialBackoff(1, 10*time.Millisecond)
|
||||
if len(b) != 1 {
|
||||
t.Error("incorrect length")
|
||||
}
|
||||
if b[0] != 10*time.Millisecond {
|
||||
t.Error("incorrect value")
|
||||
}
|
||||
|
||||
b = ExponentialBackoff(4, 1*time.Minute)
|
||||
if len(b) != 4 {
|
||||
t.Error("incorrect length")
|
||||
}
|
||||
if b[0] != 1*time.Minute {
|
||||
t.Error("incorrect value")
|
||||
}
|
||||
if b[1] != 2*time.Minute {
|
||||
t.Error("incorrect value")
|
||||
}
|
||||
if b[2] != 4*time.Minute {
|
||||
t.Error("incorrect value")
|
||||
}
|
||||
if b[3] != 8*time.Minute {
|
||||
t.Error("incorrect value")
|
||||
}
|
||||
}
|
66
vendor/github.com/eapache/go-resiliency/retrier/classifier.go
generated
vendored
Normal file
66
vendor/github.com/eapache/go-resiliency/retrier/classifier.go
generated
vendored
Normal file
|
@ -0,0 +1,66 @@
|
|||
package retrier
|
||||
|
||||
// Action is the type returned by a Classifier to indicate how the Retrier should proceed.
|
||||
type Action int
|
||||
|
||||
const (
|
||||
Succeed Action = iota // Succeed indicates the Retrier should treat this value as a success.
|
||||
Fail // Fail indicates the Retrier should treat this value as a hard failure and not retry.
|
||||
Retry // Retry indicates the Retrier should treat this value as a soft failure and retry.
|
||||
)
|
||||
|
||||
// Classifier is the interface implemented by anything that can classify Errors for a Retrier.
|
||||
type Classifier interface {
|
||||
Classify(error) Action
|
||||
}
|
||||
|
||||
// DefaultClassifier classifies errors in the simplest way possible. If
|
||||
// the error is nil, it returns Succeed, otherwise it returns Retry.
|
||||
type DefaultClassifier struct{}
|
||||
|
||||
// Classify implements the Classifier interface.
|
||||
func (c DefaultClassifier) Classify(err error) Action {
|
||||
if err == nil {
|
||||
return Succeed
|
||||
}
|
||||
|
||||
return Retry
|
||||
}
|
||||
|
||||
// WhitelistClassifier classifies errors based on a whitelist. If the error is nil, it
|
||||
// returns Succeed; if the error is in the whitelist, it returns Retry; otherwise, it returns Fail.
|
||||
type WhitelistClassifier []error
|
||||
|
||||
// Classify implements the Classifier interface.
|
||||
func (list WhitelistClassifier) Classify(err error) Action {
|
||||
if err == nil {
|
||||
return Succeed
|
||||
}
|
||||
|
||||
for _, pass := range list {
|
||||
if err == pass {
|
||||
return Retry
|
||||
}
|
||||
}
|
||||
|
||||
return Fail
|
||||
}
|
||||
|
||||
// BlacklistClassifier classifies errors based on a blacklist. If the error is nil, it
|
||||
// returns Succeed; if the error is in the blacklist, it returns Fail; otherwise, it returns Retry.
|
||||
type BlacklistClassifier []error
|
||||
|
||||
// Classify implements the Classifier interface.
|
||||
func (list BlacklistClassifier) Classify(err error) Action {
|
||||
if err == nil {
|
||||
return Succeed
|
||||
}
|
||||
|
||||
for _, pass := range list {
|
||||
if err == pass {
|
||||
return Fail
|
||||
}
|
||||
}
|
||||
|
||||
return Retry
|
||||
}
|
66
vendor/github.com/eapache/go-resiliency/retrier/classifier_test.go
generated
vendored
Normal file
66
vendor/github.com/eapache/go-resiliency/retrier/classifier_test.go
generated
vendored
Normal file
|
@ -0,0 +1,66 @@
|
|||
package retrier
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
errFoo = errors.New("FOO")
|
||||
errBar = errors.New("BAR")
|
||||
errBaz = errors.New("BAZ")
|
||||
)
|
||||
|
||||
func TestDefaultClassifier(t *testing.T) {
|
||||
c := DefaultClassifier{}
|
||||
|
||||
if c.Classify(nil) != Succeed {
|
||||
t.Error("default misclassified nil")
|
||||
}
|
||||
|
||||
if c.Classify(errFoo) != Retry {
|
||||
t.Error("default misclassified foo")
|
||||
}
|
||||
if c.Classify(errBar) != Retry {
|
||||
t.Error("default misclassified bar")
|
||||
}
|
||||
if c.Classify(errBaz) != Retry {
|
||||
t.Error("default misclassified baz")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWhitelistClassifier(t *testing.T) {
|
||||
c := WhitelistClassifier{errFoo, errBar}
|
||||
|
||||
if c.Classify(nil) != Succeed {
|
||||
t.Error("whitelist misclassified nil")
|
||||
}
|
||||
|
||||
if c.Classify(errFoo) != Retry {
|
||||
t.Error("whitelist misclassified foo")
|
||||
}
|
||||
if c.Classify(errBar) != Retry {
|
||||
t.Error("whitelist misclassified bar")
|
||||
}
|
||||
if c.Classify(errBaz) != Fail {
|
||||
t.Error("whitelist misclassified baz")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlacklistClassifier(t *testing.T) {
|
||||
c := BlacklistClassifier{errBar}
|
||||
|
||||
if c.Classify(nil) != Succeed {
|
||||
t.Error("blacklist misclassified nil")
|
||||
}
|
||||
|
||||
if c.Classify(errFoo) != Retry {
|
||||
t.Error("blacklist misclassified foo")
|
||||
}
|
||||
if c.Classify(errBar) != Fail {
|
||||
t.Error("blacklist misclassified bar")
|
||||
}
|
||||
if c.Classify(errBaz) != Retry {
|
||||
t.Error("blacklist misclassified baz")
|
||||
}
|
||||
}
|
69
vendor/github.com/eapache/go-resiliency/retrier/retrier.go
generated
vendored
Normal file
69
vendor/github.com/eapache/go-resiliency/retrier/retrier.go
generated
vendored
Normal file
|
@ -0,0 +1,69 @@
|
|||
// Package retrier implements the "retriable" resiliency pattern for Go.
|
||||
package retrier
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Retrier implements the "retriable" resiliency pattern, abstracting out the process of retrying a failed action
|
||||
// a certain number of times with an optional back-off between each retry.
|
||||
type Retrier struct {
|
||||
backoff []time.Duration
|
||||
class Classifier
|
||||
jitter float64
|
||||
rand *rand.Rand
|
||||
}
|
||||
|
||||
// New constructs a Retrier with the given backoff pattern and classifier. The length of the backoff pattern
|
||||
// indicates how many times an action will be retried, and the value at each index indicates the amount of time
|
||||
// waited before each subsequent retry. The classifier is used to determine which errors should be retried and
|
||||
// which should cause the retrier to fail fast. The DefaultClassifier is used if nil is passed.
|
||||
func New(backoff []time.Duration, class Classifier) *Retrier {
|
||||
if class == nil {
|
||||
class = DefaultClassifier{}
|
||||
}
|
||||
|
||||
return &Retrier{
|
||||
backoff: backoff,
|
||||
class: class,
|
||||
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
|
||||
}
|
||||
}
|
||||
|
||||
// Run executes the given work function, then classifies its return value based on the classifier used
|
||||
// to construct the Retrier. If the result is Succeed or Fail, the return value of the work function is
|
||||
// returned to the caller. If the result is Retry, then Run sleeps according to the its backoff policy
|
||||
// before retrying. If the total number of retries is exceeded then the return value of the work function
|
||||
// is returned to the caller regardless.
|
||||
func (r *Retrier) Run(work func() error) error {
|
||||
retries := 0
|
||||
for {
|
||||
ret := work()
|
||||
|
||||
switch r.class.Classify(ret) {
|
||||
case Succeed, Fail:
|
||||
return ret
|
||||
case Retry:
|
||||
if retries >= len(r.backoff) {
|
||||
return ret
|
||||
}
|
||||
time.Sleep(r.calcSleep(retries))
|
||||
retries++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Retrier) calcSleep(i int) time.Duration {
|
||||
// take a random float in the range (-r.jitter, +r.jitter) and multiply it by the base amount
|
||||
return r.backoff[i] + time.Duration(((r.rand.Float64()*2)-1)*r.jitter*float64(r.backoff[i]))
|
||||
}
|
||||
|
||||
// SetJitter sets the amount of jitter on each back-off to a factor between 0.0 and 1.0 (values outside this range
|
||||
// are silently ignored). When a retry occurs, the back-off is adjusted by a random amount up to this value.
|
||||
func (r *Retrier) SetJitter(jit float64) {
|
||||
if jit < 0 || jit > 1 {
|
||||
return
|
||||
}
|
||||
r.jitter = jit
|
||||
}
|
129
vendor/github.com/eapache/go-resiliency/retrier/retrier_test.go
generated
vendored
Normal file
129
vendor/github.com/eapache/go-resiliency/retrier/retrier_test.go
generated
vendored
Normal file
|
@ -0,0 +1,129 @@
|
|||
package retrier
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var i int
|
||||
|
||||
func genWork(returns []error) func() error {
|
||||
i = 0
|
||||
return func() error {
|
||||
i++
|
||||
if i > len(returns) {
|
||||
return nil
|
||||
}
|
||||
return returns[i-1]
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetrier(t *testing.T) {
|
||||
r := New([]time.Duration{0, 10 * time.Millisecond}, WhitelistClassifier{errFoo})
|
||||
|
||||
err := r.Run(genWork([]error{errFoo, errFoo}))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if i != 3 {
|
||||
t.Error("run wrong number of times")
|
||||
}
|
||||
|
||||
err = r.Run(genWork([]error{errFoo, errBar}))
|
||||
if err != errBar {
|
||||
t.Error(err)
|
||||
}
|
||||
if i != 2 {
|
||||
t.Error("run wrong number of times")
|
||||
}
|
||||
|
||||
err = r.Run(genWork([]error{errBar, errBaz}))
|
||||
if err != errBar {
|
||||
t.Error(err)
|
||||
}
|
||||
if i != 1 {
|
||||
t.Error("run wrong number of times")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetrierNone(t *testing.T) {
|
||||
r := New(nil, nil)
|
||||
|
||||
i = 0
|
||||
err := r.Run(func() error {
|
||||
i++
|
||||
return errFoo
|
||||
})
|
||||
if err != errFoo {
|
||||
t.Error(err)
|
||||
}
|
||||
if i != 1 {
|
||||
t.Error("run wrong number of times")
|
||||
}
|
||||
|
||||
i = 0
|
||||
err = r.Run(func() error {
|
||||
i++
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if i != 1 {
|
||||
t.Error("run wrong number of times")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetrierJitter(t *testing.T) {
|
||||
r := New([]time.Duration{0, 10 * time.Millisecond, 4 * time.Hour}, nil)
|
||||
|
||||
if r.calcSleep(0) != 0 {
|
||||
t.Error("Incorrect sleep calculated")
|
||||
}
|
||||
if r.calcSleep(1) != 10*time.Millisecond {
|
||||
t.Error("Incorrect sleep calculated")
|
||||
}
|
||||
if r.calcSleep(2) != 4*time.Hour {
|
||||
t.Error("Incorrect sleep calculated")
|
||||
}
|
||||
|
||||
r.SetJitter(0.25)
|
||||
for i := 0; i < 20; i++ {
|
||||
if r.calcSleep(0) != 0 {
|
||||
t.Error("Incorrect sleep calculated")
|
||||
}
|
||||
|
||||
slp := r.calcSleep(1)
|
||||
if slp < 7500*time.Microsecond || slp > 12500*time.Microsecond {
|
||||
t.Error("Incorrect sleep calculated")
|
||||
}
|
||||
|
||||
slp = r.calcSleep(2)
|
||||
if slp < 3*time.Hour || slp > 5*time.Hour {
|
||||
t.Error("Incorrect sleep calculated")
|
||||
}
|
||||
}
|
||||
|
||||
r.SetJitter(-1)
|
||||
if r.jitter != 0.25 {
|
||||
t.Error("Invalid jitter value accepted")
|
||||
}
|
||||
|
||||
r.SetJitter(2)
|
||||
if r.jitter != 0.25 {
|
||||
t.Error("Invalid jitter value accepted")
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleRetrier() {
|
||||
r := New(ConstantBackoff(3, 100*time.Millisecond), nil)
|
||||
|
||||
err := r.Run(func() error {
|
||||
// do some work
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
// handle the case where the work failed three times
|
||||
}
|
||||
}
|
21
vendor/github.com/eapache/go-resiliency/semaphore/README.md
generated
vendored
Normal file
21
vendor/github.com/eapache/go-resiliency/semaphore/README.md
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
semaphore
|
||||
=========
|
||||
|
||||
[](https://travis-ci.org/eapache/go-resiliency)
|
||||
[](https://godoc.org/github.com/eapache/go-resiliency/semaphore)
|
||||
|
||||
The semaphore resiliency pattern for golang.
|
||||
|
||||
Creating a semaphore takes two parameters:
|
||||
- ticket count (how many tickets to give out at once)
|
||||
- timeout (how long to wait for a ticket if none are currently available)
|
||||
|
||||
```go
|
||||
sem := semaphore.New(3, 1*time.Second)
|
||||
|
||||
if err := sem.Acquire(); err != nil {
|
||||
// could not acquire semaphore
|
||||
return err
|
||||
}
|
||||
defer sem.Release()
|
||||
```
|
45
vendor/github.com/eapache/go-resiliency/semaphore/semaphore.go
generated
vendored
Normal file
45
vendor/github.com/eapache/go-resiliency/semaphore/semaphore.go
generated
vendored
Normal file
|
@ -0,0 +1,45 @@
|
|||
// Package semaphore implements the semaphore resiliency pattern for Go.
|
||||
package semaphore
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ErrNoTickets is the error returned by Acquire when it could not acquire
|
||||
// a ticket from the semaphore within the configured timeout.
|
||||
var ErrNoTickets = errors.New("could not aquire semaphore ticket")
|
||||
|
||||
// Semaphore implements the semaphore resiliency pattern
|
||||
type Semaphore struct {
|
||||
sem chan struct{}
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
// New constructs a new Semaphore with the given ticket-count
|
||||
// and timeout.
|
||||
func New(tickets int, timeout time.Duration) *Semaphore {
|
||||
return &Semaphore{
|
||||
sem: make(chan struct{}, tickets),
|
||||
timeout: timeout,
|
||||
}
|
||||
}
|
||||
|
||||
// Acquire tries to acquire a ticket from the semaphore. If it can, it returns nil.
|
||||
// If it cannot after "timeout" amount of time, it returns ErrNoTickets. It is
|
||||
// safe to call Acquire concurrently on a single Semaphore.
|
||||
func (s *Semaphore) Acquire() error {
|
||||
select {
|
||||
case s.sem <- struct{}{}:
|
||||
return nil
|
||||
case <-time.After(s.timeout):
|
||||
return ErrNoTickets
|
||||
}
|
||||
}
|
||||
|
||||
// Release releases an acquired ticket back to the semaphore. It is safe to call
|
||||
// Release concurrently on a single Semaphore. It is an error to call Release on
|
||||
// a Semaphore from which you have not first acquired a ticket.
|
||||
func (s *Semaphore) Release() {
|
||||
<-s.sem
|
||||
}
|
61
vendor/github.com/eapache/go-resiliency/semaphore/semaphore_test.go
generated
vendored
Normal file
61
vendor/github.com/eapache/go-resiliency/semaphore/semaphore_test.go
generated
vendored
Normal file
|
@ -0,0 +1,61 @@
|
|||
package semaphore
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestSemaphoreAcquireRelease(t *testing.T) {
|
||||
sem := New(3, 1*time.Second)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
if err := sem.Acquire(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := sem.Acquire(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if err := sem.Acquire(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
sem.Release()
|
||||
sem.Release()
|
||||
sem.Release()
|
||||
}
|
||||
}
|
||||
|
||||
func TestSemaphoreBlockTimeout(t *testing.T) {
|
||||
sem := New(1, 200*time.Millisecond)
|
||||
|
||||
if err := sem.Acquire(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
start := time.Now()
|
||||
if err := sem.Acquire(); err != ErrNoTickets {
|
||||
t.Error(err)
|
||||
}
|
||||
if start.Add(200 * time.Millisecond).After(time.Now()) {
|
||||
t.Error("semaphore did not wait long enough")
|
||||
}
|
||||
|
||||
sem.Release()
|
||||
if err := sem.Acquire(); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func ExampleSemaphore() {
|
||||
sem := New(3, 1*time.Second)
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
go func() {
|
||||
if err := sem.Acquire(); err != nil {
|
||||
return //could not acquire semaphore
|
||||
}
|
||||
defer sem.Release()
|
||||
|
||||
// do something semaphore-guarded
|
||||
}()
|
||||
}
|
||||
}
|
24
vendor/github.com/eapache/go-xerial-snappy/.gitignore
generated
vendored
Normal file
24
vendor/github.com/eapache/go-xerial-snappy/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
7
vendor/github.com/eapache/go-xerial-snappy/.travis.yml
generated
vendored
Normal file
7
vendor/github.com/eapache/go-xerial-snappy/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.5.4
|
||||
- 1.6.1
|
||||
|
||||
sudo: false
|
21
vendor/github.com/eapache/go-xerial-snappy/LICENSE
generated
vendored
Normal file
21
vendor/github.com/eapache/go-xerial-snappy/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Evan Huus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
13
vendor/github.com/eapache/go-xerial-snappy/README.md
generated
vendored
Normal file
13
vendor/github.com/eapache/go-xerial-snappy/README.md
generated
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
# go-xerial-snappy
|
||||
|
||||
[](https://travis-ci.org/eapache/go-xerial-snappy)
|
||||
|
||||
Xerial-compatible Snappy framing support for golang.
|
||||
|
||||
Packages using Xerial for snappy encoding use a framing format incompatible with
|
||||
basically everything else in existence. This package wraps Go's built-in snappy
|
||||
package to support it.
|
||||
|
||||
Apps that use this format include Apache Kafka (see
|
||||
https://github.com/dpkp/kafka-python/issues/126#issuecomment-35478921 for
|
||||
details).
|
43
vendor/github.com/eapache/go-xerial-snappy/snappy.go
generated
vendored
Normal file
43
vendor/github.com/eapache/go-xerial-snappy/snappy.go
generated
vendored
Normal file
|
@ -0,0 +1,43 @@
|
|||
package snappy
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
|
||||
master "github.com/golang/snappy"
|
||||
)
|
||||
|
||||
var xerialHeader = []byte{130, 83, 78, 65, 80, 80, 89, 0}
|
||||
|
||||
// Encode encodes data as snappy with no framing header.
|
||||
func Encode(src []byte) []byte {
|
||||
return master.Encode(nil, src)
|
||||
}
|
||||
|
||||
// Decode decodes snappy data whether it is traditional unframed
|
||||
// or includes the xerial framing format.
|
||||
func Decode(src []byte) ([]byte, error) {
|
||||
if !bytes.Equal(src[:8], xerialHeader) {
|
||||
return master.Decode(nil, src)
|
||||
}
|
||||
|
||||
var (
|
||||
pos = uint32(16)
|
||||
max = uint32(len(src))
|
||||
dst = make([]byte, 0, len(src))
|
||||
chunk []byte
|
||||
err error
|
||||
)
|
||||
for pos < max {
|
||||
size := binary.BigEndian.Uint32(src[pos : pos+4])
|
||||
pos += 4
|
||||
|
||||
chunk, err = master.Decode(chunk, src[pos:pos+size])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pos += size
|
||||
dst = append(dst, chunk...)
|
||||
}
|
||||
return dst, nil
|
||||
}
|
49
vendor/github.com/eapache/go-xerial-snappy/snappy_test.go
generated
vendored
Normal file
49
vendor/github.com/eapache/go-xerial-snappy/snappy_test.go
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
23
vendor/github.com/eapache/queue/.gitignore
generated
vendored
Normal file
23
vendor/github.com/eapache/queue/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
7
vendor/github.com/eapache/queue/.travis.yml
generated
vendored
Normal file
7
vendor/github.com/eapache/queue/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
language: go
|
||||
sudo: false
|
||||
|
||||
go:
|
||||
- 1.2
|
||||
- 1.3
|
||||
- 1.4
|
21
vendor/github.com/eapache/queue/LICENSE
generated
vendored
Normal file
21
vendor/github.com/eapache/queue/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Evan Huus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
16
vendor/github.com/eapache/queue/README.md
generated
vendored
Normal file
16
vendor/github.com/eapache/queue/README.md
generated
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
Queue
|
||||
=====
|
||||
|
||||
[](https://travis-ci.org/eapache/queue)
|
||||
[](https://godoc.org/github.com/eapache/queue)
|
||||
[](https://eapache.github.io/conduct.html)
|
||||
|
||||
A fast Golang queue using a ring-buffer, based on the version suggested by Dariusz Górecki.
|
||||
Using this instead of other, simpler, queue implementations (slice+append or linked list) provides
|
||||
substantial memory and time benefits, and fewer GC pauses.
|
||||
|
||||
The queue implemented here is as fast as it is in part because it is *not* thread-safe.
|
||||
|
||||
Follows semantic versioning using https://gopkg.in/ - import from
|
||||
[`gopkg.in/eapache/queue.v1`](https://gopkg.in/eapache/queue.v1)
|
||||
for guaranteed API stability.
|
88
vendor/github.com/eapache/queue/queue.go
generated
vendored
Normal file
88
vendor/github.com/eapache/queue/queue.go
generated
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
Package queue provides a fast, ring-buffer queue based on the version suggested by Dariusz Górecki.
|
||||
Using this instead of other, simpler, queue implementations (slice+append or linked list) provides
|
||||
substantial memory and time benefits, and fewer GC pauses.
|
||||
|
||||
The queue implemented here is as fast as it is for an additional reason: it is *not* thread-safe.
|
||||
*/
|
||||
package queue
|
||||
|
||||
const minQueueLen = 16
|
||||
|
||||
// Queue represents a single instance of the queue data structure.
|
||||
type Queue struct {
|
||||
buf []interface{}
|
||||
head, tail, count int
|
||||
}
|
||||
|
||||
// New constructs and returns a new Queue.
|
||||
func New() *Queue {
|
||||
return &Queue{
|
||||
buf: make([]interface{}, minQueueLen),
|
||||
}
|
||||
}
|
||||
|
||||
// Length returns the number of elements currently stored in the queue.
|
||||
func (q *Queue) Length() int {
|
||||
return q.count
|
||||
}
|
||||
|
||||
// resizes the queue to fit exactly twice its current contents
|
||||
// this can result in shrinking if the queue is less than half-full
|
||||
func (q *Queue) resize() {
|
||||
newBuf := make([]interface{}, q.count*2)
|
||||
|
||||
if q.tail > q.head {
|
||||
copy(newBuf, q.buf[q.head:q.tail])
|
||||
} else {
|
||||
n := copy(newBuf, q.buf[q.head:])
|
||||
copy(newBuf[n:], q.buf[:q.tail])
|
||||
}
|
||||
|
||||
q.head = 0
|
||||
q.tail = q.count
|
||||
q.buf = newBuf
|
||||
}
|
||||
|
||||
// Add puts an element on the end of the queue.
|
||||
func (q *Queue) Add(elem interface{}) {
|
||||
if q.count == len(q.buf) {
|
||||
q.resize()
|
||||
}
|
||||
|
||||
q.buf[q.tail] = elem
|
||||
q.tail = (q.tail + 1) % len(q.buf)
|
||||
q.count++
|
||||
}
|
||||
|
||||
// Peek returns the element at the head of the queue. This call panics
|
||||
// if the queue is empty.
|
||||
func (q *Queue) Peek() interface{} {
|
||||
if q.count <= 0 {
|
||||
panic("queue: Peek() called on empty queue")
|
||||
}
|
||||
return q.buf[q.head]
|
||||
}
|
||||
|
||||
// Get returns the element at index i in the queue. If the index is
|
||||
// invalid, the call will panic.
|
||||
func (q *Queue) Get(i int) interface{} {
|
||||
if i < 0 || i >= q.count {
|
||||
panic("queue: Get() called with index out of range")
|
||||
}
|
||||
return q.buf[(q.head+i)%len(q.buf)]
|
||||
}
|
||||
|
||||
// Remove removes the element from the front of the queue. If you actually
|
||||
// want the element, call Peek first. This call panics if the queue is empty.
|
||||
func (q *Queue) Remove() {
|
||||
if q.count <= 0 {
|
||||
panic("queue: Remove() called on empty queue")
|
||||
}
|
||||
q.buf[q.head] = nil
|
||||
q.head = (q.head + 1) % len(q.buf)
|
||||
q.count--
|
||||
if len(q.buf) > minQueueLen && q.count*4 == len(q.buf) {
|
||||
q.resize()
|
||||
}
|
||||
}
|
162
vendor/github.com/eapache/queue/queue_test.go
generated
vendored
Normal file
162
vendor/github.com/eapache/queue/queue_test.go
generated
vendored
Normal file
|
@ -0,0 +1,162 @@
|
|||
package queue
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestQueueSimple(t *testing.T) {
|
||||
q := New()
|
||||
|
||||
for i := 0; i < minQueueLen; i++ {
|
||||
q.Add(i)
|
||||
}
|
||||
for i := 0; i < minQueueLen; i++ {
|
||||
if q.Peek().(int) != i {
|
||||
t.Error("peek", i, "had value", q.Peek())
|
||||
}
|
||||
q.Remove()
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueueWrapping(t *testing.T) {
|
||||
q := New()
|
||||
|
||||
for i := 0; i < minQueueLen; i++ {
|
||||
q.Add(i)
|
||||
}
|
||||
for i := 0; i < 3; i++ {
|
||||
q.Remove()
|
||||
q.Add(minQueueLen + i)
|
||||
}
|
||||
|
||||
for i := 0; i < minQueueLen; i++ {
|
||||
if q.Peek().(int) != i+3 {
|
||||
t.Error("peek", i, "had value", q.Peek())
|
||||
}
|
||||
q.Remove()
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueueLength(t *testing.T) {
|
||||
q := New()
|
||||
|
||||
if q.Length() != 0 {
|
||||
t.Error("empty queue length not 0")
|
||||
}
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
q.Add(i)
|
||||
if q.Length() != i+1 {
|
||||
t.Error("adding: queue with", i, "elements has length", q.Length())
|
||||
}
|
||||
}
|
||||
for i := 0; i < 1000; i++ {
|
||||
q.Remove()
|
||||
if q.Length() != 1000-i-1 {
|
||||
t.Error("removing: queue with", 1000-i-i, "elements has length", q.Length())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueueGet(t *testing.T) {
|
||||
q := New()
|
||||
|
||||
for i := 0; i < 1000; i++ {
|
||||
q.Add(i)
|
||||
for j := 0; j < q.Length(); j++ {
|
||||
if q.Get(j).(int) != j {
|
||||
t.Errorf("index %d doesn't contain %d", j, j)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueueGetOutOfRangePanics(t *testing.T) {
|
||||
q := New()
|
||||
|
||||
q.Add(1)
|
||||
q.Add(2)
|
||||
q.Add(3)
|
||||
|
||||
assertPanics(t, "should panic when negative index", func() {
|
||||
q.Get(-1)
|
||||
})
|
||||
|
||||
assertPanics(t, "should panic when index greater than length", func() {
|
||||
q.Get(4)
|
||||
})
|
||||
}
|
||||
|
||||
func TestQueuePeekOutOfRangePanics(t *testing.T) {
|
||||
q := New()
|
||||
|
||||
assertPanics(t, "should panic when peeking empty queue", func() {
|
||||
q.Peek()
|
||||
})
|
||||
|
||||
q.Add(1)
|
||||
q.Remove()
|
||||
|
||||
assertPanics(t, "should panic when peeking emptied queue", func() {
|
||||
q.Peek()
|
||||
})
|
||||
}
|
||||
|
||||
func TestQueueRemoveOutOfRangePanics(t *testing.T) {
|
||||
q := New()
|
||||
|
||||
assertPanics(t, "should panic when removing empty queue", func() {
|
||||
q.Remove()
|
||||
})
|
||||
|
||||
q.Add(1)
|
||||
q.Remove()
|
||||
|
||||
assertPanics(t, "should panic when removing emptied queue", func() {
|
||||
q.Remove()
|
||||
})
|
||||
}
|
||||
|
||||
func assertPanics(t *testing.T, name string, f func()) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Errorf("%s: didn't panic as expected", name)
|
||||
}
|
||||
}()
|
||||
|
||||
f()
|
||||
}
|
||||
|
||||
// General warning: Go's benchmark utility (go test -bench .) increases the number of
|
||||
// iterations until the benchmarks take a reasonable amount of time to run; memory usage
|
||||
// is *NOT* considered. On my machine, these benchmarks hit around ~1GB before they've had
|
||||
// enough, but if you have less than that available and start swapping, then all bets are off.
|
||||
|
||||
func BenchmarkQueueSerial(b *testing.B) {
|
||||
q := New()
|
||||
for i := 0; i < b.N; i++ {
|
||||
q.Add(nil)
|
||||
}
|
||||
for i := 0; i < b.N; i++ {
|
||||
q.Peek()
|
||||
q.Remove()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkQueueGet(b *testing.B) {
|
||||
q := New()
|
||||
for i := 0; i < b.N; i++ {
|
||||
q.Add(i)
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
q.Get(i)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkQueueTickTock(b *testing.B) {
|
||||
q := New()
|
||||
for i := 0; i < b.N; i++ {
|
||||
q.Add(nil)
|
||||
q.Peek()
|
||||
q.Remove()
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue