// Step advances the state machine using msgs. The ctx.Err() will be returned, // if any. func (n *Node) Step(ctx context.Context, m pb.Message) error { ch := n.recvc if m.Type == msgProp { ch = n.propc } select { case ch <- m: return nil case <-ctx.Done(): return ctx.Err() case <-n.done: return ErrStopped } }
// waitForEvent waits for a given Watcher to return its associated // event. It returns a non-nil error if the given Context times out // or the given ResponseWriter triggers a CloseNotify. func waitForEvent(ctx context.Context, w http.ResponseWriter, wa store.Watcher) (*store.Event, error) { // TODO(bmizerany): support streaming? defer wa.Remove() var nch <-chan bool if x, ok := w.(http.CloseNotifier); ok { nch = x.CloseNotify() } select { case ev := <-wa.EventChan(): return ev, nil case <-nch: elog.TODO() return nil, errClosed case <-ctx.Done(): return nil, ctx.Err() } }
// configure sends configuration change through consensus then performs it. // It will block until the change is performed or there is an error. func (s *EtcdServer) configure(ctx context.Context, cc raftpb.ConfChange) error { ch := s.w.Register(cc.ID) if err := s.node.ProposeConfChange(ctx, cc); err != nil { log.Printf("configure error: %v", err) s.w.Trigger(cc.ID, nil) return err } select { case <-ch: return nil case <-ctx.Done(): s.w.Trigger(cc.ID, nil) // GC wait return ctx.Err() case <-s.done: return ErrStopped } }
func handleWatch(ctx context.Context, w http.ResponseWriter, wa store.Watcher, stream bool, rt etcdserver.RaftTimer) { defer wa.Remove() ech := wa.EventChan() var nch <-chan bool if x, ok := w.(http.CloseNotifier); ok { nch = x.CloseNotify() } w.Header().Set("Content-Type", "application/json") w.Header().Set("X-Etcd-Index", fmt.Sprint(wa.StartIndex())) w.Header().Set("X-Raft-Index", fmt.Sprint(rt.Index())) w.Header().Set("X-Raft-Term", fmt.Sprint(rt.Term())) w.WriteHeader(http.StatusOK) // Ensure headers are flushed early, in case of long polling w.(http.Flusher).Flush() for { select { case <-nch: // Client closed connection. Nothing to do. return case <-ctx.Done(): // Timed out. net/http will close the connection for us, so nothing to do. return case ev, ok := <-ech: if !ok { // If the channel is closed this may be an indication of // that notifications are much more than we are able to // send to the client in time. Then we simply end streaming. return } if err := json.NewEncoder(w).Encode(ev); err != nil { // Should never be reached log.Println("error writing event: %v", err) return } if !stream { return } w.(http.Flusher).Flush() } } }
// Do interprets r and performs an operation on s.Store according to r.Method // and other fields. If r.Method is "POST", "PUT", "DELETE", or a "GET with // Quorum == true, r will be sent through consensus before performing its // respective operation. Do will block until an action is performed or there is // an error. func (s *Server) Do(ctx context.Context, r pb.Request) (Response, error) { if r.Id == 0 { panic("r.Id cannot be 0") } if r.Method == "GET" && r.Quorum { r.Method = "QGET" } switch r.Method { case "POST", "PUT", "DELETE", "QGET": data, err := r.Marshal() if err != nil { return Response{}, err } ch := s.w.Register(r.Id) s.Node.Propose(ctx, data) select { case x := <-ch: resp := x.(Response) return resp, resp.err case <-ctx.Done(): s.w.Trigger(r.Id, nil) // GC wait return Response{}, ctx.Err() case <-s.done: return Response{}, ErrStopped } case "GET": switch { case r.Wait: wc, err := s.Store.Watch(r.Path, r.Recursive, false, r.Since) if err != nil { return Response{}, err } return Response{Watcher: wc}, nil default: ev, err := s.Store.Get(r.Path, r.Recursive, r.Sorted) if err != nil { return Response{}, err } return Response{Event: ev}, nil } default: return Response{}, ErrUnknownMethod } }
func (c *httpClient) do(ctx context.Context, act httpAction) (*http.Response, []byte, error) { req := act.httpRequest(c.endpoint) rtchan := make(chan roundTripResponse, 1) go func() { resp, err := c.transport.RoundTrip(req) rtchan <- roundTripResponse{resp: resp, err: err} close(rtchan) }() var resp *http.Response var err error select { case rtresp := <-rtchan: resp, err = rtresp.resp, rtresp.err case <-ctx.Done(): c.transport.CancelRequest(req) // wait for request to actually exit before continuing <-rtchan err = ctx.Err() } // always check for resp nil-ness to deal with possible // race conditions between channels above defer func() { if resp != nil { resp.Body.Close() } }() if err != nil { return nil, nil, err } body, err := ioutil.ReadAll(resp.Body) return resp, body, err }