diff --git a/chord/client.go b/chord/client.go index ac1ce65..621f5db 100644 --- a/chord/client.go +++ b/chord/client.go @@ -9,8 +9,10 @@ import ( // Client represents the communications a Chord node performs. type Client interface { - // FindSuccessor asks s to find the first peer in the network preceding id. - FindSuccessor(ctx context.Context, s Peer, id ID) (Peer, error) + // Find asks s to find a value and the peer that owns it. + // If the ID is not associated with a key, the result must be the empty + // string with a nil error. + Find(ctx context.Context, s Peer, id ID) (Peer, string, error) // Notify tells s we believe n to be its predecessor. Notify(ctx context.Context, n *Node, s Peer) error // Neighbors requests a peer's beliefs about its own neighbors. @@ -22,13 +24,13 @@ type Client interface { // considering we can sort by increasing distance from the origin and then do // the query in linear time. -// Find finds the first node in the network preceding id. -func Find(ctx context.Context, cl Client, n *Node, id ID) (Peer, error) { +// Find gets a value and the peer that owns it. +func Find(ctx context.Context, cl Client, n *Node, id ID) (Peer, string, error) { if n.IsLocal(id) { - return n.self, nil + return n.self, "", nil } - p, err := cl.FindSuccessor(ctx, n.Closest(id), id) - return p, err + p, s, err := cl.Find(ctx, n.Closest(id), id) + return p, s, err } // Join creates a new node joining an existing Chord network by communicating @@ -41,7 +43,7 @@ func Join(ctx context.Context, cl Client, addr netip.AddrPort, np Peer) (*Node, return nil, errors.New("chord: invalid peer") } self := Address(addr) - p, err := cl.FindSuccessor(ctx, np, self.id) + p, _, err := cl.Find(ctx, np, self.id) if err != nil { return nil, fmt.Errorf("couldn't query own successor: %w", err) } diff --git a/chord/httpnode/client.go b/chord/httpnode/client.go index 2267857..4754e3a 100644 --- a/chord/httpnode/client.go +++ b/chord/httpnode/client.go @@ -5,7 +5,6 @@ import ( "errors" "fmt" "net/http" - "net/netip" "net/url" "path" @@ -19,11 +18,11 @@ type Client struct { APIBase string } -// FindSuccessor asks s to find the first peer in the network preceding id. -func (cl *Client) FindSuccessor(ctx context.Context, s chord.Peer, id chord.ID) (chord.Peer, error) { +// Find asks s to find a value and the peer that owns it. +func (cl *Client) Find(ctx context.Context, s chord.Peer, id chord.ID) (chord.Peer, string, error) { _, addr := s.Values() if !addr.IsValid() { - return chord.Peer{}, errors.New("FindSuccessor with invalid peer") + return chord.Peer{}, "", errors.New("Find with invalid peer") } url := url.URL{ Scheme: "http", @@ -33,17 +32,17 @@ func (cl *Client) FindSuccessor(ctx context.Context, s chord.Peer, id chord.ID) } req, err := http.NewRequestWithContext(ctx, "GET", url.String(), nil) if err != nil { - return chord.Peer{}, err + return chord.Peer{}, "", err } resp, err := cl.HTTP.Do(req) if err != nil { - return chord.Peer{}, err + return chord.Peer{}, "", err } - p, err := readResponse[netip.AddrPort](resp) + p, err := readResponse[peervalue](resp) if err != nil { - return chord.Peer{}, fmt.Errorf("%w (%s)", err, resp.Status) + return chord.Peer{}, "", fmt.Errorf("%w (%s)", err, resp.Status) } - return chord.Address(p), nil + return chord.Address(p.Peer), p.Value, nil } // Notify tells s we believe n to be its predecessor. diff --git a/chord/httpnode/httpnode.go b/chord/httpnode/httpnode.go index bdc5672..81022f1 100644 --- a/chord/httpnode/httpnode.go +++ b/chord/httpnode/httpnode.go @@ -45,3 +45,8 @@ type neighbors struct { Succ []netip.AddrPort `json:"succ"` Pred netip.AddrPort `json:"pred,omitzero"` } + +type peervalue struct { + Peer netip.AddrPort `json:"peer"` + Value string `json:"value,omitzero"` +} diff --git a/chord/httpnode/server.go b/chord/httpnode/server.go index 99e9451..891dae7 100644 --- a/chord/httpnode/server.go +++ b/chord/httpnode/server.go @@ -45,7 +45,7 @@ func New(l net.Listener, cl chord.Client) (*Node, error) { // Router creates a handler for the Chord HTTP endpoints. func (n *Node) Router() http.Handler { m := http.NewServeMux() - m.HandleFunc("GET /succ", n.successor) + m.HandleFunc("GET /key", n.key) m.HandleFunc("POST /pred", n.notify) m.HandleFunc("GET /neighbors", n.neighbors) return m @@ -65,20 +65,21 @@ func (n *Node) Check(ctx context.Context) error { return nil } -func (n *Node) successor(w http.ResponseWriter, r *http.Request) { +func (n *Node) key(w http.ResponseWriter, r *http.Request) { s := r.FormValue("s") id, err := chord.ParseID(s) if err != nil { writeError(w, http.StatusBadRequest, err.Error()) return } - p, err := chord.Find(r.Context(), n.client, n.self, id) + p, v, err := chord.Find(r.Context(), n.client, n.self, id) if err != nil { writeError(w, http.StatusInternalServerError, err.Error()) return } _, addr := p.Values() - writeOk(w, addr) + pv := peervalue{addr, v} + writeOk(w, pv) } func (n *Node) notify(w http.ResponseWriter, r *http.Request) {