Moved to google.golang.org/genproto/googleapis/api/annotations
Fixes #52
This commit is contained in:
15
vendor/github.com/go-kit/kit/sd/lb/balancer.go
generated
vendored
Normal file
15
vendor/github.com/go-kit/kit/sd/lb/balancer.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
package lb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
)
|
||||
|
||||
// Balancer yields endpoints according to some heuristic.
|
||||
type Balancer interface {
|
||||
Endpoint() (endpoint.Endpoint, error)
|
||||
}
|
||||
|
||||
// ErrNoEndpoints is returned when no qualifying endpoints are available.
|
||||
var ErrNoEndpoints = errors.New("no endpoints available")
|
4
vendor/github.com/go-kit/kit/sd/lb/doc.go
generated
vendored
Normal file
4
vendor/github.com/go-kit/kit/sd/lb/doc.go
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
// Package lb implements the client-side load balancer pattern. When combined
|
||||
// with a service discovery system of record, it enables a more decentralized
|
||||
// architecture, removing the need for separate load balancers like HAProxy.
|
||||
package lb
|
32
vendor/github.com/go-kit/kit/sd/lb/random.go
generated
vendored
Normal file
32
vendor/github.com/go-kit/kit/sd/lb/random.go
generated
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
package lb
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/go-kit/kit/sd"
|
||||
)
|
||||
|
||||
// NewRandom returns a load balancer that selects services randomly.
|
||||
func NewRandom(s sd.Subscriber, seed int64) Balancer {
|
||||
return &random{
|
||||
s: s,
|
||||
r: rand.New(rand.NewSource(seed)),
|
||||
}
|
||||
}
|
||||
|
||||
type random struct {
|
||||
s sd.Subscriber
|
||||
r *rand.Rand
|
||||
}
|
||||
|
||||
func (r *random) Endpoint() (endpoint.Endpoint, error) {
|
||||
endpoints, err := r.s.Endpoints()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(endpoints) <= 0 {
|
||||
return nil, ErrNoEndpoints
|
||||
}
|
||||
return endpoints[r.r.Intn(len(endpoints))], nil
|
||||
}
|
52
vendor/github.com/go-kit/kit/sd/lb/random_test.go
generated
vendored
Normal file
52
vendor/github.com/go-kit/kit/sd/lb/random_test.go
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
package lb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/go-kit/kit/sd"
|
||||
)
|
||||
|
||||
func TestRandom(t *testing.T) {
|
||||
var (
|
||||
n = 7
|
||||
endpoints = make([]endpoint.Endpoint, n)
|
||||
counts = make([]int, n)
|
||||
seed = int64(12345)
|
||||
iterations = 1000000
|
||||
want = iterations / n
|
||||
tolerance = want / 100 // 1%
|
||||
)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
i0 := i
|
||||
endpoints[i] = func(context.Context, interface{}) (interface{}, error) { counts[i0]++; return struct{}{}, nil }
|
||||
}
|
||||
|
||||
subscriber := sd.FixedSubscriber(endpoints)
|
||||
balancer := NewRandom(subscriber, seed)
|
||||
|
||||
for i := 0; i < iterations; i++ {
|
||||
endpoint, _ := balancer.Endpoint()
|
||||
endpoint(context.Background(), struct{}{})
|
||||
}
|
||||
|
||||
for i, have := range counts {
|
||||
delta := int(math.Abs(float64(want - have)))
|
||||
if delta > tolerance {
|
||||
t.Errorf("%d: want %d, have %d, delta %d > %d tolerance", i, want, have, delta, tolerance)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomNoEndpoints(t *testing.T) {
|
||||
subscriber := sd.FixedSubscriber{}
|
||||
balancer := NewRandom(subscriber, 1415926)
|
||||
_, err := balancer.Endpoint()
|
||||
if want, have := ErrNoEndpoints, err; want != have {
|
||||
t.Errorf("want %v, have %v", want, have)
|
||||
}
|
||||
|
||||
}
|
117
vendor/github.com/go-kit/kit/sd/lb/retry.go
generated
vendored
Normal file
117
vendor/github.com/go-kit/kit/sd/lb/retry.go
generated
vendored
Normal file
@@ -0,0 +1,117 @@
|
||||
package lb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
)
|
||||
|
||||
// RetryError is an error wrapper that is used by the retry mechanism. All
|
||||
// errors returned by the retry mechanism via its endpoint will be RetryErrors.
|
||||
type RetryError struct {
|
||||
RawErrors []error // all errors encountered from endpoints directly
|
||||
Final error // the final, terminating error
|
||||
}
|
||||
|
||||
func (e RetryError) Error() string {
|
||||
var suffix string
|
||||
if len(e.RawErrors) > 1 {
|
||||
a := make([]string, len(e.RawErrors)-1)
|
||||
for i := 0; i < len(e.RawErrors)-1; i++ { // last one is Final
|
||||
a[i] = e.RawErrors[i].Error()
|
||||
}
|
||||
suffix = fmt.Sprintf(" (previously: %s)", strings.Join(a, "; "))
|
||||
}
|
||||
return fmt.Sprintf("%v%s", e.Final, suffix)
|
||||
}
|
||||
|
||||
// Callback is a function that is given the current attempt count and the error
|
||||
// received from the underlying endpoint. It should return whether the Retry
|
||||
// function should continue trying to get a working endpoint, and a custom error
|
||||
// if desired. The error message may be nil, but a true/false is always
|
||||
// expected. In all cases, if the replacement error is supplied, the received
|
||||
// error will be replaced in the calling context.
|
||||
type Callback func(n int, received error) (keepTrying bool, replacement error)
|
||||
|
||||
// Retry wraps a service load balancer and returns an endpoint oriented load
|
||||
// balancer for the specified service method. Requests to the endpoint will be
|
||||
// automatically load balanced via the load balancer. Requests that return
|
||||
// errors will be retried until they succeed, up to max times, or until the
|
||||
// timeout is elapsed, whichever comes first.
|
||||
func Retry(max int, timeout time.Duration, b Balancer) endpoint.Endpoint {
|
||||
return RetryWithCallback(timeout, b, maxRetries(max))
|
||||
}
|
||||
|
||||
func maxRetries(max int) Callback {
|
||||
return func(n int, err error) (keepTrying bool, replacement error) {
|
||||
return n < max, nil
|
||||
}
|
||||
}
|
||||
|
||||
func alwaysRetry(int, error) (keepTrying bool, replacement error) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// RetryWithCallback wraps a service load balancer and returns an endpoint
|
||||
// oriented load balancer for the specified service method. Requests to the
|
||||
// endpoint will be automatically load balanced via the load balancer. Requests
|
||||
// that return errors will be retried until they succeed, up to max times, until
|
||||
// the callback returns false, or until the timeout is elapsed, whichever comes
|
||||
// first.
|
||||
func RetryWithCallback(timeout time.Duration, b Balancer, cb Callback) endpoint.Endpoint {
|
||||
if cb == nil {
|
||||
cb = alwaysRetry
|
||||
}
|
||||
if b == nil {
|
||||
panic("nil Balancer")
|
||||
}
|
||||
|
||||
return func(ctx context.Context, request interface{}) (response interface{}, err error) {
|
||||
var (
|
||||
newctx, cancel = context.WithTimeout(ctx, timeout)
|
||||
responses = make(chan interface{}, 1)
|
||||
errs = make(chan error, 1)
|
||||
final RetryError
|
||||
)
|
||||
defer cancel()
|
||||
|
||||
for i := 1; ; i++ {
|
||||
go func() {
|
||||
e, err := b.Endpoint()
|
||||
if err != nil {
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
response, err := e(newctx, request)
|
||||
if err != nil {
|
||||
errs <- err
|
||||
return
|
||||
}
|
||||
responses <- response
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-newctx.Done():
|
||||
return nil, newctx.Err()
|
||||
|
||||
case response := <-responses:
|
||||
return response, nil
|
||||
|
||||
case err := <-errs:
|
||||
final.RawErrors = append(final.RawErrors, err)
|
||||
keepTrying, replacement := cb(i, err)
|
||||
if replacement != nil {
|
||||
err = replacement
|
||||
}
|
||||
if !keepTrying {
|
||||
final.Final = err
|
||||
return nil, final
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
141
vendor/github.com/go-kit/kit/sd/lb/retry_test.go
generated
vendored
Normal file
141
vendor/github.com/go-kit/kit/sd/lb/retry_test.go
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
package lb_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/go-kit/kit/sd"
|
||||
"github.com/go-kit/kit/sd/lb"
|
||||
)
|
||||
|
||||
func TestRetryMaxTotalFail(t *testing.T) {
|
||||
var (
|
||||
endpoints = sd.FixedSubscriber{} // no endpoints
|
||||
rr = lb.NewRoundRobin(endpoints)
|
||||
retry = lb.Retry(999, time.Second, rr) // lots of retries
|
||||
ctx = context.Background()
|
||||
)
|
||||
if _, err := retry(ctx, struct{}{}); err == nil {
|
||||
t.Errorf("expected error, got none") // should fail
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetryMaxPartialFail(t *testing.T) {
|
||||
var (
|
||||
endpoints = []endpoint.Endpoint{
|
||||
func(context.Context, interface{}) (interface{}, error) { return nil, errors.New("error one") },
|
||||
func(context.Context, interface{}) (interface{}, error) { return nil, errors.New("error two") },
|
||||
func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil /* OK */ },
|
||||
}
|
||||
subscriber = sd.FixedSubscriber{
|
||||
0: endpoints[0],
|
||||
1: endpoints[1],
|
||||
2: endpoints[2],
|
||||
}
|
||||
retries = len(endpoints) - 1 // not quite enough retries
|
||||
rr = lb.NewRoundRobin(subscriber)
|
||||
ctx = context.Background()
|
||||
)
|
||||
if _, err := lb.Retry(retries, time.Second, rr)(ctx, struct{}{}); err == nil {
|
||||
t.Errorf("expected error two, got none")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetryMaxSuccess(t *testing.T) {
|
||||
var (
|
||||
endpoints = []endpoint.Endpoint{
|
||||
func(context.Context, interface{}) (interface{}, error) { return nil, errors.New("error one") },
|
||||
func(context.Context, interface{}) (interface{}, error) { return nil, errors.New("error two") },
|
||||
func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil /* OK */ },
|
||||
}
|
||||
subscriber = sd.FixedSubscriber{
|
||||
0: endpoints[0],
|
||||
1: endpoints[1],
|
||||
2: endpoints[2],
|
||||
}
|
||||
retries = len(endpoints) // exactly enough retries
|
||||
rr = lb.NewRoundRobin(subscriber)
|
||||
ctx = context.Background()
|
||||
)
|
||||
if _, err := lb.Retry(retries, time.Second, rr)(ctx, struct{}{}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRetryTimeout(t *testing.T) {
|
||||
var (
|
||||
step = make(chan struct{})
|
||||
e = func(context.Context, interface{}) (interface{}, error) { <-step; return struct{}{}, nil }
|
||||
timeout = time.Millisecond
|
||||
retry = lb.Retry(999, timeout, lb.NewRoundRobin(sd.FixedSubscriber{0: e}))
|
||||
errs = make(chan error, 1)
|
||||
invoke = func() { _, err := retry(context.Background(), struct{}{}); errs <- err }
|
||||
)
|
||||
|
||||
go func() { step <- struct{}{} }() // queue up a flush of the endpoint
|
||||
invoke() // invoke the endpoint and trigger the flush
|
||||
if err := <-errs; err != nil { // that should succeed
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
go func() { time.Sleep(10 * timeout); step <- struct{}{} }() // a delayed flush
|
||||
invoke() // invoke the endpoint
|
||||
if err := <-errs; err != context.DeadlineExceeded { // that should not succeed
|
||||
t.Errorf("wanted %v, got none", context.DeadlineExceeded)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAbortEarlyCustomMessage(t *testing.T) {
|
||||
var (
|
||||
myErr = errors.New("aborting early")
|
||||
cb = func(int, error) (bool, error) { return false, myErr }
|
||||
endpoints = sd.FixedSubscriber{} // no endpoints
|
||||
rr = lb.NewRoundRobin(endpoints)
|
||||
retry = lb.RetryWithCallback(time.Second, rr, cb) // lots of retries
|
||||
ctx = context.Background()
|
||||
)
|
||||
_, err := retry(ctx, struct{}{})
|
||||
if want, have := myErr, err.(lb.RetryError).Final; want != have {
|
||||
t.Errorf("want %v, have %v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestErrorPassedUnchangedToCallback(t *testing.T) {
|
||||
var (
|
||||
myErr = errors.New("my custom error")
|
||||
cb = func(_ int, err error) (bool, error) {
|
||||
if want, have := myErr, err; want != have {
|
||||
t.Errorf("want %v, have %v", want, have)
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
endpoint = func(ctx context.Context, request interface{}) (interface{}, error) {
|
||||
return nil, myErr
|
||||
}
|
||||
endpoints = sd.FixedSubscriber{endpoint} // no endpoints
|
||||
rr = lb.NewRoundRobin(endpoints)
|
||||
retry = lb.RetryWithCallback(time.Second, rr, cb) // lots of retries
|
||||
ctx = context.Background()
|
||||
)
|
||||
_, err := retry(ctx, struct{}{})
|
||||
if want, have := myErr, err.(lb.RetryError).Final; want != have {
|
||||
t.Errorf("want %v, have %v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleNilCallback(t *testing.T) {
|
||||
var (
|
||||
subscriber = sd.FixedSubscriber{
|
||||
func(context.Context, interface{}) (interface{}, error) { return struct{}{}, nil /* OK */ },
|
||||
}
|
||||
rr = lb.NewRoundRobin(subscriber)
|
||||
ctx = context.Background()
|
||||
)
|
||||
retry := lb.RetryWithCallback(time.Second, rr, nil)
|
||||
if _, err := retry(ctx, struct{}{}); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
34
vendor/github.com/go-kit/kit/sd/lb/round_robin.go
generated
vendored
Normal file
34
vendor/github.com/go-kit/kit/sd/lb/round_robin.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
package lb
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/go-kit/kit/sd"
|
||||
)
|
||||
|
||||
// NewRoundRobin returns a load balancer that returns services in sequence.
|
||||
func NewRoundRobin(s sd.Subscriber) Balancer {
|
||||
return &roundRobin{
|
||||
s: s,
|
||||
c: 0,
|
||||
}
|
||||
}
|
||||
|
||||
type roundRobin struct {
|
||||
s sd.Subscriber
|
||||
c uint64
|
||||
}
|
||||
|
||||
func (rr *roundRobin) Endpoint() (endpoint.Endpoint, error) {
|
||||
endpoints, err := rr.s.Endpoints()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(endpoints) <= 0 {
|
||||
return nil, ErrNoEndpoints
|
||||
}
|
||||
old := atomic.AddUint64(&rr.c, 1) - 1
|
||||
idx := old % uint64(len(endpoints))
|
||||
return endpoints[idx], nil
|
||||
}
|
95
vendor/github.com/go-kit/kit/sd/lb/round_robin_test.go
generated
vendored
Normal file
95
vendor/github.com/go-kit/kit/sd/lb/round_robin_test.go
generated
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
package lb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/endpoint"
|
||||
"github.com/go-kit/kit/sd"
|
||||
)
|
||||
|
||||
func TestRoundRobin(t *testing.T) {
|
||||
var (
|
||||
counts = []int{0, 0, 0}
|
||||
endpoints = []endpoint.Endpoint{
|
||||
func(context.Context, interface{}) (interface{}, error) { counts[0]++; return struct{}{}, nil },
|
||||
func(context.Context, interface{}) (interface{}, error) { counts[1]++; return struct{}{}, nil },
|
||||
func(context.Context, interface{}) (interface{}, error) { counts[2]++; return struct{}{}, nil },
|
||||
}
|
||||
)
|
||||
|
||||
subscriber := sd.FixedSubscriber(endpoints)
|
||||
balancer := NewRoundRobin(subscriber)
|
||||
|
||||
for i, want := range [][]int{
|
||||
{1, 0, 0},
|
||||
{1, 1, 0},
|
||||
{1, 1, 1},
|
||||
{2, 1, 1},
|
||||
{2, 2, 1},
|
||||
{2, 2, 2},
|
||||
{3, 2, 2},
|
||||
} {
|
||||
endpoint, err := balancer.Endpoint()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
endpoint(context.Background(), struct{}{})
|
||||
if have := counts; !reflect.DeepEqual(want, have) {
|
||||
t.Fatalf("%d: want %v, have %v", i, want, have)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundRobinNoEndpoints(t *testing.T) {
|
||||
subscriber := sd.FixedSubscriber{}
|
||||
balancer := NewRoundRobin(subscriber)
|
||||
_, err := balancer.Endpoint()
|
||||
if want, have := ErrNoEndpoints, err; want != have {
|
||||
t.Errorf("want %v, have %v", want, have)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundRobinNoRace(t *testing.T) {
|
||||
balancer := NewRoundRobin(sd.FixedSubscriber([]endpoint.Endpoint{
|
||||
endpoint.Nop,
|
||||
endpoint.Nop,
|
||||
endpoint.Nop,
|
||||
endpoint.Nop,
|
||||
endpoint.Nop,
|
||||
}))
|
||||
|
||||
var (
|
||||
n = 100
|
||||
done = make(chan struct{})
|
||||
wg sync.WaitGroup
|
||||
count uint64
|
||||
)
|
||||
|
||||
wg.Add(n)
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
default:
|
||||
_, _ = balancer.Endpoint()
|
||||
atomic.AddUint64(&count, 1)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
time.Sleep(time.Second)
|
||||
close(done)
|
||||
wg.Wait()
|
||||
|
||||
t.Logf("made %d calls", atomic.LoadUint64(&count))
|
||||
}
|
Reference in New Issue
Block a user