func (sws *serverWatchStream) recvLoop() error { for { req, err := sws.gRPCStream.Recv() if err == io.EOF { return nil } if err != nil { return err } switch uv := req.RequestUnion.(type) { case *pb.WatchRequest_CreateRequest: if uv.CreateRequest == nil { break } creq := uv.CreateRequest if len(creq.Key) == 0 { // \x00 is the smallest key creq.Key = []byte{0} } if len(creq.RangeEnd) == 1 && creq.RangeEnd[0] == 0 { // support >= key queries creq.RangeEnd = []byte{} } wsrev := sws.watchStream.Rev() rev := creq.StartRevision if rev == 0 { rev = wsrev + 1 } id := sws.watchStream.Watch(creq.Key, creq.RangeEnd, rev) if id != -1 && creq.ProgressNotify { sws.progress[id] = true } sws.ctrlStream <- &pb.WatchResponse{ Header: sws.newResponseHeader(wsrev), WatchId: int64(id), Created: true, Canceled: id == -1, } case *pb.WatchRequest_CancelRequest: if uv.CancelRequest != nil { id := uv.CancelRequest.WatchId err := sws.watchStream.Cancel(storage.WatchID(id)) if err == nil { sws.ctrlStream <- &pb.WatchResponse{ Header: sws.newResponseHeader(sws.watchStream.Rev()), WatchId: id, Canceled: true, } delete(sws.progress, storage.WatchID(id)) } } // TODO: do we need to return error back to client? default: panic("not implemented") } } }
func (sws *serverWatchStream) recvLoop() error { for { req, err := sws.gRPCStream.Recv() if err == io.EOF { return nil } if err != nil { return err } switch uv := req.RequestUnion.(type) { case *pb.WatchRequest_CreateRequest: if uv.CreateRequest != nil { creq := uv.CreateRequest var prefix bool toWatch := creq.Key if len(creq.Key) == 0 { toWatch = creq.Prefix prefix = true } rev := creq.StartRevision wsrev := sws.watchStream.Rev() if rev == 0 { // rev 0 watches past the current revision rev = wsrev + 1 } else if rev > wsrev { // do not allow watching future revision. sws.ctrlStream <- &pb.WatchResponse{ Header: sws.newResponseHeader(wsrev), WatchId: -1, Created: true, Canceled: true, } continue } id := sws.watchStream.Watch(toWatch, prefix, rev) sws.ctrlStream <- &pb.WatchResponse{ Header: sws.newResponseHeader(wsrev), WatchId: int64(id), Created: true, } } case *pb.WatchRequest_CancelRequest: if uv.CancelRequest != nil { id := uv.CancelRequest.WatchId err := sws.watchStream.Cancel(storage.WatchID(id)) if err == nil { sws.ctrlStream <- &pb.WatchResponse{ Header: sws.newResponseHeader(sws.watchStream.Rev()), WatchId: id, Canceled: true, } } } // TODO: do we need to return error back to client? default: panic("not implemented") } } }
func newWatcher(c *EtcdClient, key string, rev int64, isPrefix bool) (*Watcher, error) { w, err := c.Watch.Watch(context.Background()) if err != nil { return nil, err } req := &pb.WatchCreateRequest{StartRevision: rev} if isPrefix { req.Prefix = []byte(key) } else { req.Key = []byte(key) } if err := w.Send(&pb.WatchRequest{CreateRequest: req}); err != nil { return nil, err } wresp, err := w.Recv() if err != nil { return nil, err } if len(wresp.Events) != 0 || wresp.Created != true { return nil, ErrWaitMismatch } ret := &Watcher{ wstream: w, donec: make(chan struct{}), id: storage.WatchID(wresp.WatchId), recvc: make(chan *storagepb.Event), } go ret.recvLoop() return ret, nil }
func (sws *serverWatchStream) recvLoop() error { for { req, err := sws.gRPCStream.Recv() if err == io.EOF { return nil } if err != nil { return err } switch { case req.CreateRequest != nil: creq := req.CreateRequest var prefix bool toWatch := creq.Key if len(creq.Key) == 0 { toWatch = creq.Prefix prefix = true } id := sws.watchStream.Watch(toWatch, prefix, creq.StartRevision) sws.ctrlStream <- &pb.WatchResponse{ Header: sws.newResponseHeader(sws.watchStream.Rev()), WatchId: int64(id), Created: true, } case req.CancelRequest != nil: id := req.CancelRequest.WatchId err := sws.watchStream.Cancel(storage.WatchID(id)) if err == nil { sws.ctrlStream <- &pb.WatchResponse{ Header: sws.newResponseHeader(sws.watchStream.Rev()), WatchId: id, Canceled: true, } } // TODO: do we need to return error back to client? default: panic("not implemented") } } }
func (sws *serverWatchStream) sendLoop() { // watch ids that are currently active ids := make(map[storage.WatchID]struct{}) // watch responses pending on a watch id creation message pending := make(map[storage.WatchID][]*pb.WatchResponse) progressTicker := time.NewTicker(ProgressReportInterval) defer progressTicker.Stop() for { select { case wresp, ok := <-sws.watchStream.Chan(): if !ok { return } // TODO: evs is []storagepb.Event type // either return []*storagepb.Event from storage package // or define protocol buffer with []storagepb.Event. evs := wresp.Events events := make([]*storagepb.Event, len(evs)) for i := range evs { events[i] = &evs[i] } wr := &pb.WatchResponse{ Header: sws.newResponseHeader(wresp.Revision), WatchId: int64(wresp.WatchID), Events: events, CompactRevision: wresp.CompactRevision, } if _, hasId := ids[wresp.WatchID]; !hasId { // buffer if id not yet announced wrs := append(pending[wresp.WatchID], wr) pending[wresp.WatchID] = wrs continue } storage.ReportEventReceived() if err := sws.gRPCStream.Send(wr); err != nil { return } if _, ok := sws.progress[wresp.WatchID]; ok { sws.progress[wresp.WatchID] = false } case c, ok := <-sws.ctrlStream: if !ok { return } if err := sws.gRPCStream.Send(c); err != nil { return } // track id creation wid := storage.WatchID(c.WatchId) if c.Canceled { delete(ids, wid) continue } if c.Created { // flush buffered events ids[wid] = struct{}{} for _, v := range pending[wid] { storage.ReportEventReceived() if err := sws.gRPCStream.Send(v); err != nil { return } } delete(pending, wid) } case <-progressTicker.C: for id, ok := range sws.progress { if ok { sws.watchStream.RequestProgress(id) } sws.progress[id] = true } case <-sws.closec: // drain the chan to clean up pending events for range sws.watchStream.Chan() { storage.ReportEventReceived() } for _, wrs := range pending { for range wrs { storage.ReportEventReceived() } } } } }