wendy: secondbit.org/wendy Index | Files

package wendy

import "secondbit.org/wendy"

Package wendy implements a fault-tolerant, concurrency-safe distributed hash table.

Self-Organising Services

Wendy is a package to help make your Go programs self-organising. It makes communicating between a variable number of machines easy and reliable. Machines are referred to as Nodes, which create a Cluster together. Messages can then be routed throughout the Cluster.

Getting Started

Getting your own Cluster running is easy. Just create a Node, build a Cluster around it, and announce your presence.

hostname, err := os.Hostname()
if err != nil {
id, err := wendy.NodeIDFromBytes([]byte(hostname+" test server"))
if err != nil {
node := wendy.NewNode(id, "your_local_ip_address", "your_global_ip_address", "your_region", 8080)

credentials := wendy.Passphrase("I <3 Gophers.")
cluster := wendy.NewCluster(node, credentials)
go func() {
	defer cluster.Stop()
	err := cluster.Listen()
	if err != nil {
cluster.Join("ip of another Node", 8080) // ports can be different for each Node
select {}

About Credentials

Credentials are an interface that is used to control access to your Cluster. Wendy provides the Passphrase implementation, which limits access to Nodes that set their Credentials to the same string. You can feel free to make your own--the only requirements are that you return a slice of bytes when the Marshal() function is called and that you return a boolean when the Valid([]byte) function is called, which should return true if the supplied slice of bytes can be unmarshaled to a valid instance of your Credentials implementation AND that valid instance should be granted access to this Cluster.


Package Files

cluster.go doc.go leafset.go message.go neighborhood.go node.go nodeid.go table.go wendy.go


const (
    NODE_JOIN = byte(iota) // Used when a Node wishes to join the cluster
    NODE_EXIT              // Used when a Node leaves the cluster
    HEARTBEAT              // Used when a Node is being tested
    STAT_DATA              // Used when a Node broadcasts state info
    STAT_REQ               // Used when a Node is requesting state info
    NODE_RACE              // Used when a Node hits a race condition
    NODE_REPR              // Used when a Node needs to repair its LeafSet
    NODE_ANN               // Used when a Node broadcasts its presence
const (
    LogLevelDebug = iota

type Application Uses

type Application interface {
    OnError(err error)
    OnDeliver(msg Message)
    OnForward(msg *Message, nextId NodeID) bool // return False if Wendy should not forward
    OnNewLeaves(leafset []*Node)
    OnNodeJoin(node Node)
    OnNodeExit(node Node)
    OnHeartbeat(node Node)

Application is an interface that other packages can fulfill to hook into Wendy.

OnError is called on errors that are even remotely recoverable, passing the error that was raised.

OnDeliver is called when the current Node is determined to be the final destination of a Message. It passes the Message that was received.

OnForward is called immediately before a Message is forwarded to the next Node in its route through the Cluster. The function receives a pointer to the Message, which can be modified before it is sent, and the ID of the next step in the Message's route. The function must return a boolean; true if the Message should continue its way through the Cluster, false if the Message should be prematurely terminated instead of forwarded.

OnNewLeaves is called when the current Node's leafSet is updated. The function receives a dump of the leafSet.

OnNodeJoin is called when the current Node learns of a new Node in the Cluster. It receives the Node that just joined.

OnNodeExit is called when a Node is discovered to no longer be participating in the Cluster. It is passed the Node that just left the Cluster. Note that by the time this method is called, the Node is no longer reachable.

OnHeartbeat is called when the current Node receives a heartbeat from another Node. Heartbeats are sent at a configurable interval, if no messages have been sent between the Nodes, and serve the purpose of a health check.

type Cluster Uses

type Cluster struct {
    // contains filtered or unexported fields

Cluster holds the information about the state of the network. It is the main interface to the distributed network of Nodes.

func NewCluster Uses

func NewCluster(self *Node, credentials Credentials) *Cluster

NewCluster creates a new instance of a connection to the network and intialises the state tables and channels it requires.

func (*Cluster) GetIP Uses

func (c *Cluster) GetIP(node Node) string

GetIP returns the IP address to use when communicating with a Node.

func (*Cluster) ID Uses

func (c *Cluster) ID() NodeID

ID returns an identifier for the Cluster. It uses the ID of the current Node.

func (*Cluster) Join Uses

func (c *Cluster) Join(ip string, port int) error

Join expresses a Node's desire to join the Cluster, kicking off a process that will populate its child leafSet, neighborhoodSet and routingTable. Once that process is complete, the Node can be said to be fully participating in the Cluster.

The IP and port passed to Join should be those of a known Node in the Cluster. The algorithm assumes that the known Node is close in proximity to the current Node, but that is not a hard requirement.

func (*Cluster) Kill Uses

func (c *Cluster) Kill()

Kill shuts down the local connection to the Cluster, removing the local Node from the Cluster and preventing it from receiving or sending further messages.

Unlike Stop, Kill immediately disconnects the Node without sending a message to let other Nodes know of its exit.

func (*Cluster) Listen Uses

func (c *Cluster) Listen() error

Listen starts the Cluster listening for events, including all the individual listeners for each state sub-object.

Note that Listen does *not* join a Node to the Cluster. The Node must announce its presence before the Node is considered active in the Cluster.

func (*Cluster) NewMessage Uses

func (c *Cluster) NewMessage(purpose byte, key NodeID, value []byte) Message

func (*Cluster) RegisterCallback Uses

func (c *Cluster) RegisterCallback(app Application)

RegisterCallback allows anything that fulfills the Application interface to be hooked into the Wendy's callbacks.

func (*Cluster) Route Uses

func (c *Cluster) Route(key NodeID) (*Node, error)

Route checks the leafSet and routingTable to see if there's an appropriate match for the NodeID. If there is a better match than the current Node, a pointer to that Node is returned. Otherwise, nil is returned (and the message should be delivered).

func (*Cluster) Send Uses

func (c *Cluster) Send(msg Message) error

Send routes a message through the Cluster.

func (*Cluster) SendToIP Uses

func (c *Cluster) SendToIP(msg Message, address string) error

SendToIP sends a message directly to an IP using the Wendy networking logic.

func (*Cluster) SetHeartbeatFrequency Uses

func (c *Cluster) SetHeartbeatFrequency(freq int)

SetHeartbeatFrequency sets the frequency in seconds with which heartbeats will be sent from this Node to test the health of other Nodes in the Cluster.

func (*Cluster) SetLogLevel Uses

func (c *Cluster) SetLogLevel(level int)

SetLogLevel sets the level of logging that will be written to the Logger. It will be mirrored to the child routingTable and leafSet.

Use wendy.LogLevelDebug to write to the most verbose level of logging, helpful for debugging.

Use wendy.LogLevelWarn (the default) to write on events that may, but do not necessarily, indicate an error.

Use wendy.LogLevelError to write only when an event occurs that is undoubtedly an error.

func (*Cluster) SetLogger Uses

func (c *Cluster) SetLogger(l *log.Logger)

SetLogger sets the log.Logger that the Cluster, along with its child routingTable and leafSet, will write to.

func (*Cluster) SetNetworkTimeout Uses

func (c *Cluster) SetNetworkTimeout(timeout int)

SetNetworkTimeout sets the number of seconds before which network requests will be considered timed out and killed.

func (*Cluster) Stop Uses

func (c *Cluster) Stop()

Stop gracefully shuts down the local connection to the Cluster, removing the local Node from the Cluster and preventing it from receiving or sending further messages.

Before it disconnects the Node, Stop contacts every Node it knows of to warn them of its departure. If a graceful disconnect is not necessary, Kill should be used instead. Nodes will remove the Node from their state tables next time they attempt to contact it.

func (*Cluster) String Uses

func (c *Cluster) String() string

String returns a string representation of the Cluster, in the form of its ID.

type Credentials Uses

type Credentials interface {
    Valid([]byte) bool
    Marshal() []byte

Credentials is an interface that can be fulfilled to limit access to the Cluster.

type IdentityError Uses

type IdentityError struct {
    Action      string
    Preposition string
    Container   string

IdentityError represents an error that was raised when a Node attempted to perform actions on its state tables using its own ID, which is problematic. It is its own type for the purposes of handling the error.

func (IdentityError) Error Uses

func (e IdentityError) Error() string

Error returns the IdentityError as a string and fulfills the error interface.

type InvalidArgumentError Uses

type InvalidArgumentError string

InvalidArgumentError represents an error that is raised when arguments that are invalid are passed to a function that depends on those arguments. It is its own type for the purposes of handling the error.

func (InvalidArgumentError) Error Uses

func (e InvalidArgumentError) Error() string

type Message Uses

type Message struct {
    Purpose     byte
    Sender      Node   // The Node a message originated at
    Key         NodeID // The message's ID
    Value       []byte // The message being passed
    Credentials []byte // The Credentials used to authenticate the Message
    LSVersion   uint64 // The version of the leaf set, for join messages
    RTVersion   uint64 // The version of the routing table, for join messages
    NSVersion   uint64 // The version of the neighborhood set, for join messages
    Hop         int    // The number of hops the message has taken

Message represents the messages that are sent through the cluster of Nodes

func (*Message) String Uses

func (m *Message) String() string

String returns a string representation of a message.

type Node Uses

type Node struct {
    LocalIP  string // The IP through which the Node should be accessed by other Nodes with an identical Region
    GlobalIP string // The IP through which the Node should be accessed by other Nodes whose Region differs
    Port     int    // The port the Node is listening on
    Region   string // A string that allows you to intelligently route between local and global requests for, e.g., EC2 regions
    ID       NodeID
    // contains filtered or unexported fields

Node represents a specific machine in the cluster.

func NewNode Uses

func NewNode(id NodeID, local, global, region string, port int) *Node

NewNode initialises a new Node and its associated mutexes. It does *not* update the proximity of the Node.

func (Node) GetIP Uses

func (self Node) GetIP(other Node) string

GetIP returns the IP and port that should be used when communicating with a Node, to respect Regions.

func (Node) IsZero Uses

func (self Node) IsZero() bool

IsZero returns whether or the given Node has been initialised or if it's an empty Node struct. IsZero returns true if the Node has been initialised, false if it's an empty struct.

func (*Node) LastHeardFrom Uses

func (self *Node) LastHeardFrom() time.Time

func (*Node) Proximity Uses

func (self *Node) Proximity(n *Node) int64

Proximity returns the proximity score for the Node, adjusted for the Region. The proximity score of a Node reflects how close it is to the current Node; a lower proximity score means a closer Node. Nodes outside the current Region are penalised by a multiplier.

type NodeID Uses

type NodeID [2]uint64

NodeID is a unique address for a node in the network.

func NodeIDFromBytes Uses

func NodeIDFromBytes(source []byte) (NodeID, error)

NodeIDFromBytes creates a NodeID from an array of bytes. It returns the created NodeID, trimmed to the first 32 digits, or nil and an error if there are not enough bytes to yield 32 digits.

func (NodeID) Base10 Uses

func (id NodeID) Base10() *big.Int

Base10 returns the NodeID as a base 10 number, translating each base 16 digit.

func (NodeID) CommonPrefixLen Uses

func (id NodeID) CommonPrefixLen(other NodeID) int

CommonPrefixLen returns the number of leading digits that are equal in the two NodeIDs.

func (NodeID) Diff Uses

func (id NodeID) Diff(other NodeID) *big.Int

Diff returns the difference between two NodeIDs as an absolute value. It performs the modular arithmetic necessary to find the shortest distance between the IDs in the (2^128)-1 item nodespace.

func (NodeID) Digit Uses

func (id NodeID) Digit(i int) byte

Digit returns the ith 4-bit digit in the NodeID. If i >= 32, Digit panics.

func (NodeID) Equals Uses

func (id NodeID) Equals(other NodeID) bool

Equals tests two NodeIDs for equality and returns true if they are considered equal, false if they are considered inequal. NodeIDs are considered equal if each digit of the NodeID is equal.

func (NodeID) Less Uses

func (id NodeID) Less(other NodeID) bool

Less tests two NodeIDs to determine if the ID the method is called on is less than the ID passed as an argument. An ID is considered to be less if the first inequal digit between the two IDs is considered to be less.

func (NodeID) MarshalJSON Uses

func (id NodeID) MarshalJSON() ([]byte, error)

MarshalJSON fulfills the Marshaler interface, allowing NodeIDs to be serialised to JSON safely.

func (NodeID) RelPos Uses

func (id NodeID) RelPos(other NodeID) int

RelPos uses modular arithmetic to determine whether the NodeID passed as an argument is to the left of the NodeID it is called on (-1), the same as the NodeID it is called on (0), or to the right of the NodeID it is called on (1) in the circular node space.

func (NodeID) String Uses

func (id NodeID) String() string

String returns the hexadecimal string encoding of the NodeID.

func (*NodeID) UnmarshalJSON Uses

func (id *NodeID) UnmarshalJSON(source []byte) error

UnmarshalJSON fulfills the Unmarshaler interface, allowing NodeIDs to be unserialised from JSON safely.

type Passphrase Uses

type Passphrase string

Passphrase is an implementation of Credentials that grants access to the Cluster if the Node has the same Passphrase set

func (Passphrase) Marshal Uses

func (p Passphrase) Marshal() []byte

func (Passphrase) Valid Uses

func (p Passphrase) Valid(supplied []byte) bool

type StateMask Uses

type StateMask struct {
    Mask byte
    Rows []int
    Cols []int

Package wendy imports 16 packages (graph) and is imported by 1 packages. Updated 2018-01-17. Refresh now. Tools for package owners.