|
@@ -25,6 +25,8 @@ import (
|
|
|
"sync"
|
|
"sync"
|
|
|
"time"
|
|
"time"
|
|
|
|
|
|
|
|
|
|
+ "golang.org/x/time/rate"
|
|
|
|
|
+
|
|
|
"github.com/coreos/etcd/etcdserver/stats"
|
|
"github.com/coreos/etcd/etcdserver/stats"
|
|
|
"github.com/coreos/etcd/pkg/httputil"
|
|
"github.com/coreos/etcd/pkg/httputil"
|
|
|
"github.com/coreos/etcd/pkg/transport"
|
|
"github.com/coreos/etcd/pkg/transport"
|
|
@@ -243,7 +245,9 @@ func (cw *streamWriter) closeUnlocked() bool {
|
|
|
if !cw.working {
|
|
if !cw.working {
|
|
|
return false
|
|
return false
|
|
|
}
|
|
}
|
|
|
- cw.closer.Close()
|
|
|
|
|
|
|
+ if err := cw.closer.Close(); err != nil {
|
|
|
|
|
+ plog.Errorf("peer %s (writer) connection close error: %v", cw.peerID, err)
|
|
|
|
|
+ }
|
|
|
if len(cw.msgc) > 0 {
|
|
if len(cw.msgc) > 0 {
|
|
|
cw.r.ReportUnreachable(uint64(cw.peerID))
|
|
cw.r.ReportUnreachable(uint64(cw.peerID))
|
|
|
}
|
|
}
|
|
@@ -278,25 +282,28 @@ type streamReader struct {
|
|
|
recvc chan<- raftpb.Message
|
|
recvc chan<- raftpb.Message
|
|
|
propc chan<- raftpb.Message
|
|
propc chan<- raftpb.Message
|
|
|
|
|
|
|
|
|
|
+ rl *rate.Limiter // alters the frequency of dial retrial attempts
|
|
|
|
|
+
|
|
|
errorc chan<- error
|
|
errorc chan<- error
|
|
|
|
|
|
|
|
mu sync.Mutex
|
|
mu sync.Mutex
|
|
|
paused bool
|
|
paused bool
|
|
|
- cancel func()
|
|
|
|
|
closer io.Closer
|
|
closer io.Closer
|
|
|
|
|
|
|
|
- stopc chan struct{}
|
|
|
|
|
- done chan struct{}
|
|
|
|
|
|
|
+ ctx context.Context
|
|
|
|
|
+ cancel context.CancelFunc
|
|
|
|
|
+ done chan struct{}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-func (r *streamReader) start() {
|
|
|
|
|
- r.stopc = make(chan struct{})
|
|
|
|
|
- r.done = make(chan struct{})
|
|
|
|
|
- if r.errorc == nil {
|
|
|
|
|
- r.errorc = r.tr.ErrorC
|
|
|
|
|
|
|
+func (cr *streamReader) start() {
|
|
|
|
|
+ cr.done = make(chan struct{})
|
|
|
|
|
+ if cr.errorc == nil {
|
|
|
|
|
+ cr.errorc = cr.tr.ErrorC
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
- go r.run()
|
|
|
|
|
|
|
+ if cr.ctx == nil {
|
|
|
|
|
+ cr.ctx, cr.cancel = context.WithCancel(context.Background())
|
|
|
|
|
+ }
|
|
|
|
|
+ go cr.run()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (cr *streamReader) run() {
|
|
func (cr *streamReader) run() {
|
|
@@ -311,7 +318,7 @@ func (cr *streamReader) run() {
|
|
|
} else {
|
|
} else {
|
|
|
cr.status.activate()
|
|
cr.status.activate()
|
|
|
plog.Infof("established a TCP streaming connection with peer %s (%s reader)", cr.peerID, cr.typ)
|
|
plog.Infof("established a TCP streaming connection with peer %s (%s reader)", cr.peerID, cr.typ)
|
|
|
- err := cr.decodeLoop(rc, t)
|
|
|
|
|
|
|
+ err = cr.decodeLoop(rc, t)
|
|
|
plog.Warningf("lost the TCP streaming connection with peer %s (%s reader)", cr.peerID, cr.typ)
|
|
plog.Warningf("lost the TCP streaming connection with peer %s (%s reader)", cr.peerID, cr.typ)
|
|
|
switch {
|
|
switch {
|
|
|
// all data is read out
|
|
// all data is read out
|
|
@@ -322,15 +329,16 @@ func (cr *streamReader) run() {
|
|
|
cr.status.deactivate(failureType{source: t.String(), action: "read"}, err.Error())
|
|
cr.status.deactivate(failureType{source: t.String(), action: "read"}, err.Error())
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- select {
|
|
|
|
|
- // Wait 100ms to create a new stream, so it doesn't bring too much
|
|
|
|
|
- // overhead when retry.
|
|
|
|
|
- case <-time.After(100 * time.Millisecond):
|
|
|
|
|
- case <-cr.stopc:
|
|
|
|
|
|
|
+ // Wait for a while before new dial attempt
|
|
|
|
|
+ err = cr.rl.Wait(cr.ctx)
|
|
|
|
|
+ if cr.ctx.Err() != nil {
|
|
|
plog.Infof("stopped streaming with peer %s (%s reader)", cr.peerID, t)
|
|
plog.Infof("stopped streaming with peer %s (%s reader)", cr.peerID, t)
|
|
|
close(cr.done)
|
|
close(cr.done)
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ plog.Errorf("streaming with peer %s (%s reader) rate limiter error: %v", cr.peerID, t, err)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -346,7 +354,7 @@ func (cr *streamReader) decodeLoop(rc io.ReadCloser, t streamType) error {
|
|
|
plog.Panicf("unhandled stream type %s", t)
|
|
plog.Panicf("unhandled stream type %s", t)
|
|
|
}
|
|
}
|
|
|
select {
|
|
select {
|
|
|
- case <-cr.stopc:
|
|
|
|
|
|
|
+ case <-cr.ctx.Done():
|
|
|
cr.mu.Unlock()
|
|
cr.mu.Unlock()
|
|
|
if err := rc.Close(); err != nil {
|
|
if err := rc.Close(); err != nil {
|
|
|
return err
|
|
return err
|
|
@@ -401,11 +409,8 @@ func (cr *streamReader) decodeLoop(rc io.ReadCloser, t streamType) error {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func (cr *streamReader) stop() {
|
|
func (cr *streamReader) stop() {
|
|
|
- close(cr.stopc)
|
|
|
|
|
cr.mu.Lock()
|
|
cr.mu.Lock()
|
|
|
- if cr.cancel != nil {
|
|
|
|
|
- cr.cancel()
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ cr.cancel()
|
|
|
cr.close()
|
|
cr.close()
|
|
|
cr.mu.Unlock()
|
|
cr.mu.Unlock()
|
|
|
<-cr.done
|
|
<-cr.done
|
|
@@ -429,13 +434,11 @@ func (cr *streamReader) dial(t streamType) (io.ReadCloser, error) {
|
|
|
|
|
|
|
|
setPeerURLsHeader(req, cr.tr.URLs)
|
|
setPeerURLsHeader(req, cr.tr.URLs)
|
|
|
|
|
|
|
|
- ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
|
- req = req.WithContext(ctx)
|
|
|
|
|
|
|
+ req = req.WithContext(cr.ctx)
|
|
|
|
|
|
|
|
cr.mu.Lock()
|
|
cr.mu.Lock()
|
|
|
- cr.cancel = cancel
|
|
|
|
|
select {
|
|
select {
|
|
|
- case <-cr.stopc:
|
|
|
|
|
|
|
+ case <-cr.ctx.Done():
|
|
|
cr.mu.Unlock()
|
|
cr.mu.Unlock()
|
|
|
return nil, fmt.Errorf("stream reader is stopped")
|
|
return nil, fmt.Errorf("stream reader is stopped")
|
|
|
default:
|
|
default:
|
|
@@ -497,7 +500,9 @@ func (cr *streamReader) dial(t streamType) (io.ReadCloser, error) {
|
|
|
|
|
|
|
|
func (cr *streamReader) close() {
|
|
func (cr *streamReader) close() {
|
|
|
if cr.closer != nil {
|
|
if cr.closer != nil {
|
|
|
- cr.closer.Close()
|
|
|
|
|
|
|
+ if err := cr.closer.Close(); err != nil {
|
|
|
|
|
+ plog.Errorf("peer %s (reader) connection close error: %v", cr.peerID, err)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
cr.closer = nil
|
|
cr.closer = nil
|
|
|
}
|
|
}
|