|
|
@@ -87,8 +87,7 @@ func (wr *WatchResponse) IsProgressNotify() bool {
|
|
|
|
|
|
// watcher implements the Watcher interface
|
|
|
type watcher struct {
|
|
|
- c *Client
|
|
|
- conn *grpc.ClientConn
|
|
|
+ rc *remoteClient
|
|
|
remote pb.WatchClient
|
|
|
|
|
|
// ctx controls internal remote.Watch requests
|
|
|
@@ -142,13 +141,7 @@ type watcherStream struct {
|
|
|
|
|
|
func NewWatcher(c *Client) Watcher {
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
- conn := c.ActiveConnection()
|
|
|
-
|
|
|
w := &watcher{
|
|
|
- c: c,
|
|
|
- conn: conn,
|
|
|
- remote: pb.NewWatchClient(conn),
|
|
|
-
|
|
|
ctx: ctx,
|
|
|
cancel: cancel,
|
|
|
streams: make(map[int64]*watcherStream),
|
|
|
@@ -159,6 +152,10 @@ func NewWatcher(c *Client) Watcher {
|
|
|
donec: make(chan struct{}),
|
|
|
errc: make(chan error, 1),
|
|
|
}
|
|
|
+
|
|
|
+ f := func(conn *grpc.ClientConn) { w.remote = pb.NewWatchClient(conn) }
|
|
|
+ w.rc = newRemoteClient(c, f)
|
|
|
+
|
|
|
go w.run()
|
|
|
return w
|
|
|
}
|
|
|
@@ -204,10 +201,7 @@ func (w *watcher) Watch(ctx context.Context, key string, opts ...OpOption) Watch
|
|
|
}
|
|
|
|
|
|
func (w *watcher) Close() error {
|
|
|
- select {
|
|
|
- case w.stopc <- struct{}{}:
|
|
|
- case <-w.donec:
|
|
|
- }
|
|
|
+ close(w.stopc)
|
|
|
<-w.donec
|
|
|
return v3rpc.Error(<-w.errc)
|
|
|
}
|
|
|
@@ -277,15 +271,20 @@ func (w *watcher) closeStream(ws *watcherStream) {
|
|
|
|
|
|
// run is the root of the goroutines for managing a watcher client
|
|
|
func (w *watcher) run() {
|
|
|
+ var wc pb.Watch_WatchClient
|
|
|
+ var closeErr error
|
|
|
+
|
|
|
defer func() {
|
|
|
+ select {
|
|
|
+ case w.errc <- closeErr:
|
|
|
+ default:
|
|
|
+ }
|
|
|
close(w.donec)
|
|
|
w.cancel()
|
|
|
}()
|
|
|
|
|
|
// start a stream with the etcd grpc server
|
|
|
- wc, wcerr := w.newWatchClient()
|
|
|
- if wcerr != nil {
|
|
|
- w.errc <- wcerr
|
|
|
+ if wc, closeErr = w.newWatchClient(); closeErr != nil {
|
|
|
return
|
|
|
}
|
|
|
|
|
|
@@ -335,8 +334,7 @@ func (w *watcher) run() {
|
|
|
// watch client failed to recv; spawn another if possible
|
|
|
// TODO report watch client errors from errc?
|
|
|
case <-w.errc:
|
|
|
- if wc, wcerr = w.newWatchClient(); wcerr != nil {
|
|
|
- w.errc <- wcerr
|
|
|
+ if wc, closeErr = w.newWatchClient(); closeErr != nil {
|
|
|
return
|
|
|
}
|
|
|
curReqC = w.reqc
|
|
|
@@ -345,7 +343,6 @@ func (w *watcher) run() {
|
|
|
}
|
|
|
cancelSet = make(map[int64]struct{})
|
|
|
case <-w.stopc:
|
|
|
- w.errc <- nil
|
|
|
return
|
|
|
}
|
|
|
|
|
|
@@ -503,17 +500,20 @@ func (w *watcher) resume() (ws pb.Watch_WatchClient, err error) {
|
|
|
// openWatchClient retries opening a watchclient until retryConnection fails
|
|
|
func (w *watcher) openWatchClient() (ws pb.Watch_WatchClient, err error) {
|
|
|
for {
|
|
|
- if ws, err = w.remote.Watch(w.ctx); ws != nil {
|
|
|
+ select {
|
|
|
+ case <-w.stopc:
|
|
|
+ if err == nil {
|
|
|
+ err = context.Canceled
|
|
|
+ }
|
|
|
+ return nil, err
|
|
|
+ default:
|
|
|
+ }
|
|
|
+ if ws, err = w.remote.Watch(w.ctx); ws != nil && err == nil {
|
|
|
break
|
|
|
} else if isHaltErr(w.ctx, err) {
|
|
|
return nil, v3rpc.Error(err)
|
|
|
}
|
|
|
- newConn, nerr := w.c.retryConnection(w.conn, nil)
|
|
|
- if nerr != nil {
|
|
|
- return nil, nerr
|
|
|
- }
|
|
|
- w.conn = newConn
|
|
|
- w.remote = pb.NewWatchClient(w.conn)
|
|
|
+ err = w.rc.reconnectWait(w.ctx, nil)
|
|
|
}
|
|
|
return ws, nil
|
|
|
}
|