implement node communications (Figure 6)
This commit is contained in:
parent
7dd2de5055
commit
45efc85a6e
100
chord/client.go
Normal file
100
chord/client.go
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
package chord
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/netip"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
// 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.
|
||||||
|
Neighbors(ctx context.Context, p Peer) (pred Peer, succ []Peer, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(branden): FindSuccessor should be plural; if we have multiple keys to
|
||||||
|
// search, we shouldn't have to do the whole query for all of them, especially
|
||||||
|
// considering we can sort by increasing distance from the origin and then do
|
||||||
|
// the query in linear time.
|
||||||
|
|
||||||
|
// Join creates a new node joining an existing Chord network by communicating
|
||||||
|
// with any peer already in the network.
|
||||||
|
func Join(ctx context.Context, cl Client, addr netip.AddrPort, np Peer) (*Node, error) {
|
||||||
|
if !addr.IsValid() {
|
||||||
|
return nil, errors.New("chord: cannot join with invalid address")
|
||||||
|
}
|
||||||
|
if !np.IsValid() {
|
||||||
|
return nil, errors.New("chord: invalid peer")
|
||||||
|
}
|
||||||
|
self := Address(addr)
|
||||||
|
p, err := cl.FindSuccessor(ctx, np, self.id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("couldn't query own successor: %w", err)
|
||||||
|
}
|
||||||
|
// Talk to the peer to verify connection.
|
||||||
|
// We also get replication info this way.
|
||||||
|
// TODO(branden): We could just create our node with this succ and stabilize,
|
||||||
|
// but the paper doesn't in Figure 6, so we won't.
|
||||||
|
_, succ, err := cl.Neighbors(ctx, p)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("joined successor failed: %w", err)
|
||||||
|
}
|
||||||
|
// Make our successor list by shifting up theirs.
|
||||||
|
// TODO(branden): need to be more careful about capacity?
|
||||||
|
// we're always at 1 in this project anyway...
|
||||||
|
s := make([]Peer, len(succ))
|
||||||
|
s[0] = p
|
||||||
|
copy(s[1:], succ)
|
||||||
|
r := &Node{
|
||||||
|
self: self,
|
||||||
|
succ: s,
|
||||||
|
fingers: make([]Peer, 0, 8*len(ID{})),
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stabilize recomputes n's successor.
|
||||||
|
func Stabilize(ctx context.Context, cl Client, n *Node) error {
|
||||||
|
pred, _, err := cl.Neighbors(ctx, n.Successor())
|
||||||
|
if err != nil {
|
||||||
|
// TODO(zeph): replication
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if pred.IsValid() && contains(n.self.id, n.Successor().id, pred.id) && pred.id != n.Successor().id {
|
||||||
|
// Shift in the new successor.
|
||||||
|
copy(n.succ[1:], n.succ)
|
||||||
|
n.succ[0] = pred
|
||||||
|
}
|
||||||
|
return cl.Notify(ctx, n, n.Successor())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify informs n that p considers n to be p's successor.
|
||||||
|
func Notify(n *Node, np Peer) {
|
||||||
|
if !n.pred.IsValid() || contains(n.pred.id, n.self.id, np.id) && np.id != n.self.id {
|
||||||
|
n.pred = np
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FixFingers recomputes n's finger table.
|
||||||
|
func FixFingers(ctx context.Context, cl Client, n *Node) error {
|
||||||
|
// TODO(branden): need arithmetic on IDs to do this
|
||||||
|
return errors.New("not implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
// CheckPredecessor checks the health of n's predecessor.
|
||||||
|
// Any error is considered a health check failure.
|
||||||
|
func CheckPredecessor(ctx context.Context, cl Client, n *Node) {
|
||||||
|
if !n.pred.IsValid() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _, err := cl.Neighbors(ctx, n.pred)
|
||||||
|
if err != nil {
|
||||||
|
n.pred = Peer{}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user