func Dial(addr string) (*Client, os.Error) { c, err := net.Dial("tcp", "", addr) if err != nil { return nil, err } pr := proto.NewConn(c) go pr.ReadResponses() return &Client{pr: pr, lg: util.NewLogger(addr)}, nil }
// This is a little subtle. We want to follow redirects while still pipelining // requests, and we want to allow as many requests as possible to succeed // without retrying unnecessarily. // // In particular, reads never need to redirect, and writes must always go to // the leader. So we want that read requests never retry, and write requests // retry if and only if necessary. Here's how it works: // // In the proto.Conn, when we get a redirect response, we raise a flag noting // the new address. This flag only goes up, never down. This flag effectively // means the connection is deprecated. Any pending requests can go ahead, but // new requests should use the new address. // // In the Client, when we notice that a redirect has occurred (i.e. the flag is // set), we establish a new connection to the new address. Calls in the future // will use the new connection. But we also allow the old connection to // continue functioning as it was. Any writes on the old connection will retry, // and then they are guaranteed to pick up the new connection. Any reads on the // old connection will just succeed directly. func (cl *Client) proto() (*proto.Conn, os.Error) { cl.lk.Lock() defer cl.lk.Unlock() if cl.pr.RedirectAddr != "" { conn, err := net.Dial("tcp", "", cl.pr.RedirectAddr) if err != nil { return nil, err } cl.lg = util.NewLogger(cl.pr.RedirectAddr) cl.pr = proto.NewConn(conn) go cl.pr.ReadResponses() } return cl.pr, nil }
func (s *Server) Serve(l net.Listener, cal chan int) os.Error { for { rw, err := l.Accept() if err != nil { log.Printf("%#v", err) if e, ok := err.(*net.OpError); ok && e.Error == os.EINVAL { return nil } return err } c := &conn{proto.NewConn(rw), rw, s, closed(cal)} go c.serve() } panic("unreachable") }