Moved to google.golang.org/genproto/googleapis/api/annotations
Fixes #52
This commit is contained in:
2
vendor/github.com/go-kit/kit/util/conn/doc.go
generated
vendored
Normal file
2
vendor/github.com/go-kit/kit/util/conn/doc.go
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package conn provides utilities related to connections.
|
||||
package conn
|
145
vendor/github.com/go-kit/kit/util/conn/manager.go
generated
vendored
Normal file
145
vendor/github.com/go-kit/kit/util/conn/manager.go
generated
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
package conn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
)
|
||||
|
||||
// Dialer imitates net.Dial. Dialer is assumed to yield connections that are
|
||||
// safe for use by multiple concurrent goroutines.
|
||||
type Dialer func(network, address string) (net.Conn, error)
|
||||
|
||||
// AfterFunc imitates time.After.
|
||||
type AfterFunc func(time.Duration) <-chan time.Time
|
||||
|
||||
// Manager manages a net.Conn.
|
||||
//
|
||||
// Clients provide a way to create the connection with a Dialer, network, and
|
||||
// address. Clients should Take the connection when they want to use it, and Put
|
||||
// back whatever error they receive from its use. When a non-nil error is Put,
|
||||
// the connection is invalidated, and a new connection is established.
|
||||
// Connection failures are retried after an exponential backoff.
|
||||
type Manager struct {
|
||||
dialer Dialer
|
||||
network string
|
||||
address string
|
||||
after AfterFunc
|
||||
logger log.Logger
|
||||
|
||||
takec chan net.Conn
|
||||
putc chan error
|
||||
}
|
||||
|
||||
// NewManager returns a connection manager using the passed Dialer, network, and
|
||||
// address. The AfterFunc is used to control exponential backoff and retries.
|
||||
// The logger is used to log errors; pass a log.NopLogger if you don't care to
|
||||
// receive them. For normal use, prefer NewDefaultManager.
|
||||
func NewManager(d Dialer, network, address string, after AfterFunc, logger log.Logger) *Manager {
|
||||
m := &Manager{
|
||||
dialer: d,
|
||||
network: network,
|
||||
address: address,
|
||||
after: after,
|
||||
logger: logger,
|
||||
|
||||
takec: make(chan net.Conn),
|
||||
putc: make(chan error),
|
||||
}
|
||||
go m.loop()
|
||||
return m
|
||||
}
|
||||
|
||||
// NewDefaultManager is a helper constructor, suitable for most normal use in
|
||||
// real (non-test) code. It uses the real net.Dial and time.After functions.
|
||||
func NewDefaultManager(network, address string, logger log.Logger) *Manager {
|
||||
return NewManager(net.Dial, network, address, time.After, logger)
|
||||
}
|
||||
|
||||
// Take yields the current connection. It may be nil.
|
||||
func (m *Manager) Take() net.Conn {
|
||||
return <-m.takec
|
||||
}
|
||||
|
||||
// Put accepts an error that came from a previously yielded connection. If the
|
||||
// error is non-nil, the manager will invalidate the current connection and try
|
||||
// to reconnect, with exponential backoff. Putting a nil error is a no-op.
|
||||
func (m *Manager) Put(err error) {
|
||||
m.putc <- err
|
||||
}
|
||||
|
||||
// Write writes the passed data to the connection in a single Take/Put cycle.
|
||||
func (m *Manager) Write(b []byte) (int, error) {
|
||||
conn := m.Take()
|
||||
if conn == nil {
|
||||
return 0, ErrConnectionUnavailable
|
||||
}
|
||||
n, err := conn.Write(b)
|
||||
defer m.Put(err)
|
||||
return n, err
|
||||
}
|
||||
|
||||
func (m *Manager) loop() {
|
||||
var (
|
||||
conn = dial(m.dialer, m.network, m.address, m.logger) // may block slightly
|
||||
connc = make(chan net.Conn, 1)
|
||||
reconnectc <-chan time.Time // initially nil
|
||||
backoff = time.Second
|
||||
)
|
||||
|
||||
// If the initial dial fails, we need to trigger a reconnect via the loop
|
||||
// body, below. If we did this in a goroutine, we would race on the conn
|
||||
// variable. So we use a buffered chan instead.
|
||||
connc <- conn
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-reconnectc:
|
||||
reconnectc = nil // one-shot
|
||||
go func() { connc <- dial(m.dialer, m.network, m.address, m.logger) }()
|
||||
|
||||
case conn = <-connc:
|
||||
if conn == nil {
|
||||
// didn't work
|
||||
backoff = exponential(backoff) // wait longer
|
||||
reconnectc = m.after(backoff) // try again
|
||||
} else {
|
||||
// worked!
|
||||
backoff = time.Second // reset wait time
|
||||
reconnectc = nil // no retry necessary
|
||||
}
|
||||
|
||||
case m.takec <- conn:
|
||||
|
||||
case err := <-m.putc:
|
||||
if err != nil && conn != nil {
|
||||
m.logger.Log("err", err)
|
||||
conn = nil // connection is bad
|
||||
reconnectc = m.after(time.Nanosecond) // trigger immediately
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dial(d Dialer, network, address string, logger log.Logger) net.Conn {
|
||||
conn, err := d(network, address)
|
||||
if err != nil {
|
||||
logger.Log("err", err)
|
||||
conn = nil // just to be sure
|
||||
}
|
||||
return conn
|
||||
}
|
||||
|
||||
func exponential(d time.Duration) time.Duration {
|
||||
d *= 2
|
||||
if d > time.Minute {
|
||||
d = time.Minute
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
// ErrConnectionUnavailable is returned by the Manager's Write method when the
|
||||
// manager cannot yield a good connection.
|
||||
var ErrConnectionUnavailable = errors.New("connection unavailable")
|
159
vendor/github.com/go-kit/kit/util/conn/manager_test.go
generated
vendored
Normal file
159
vendor/github.com/go-kit/kit/util/conn/manager_test.go
generated
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
package conn
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/go-kit/kit/log"
|
||||
)
|
||||
|
||||
func TestManager(t *testing.T) {
|
||||
var (
|
||||
tickc = make(chan time.Time)
|
||||
after = func(time.Duration) <-chan time.Time { return tickc }
|
||||
dialconn = &mockConn{}
|
||||
dialerr = error(nil)
|
||||
dialer = func(string, string) (net.Conn, error) { return dialconn, dialerr }
|
||||
mgr = NewManager(dialer, "netw", "addr", after, log.NewNopLogger())
|
||||
)
|
||||
|
||||
// First conn should be fine.
|
||||
conn := mgr.Take()
|
||||
if conn == nil {
|
||||
t.Fatal("nil conn")
|
||||
}
|
||||
|
||||
// Write and check it went through.
|
||||
if _, err := conn.Write([]byte{1, 2, 3}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want, have := uint64(3), atomic.LoadUint64(&dialconn.wr); want != have {
|
||||
t.Errorf("want %d, have %d", want, have)
|
||||
}
|
||||
|
||||
// Put an error to kill the conn.
|
||||
mgr.Put(errors.New("should kill the connection"))
|
||||
|
||||
// First takes should fail.
|
||||
for i := 0; i < 10; i++ {
|
||||
if conn = mgr.Take(); conn != nil {
|
||||
t.Fatalf("want nil conn, got real conn")
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger the reconnect.
|
||||
tickc <- time.Now()
|
||||
|
||||
// The dial should eventually succeed and yield a good conn.
|
||||
if !within(100*time.Millisecond, func() bool {
|
||||
conn = mgr.Take()
|
||||
return conn != nil
|
||||
}) {
|
||||
t.Fatal("conn remained nil")
|
||||
}
|
||||
|
||||
// Write and check it went through.
|
||||
if _, err := conn.Write([]byte{4, 5}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if want, have := uint64(5), atomic.LoadUint64(&dialconn.wr); want != have {
|
||||
t.Errorf("want %d, have %d", want, have)
|
||||
}
|
||||
|
||||
// Dial starts failing.
|
||||
dialconn, dialerr = nil, errors.New("oh noes")
|
||||
mgr.Put(errors.New("trigger that reconnect y'all"))
|
||||
if conn = mgr.Take(); conn != nil {
|
||||
t.Fatalf("want nil conn, got real conn")
|
||||
}
|
||||
|
||||
// As many reconnects as they want.
|
||||
go func() {
|
||||
done := time.After(100 * time.Millisecond)
|
||||
for {
|
||||
select {
|
||||
case tickc <- time.Now():
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
// The dial should never succeed.
|
||||
if within(100*time.Millisecond, func() bool {
|
||||
conn = mgr.Take()
|
||||
return conn != nil
|
||||
}) {
|
||||
t.Fatal("eventually got a good conn, despite failing dialer")
|
||||
}
|
||||
}
|
||||
|
||||
func TestIssue292(t *testing.T) {
|
||||
// The util/conn.Manager won't attempt to reconnect to the provided endpoint
|
||||
// if the endpoint is initially unavailable (e.g. dial tcp :8080:
|
||||
// getsockopt: connection refused). If the endpoint is up when
|
||||
// conn.NewManager is called and then goes down/up, it reconnects just fine.
|
||||
|
||||
var (
|
||||
tickc = make(chan time.Time)
|
||||
after = func(time.Duration) <-chan time.Time { return tickc }
|
||||
dialconn = net.Conn(nil)
|
||||
dialerr = errors.New("fail")
|
||||
dialer = func(string, string) (net.Conn, error) { return dialconn, dialerr }
|
||||
mgr = NewManager(dialer, "netw", "addr", after, log.NewNopLogger())
|
||||
)
|
||||
|
||||
if conn := mgr.Take(); conn != nil {
|
||||
t.Fatal("first Take should have yielded nil conn, but didn't")
|
||||
}
|
||||
|
||||
dialconn, dialerr = &mockConn{}, nil
|
||||
select {
|
||||
case tickc <- time.Now():
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("manager isn't listening for a tick, despite a failed dial")
|
||||
}
|
||||
|
||||
if !within(time.Second, func() bool {
|
||||
return mgr.Take() != nil
|
||||
}) {
|
||||
t.Fatal("second Take should have yielded good conn, but didn't")
|
||||
}
|
||||
}
|
||||
|
||||
type mockConn struct {
|
||||
rd, wr uint64
|
||||
}
|
||||
|
||||
func (c *mockConn) Read(b []byte) (n int, err error) {
|
||||
atomic.AddUint64(&c.rd, uint64(len(b)))
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func (c *mockConn) Write(b []byte) (n int, err error) {
|
||||
atomic.AddUint64(&c.wr, uint64(len(b)))
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
func (c *mockConn) Close() error { return nil }
|
||||
func (c *mockConn) LocalAddr() net.Addr { return nil }
|
||||
func (c *mockConn) RemoteAddr() net.Addr { return nil }
|
||||
func (c *mockConn) SetDeadline(t time.Time) error { return nil }
|
||||
func (c *mockConn) SetReadDeadline(t time.Time) error { return nil }
|
||||
func (c *mockConn) SetWriteDeadline(t time.Time) error { return nil }
|
||||
|
||||
func within(d time.Duration, f func() bool) bool {
|
||||
deadline := time.Now().Add(d)
|
||||
for {
|
||||
if time.Now().After(deadline) {
|
||||
return false
|
||||
}
|
||||
if f() {
|
||||
return true
|
||||
}
|
||||
time.Sleep(d / 10)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user