election

package
v4.9.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 14, 2022 License: Apache-2.0 Imports: 14 Imported by: 0

README

Election Library

This is a network agnostic implementation of the leader election portion of the RAFT protocol. This library provides no peer discovery mechanism, as such the user of this library must call SetPeers() on the node when the list of peers changes. Users can use any third party service discovery mechanism, such as consul, etc, k8s, or memberlist.

For our internal uses we choose https://github.com/hashicorp/memberlist such that our services have as few external dependencies as possible.

Usage

In order to use the library on the network the user must provide a SendRPC() function at initialization time. This function will be called when RPC communication between election.Node is needed. A node that wishes to receive an RPC call must in turn call Node.ReceiveRPC() when the RPC request is received by what ever network protocol the user implements.

You can see a simple example of this using http by looking at example_test.go

Documentation

Overview

Package election is a generated protocol buffer package.

It is generated from these files:

structs.proto

It has these top-level messages:

ResetElectionReq
ResetElectionResp
ResignReq
ResignResp
HeartBeatReq
HeartBeatResp
VoteResp
VoteReq
SetPeersReq
SetPeersResp
GetStateReq
GetStateResp

Index

Constants

View Source
const (
	HeartBeatRPC     = RPC("heartbeat")
	VoteRPC          = RPC("vote")
	ResetElectionRPC = RPC("reset-election")
	ResignRPC        = RPC("resign")
	SetPeersRPC      = RPC("set-peers")
	GetStateRPC      = RPC("get-state")
	UnknownRPC       = RPC("unknown")
)

Variables

View Source
var ErrNotLeader = errors.New("not the leader")

Functions

func WaitForConnect

func WaitForConnect(address string, attempts int, interval time.Duration) error

WaitForConnect waits for the specified address to accept connections then returns nil. Returns an error if all attempts have been exhausted.

Types

type Config

type Config struct {
	// How long we should wait for a single network operation to complete.
	NetworkTimeout time.Duration

	// How long followers should wait before they decide the leader
	// lost connection to peers and therefore start a new election.
	HeartBeatTimeout time.Duration

	// How long candidates should wait for an election to complete
	// before starting a new one.
	ElectionTimeout time.Duration

	// How long the leader should wait on heart beat responses from
	// followers before it decides to step down as leader and start a
	// new election.
	LeaderQuorumTimeout time.Duration

	// The minimum number of peers that are required to form a cluster and elect a leader.
	// This is to prevent a small number of nodes (or a single node) that gets disconnected
	// from the cluster to elect a leader (assuming the peer list is updated to exclude the
	// disconnected peers). Instead nodes will wait until connectivity is restored
	// to the quorum of the cluster. The default is zero, which means if a single node is
	// disconnected from the cluster, and it's peer list only includes it's self, it will elect
	// itself leader. If we set MinimumQuorum = 2 then no leader will be elected until the peer
	// list includes at least 2 peers and a successful vote has completed.
	MinimumQuorum int

	// The Initial list of peers to be considered in the election, including ourself.
	Peers []string

	// The unique id this peer identifies itself as, as found in the Peers list.
	// This is typically an ip:port address the node is listening to for RPC requests.
	UniqueID string

	// Called when the leader changes
	OnUpdate OnUpdate

	// The logger used errors and warning
	Log logrus.FieldLogger

	// Sends an RPC request to a peer, This function must be provided and can
	// utilize any network communication the implementer wishes. If context cancelled
	// should return an error.
	SendRPC SendRPCFunc
}

type GetStateReq

type GetStateReq struct {
}

Get the current state of the node

func (*GetStateReq) Descriptor

func (*GetStateReq) Descriptor() ([]byte, []int)

func (*GetStateReq) ProtoMessage

func (*GetStateReq) ProtoMessage()

func (*GetStateReq) Reset

func (m *GetStateReq) Reset()

func (*GetStateReq) String

func (m *GetStateReq) String() string

type GetStateResp

type GetStateResp struct {
	Leader string   `protobuf:"bytes,1,opt,name=leader" json:"leader,omitempty"`
	State  string   `protobuf:"bytes,2,opt,name=state" json:"state,omitempty"`
	Peers  []string `protobuf:"bytes,3,rep,name=peers" json:"peers,omitempty"`
}

func (*GetStateResp) Descriptor

func (*GetStateResp) Descriptor() ([]byte, []int)

func (*GetStateResp) GetLeader

func (m *GetStateResp) GetLeader() string

func (*GetStateResp) GetPeers

func (m *GetStateResp) GetPeers() []string

func (*GetStateResp) GetState

func (m *GetStateResp) GetState() string

func (*GetStateResp) ProtoMessage

func (*GetStateResp) ProtoMessage()

func (*GetStateResp) Reset

func (m *GetStateResp) Reset()

func (*GetStateResp) String

func (m *GetStateResp) String() string

type HeartBeatReq

type HeartBeatReq struct {
	// The leader this heart beat is from
	From string `protobuf:"bytes,1,opt,name=from" json:"from,omitempty"`
	// The current term of the leader
	Term uint64 `protobuf:"varint,2,opt,name=term" json:"term,omitempty"`
}

Sent by the leader of the election to all followers

func (*HeartBeatReq) Descriptor

func (*HeartBeatReq) Descriptor() ([]byte, []int)

func (*HeartBeatReq) GetFrom

func (m *HeartBeatReq) GetFrom() string

func (*HeartBeatReq) GetTerm

func (m *HeartBeatReq) GetTerm() uint64

func (*HeartBeatReq) ProtoMessage

func (*HeartBeatReq) ProtoMessage()

func (*HeartBeatReq) Reset

func (m *HeartBeatReq) Reset()

func (*HeartBeatReq) String

func (m *HeartBeatReq) String() string

type HeartBeatResp

type HeartBeatResp struct {
	// The follower who is responding
	From string `protobuf:"bytes,1,opt,name=from" json:"from,omitempty"`
	// The term the heart beat is for
	Term uint64 `protobuf:"varint,2,opt,name=term" json:"term,omitempty"`
}

Response to a heart beat request

func (*HeartBeatResp) Descriptor

func (*HeartBeatResp) Descriptor() ([]byte, []int)

func (*HeartBeatResp) GetFrom

func (m *HeartBeatResp) GetFrom() string

func (*HeartBeatResp) GetTerm

func (m *HeartBeatResp) GetTerm() uint64

func (*HeartBeatResp) ProtoMessage

func (*HeartBeatResp) ProtoMessage()

func (*HeartBeatResp) Reset

func (m *HeartBeatResp) Reset()

func (*HeartBeatResp) String

func (m *HeartBeatResp) String() string

type Node

type Node interface {
	// Starts the main election loop.
	Start(ctx context.Context) error

	// Cancels the election, resigns if we are leader and waits for all go
	// routines to complete before returning.
	Stop(ctx context.Context) error

	// Set the list of peers to be considered for the election
	SetPeers(ctx context.Context, peers []string) error

	// If leader, resigns as leader and starts a new election that we will not
	// participate in. returns ErrNotLeader if not currently the leader
	Resign(ctx context.Context) error

	// IsLeader is a convenience function that calls GetState() and returns true
	// if this node was elected leader. May block if main loop is occupied.
	IsLeader() bool

	// GetLeader is a convenience function that calls GetState() returns the
	// unique id of the node that is currently leader. May block if main loop is occupied.
	GetLeader() string

	// Returns the current state of this node
	GetState(ctx context.Context) (NodeState, error)

	// Called when this peer receives a RPC request from a peer
	ReceiveRPC(RPCRequest, *RPCResponse)
}

func NewNode

func NewNode(conf Config) (Node, error)

Creates a new node. You must call Start() to be participate in the election.

type NodeState

type NodeState GetStateResp

type OnUpdate

type OnUpdate func(string)

type RPC

type RPC string

type RPCPayload

type RPCPayload struct {
	RPC      RPC             `json:"rpc"`
	Request  json.RawMessage `json:"request,omitempty"`
	Response json.RawMessage `json:"response,omitempty"`
	Error    string          `json:"error,omitempty"`
}

type RPCRequest

type RPCRequest struct {
	RPC     RPC
	Request interface{}
	// contains filtered or unexported fields
}

func (RPCRequest) MarshalJSON

func (r RPCRequest) MarshalJSON() ([]byte, error)

func (*RPCRequest) UnmarshalJSON

func (r *RPCRequest) UnmarshalJSON(s []byte) error

type RPCResponse

type RPCResponse struct {
	RPC      RPC
	Response interface{}
	Error    string
}

func (RPCResponse) MarshalJSON

func (r RPCResponse) MarshalJSON() ([]byte, error)

func (*RPCResponse) UnmarshalJSON

func (r *RPCResponse) UnmarshalJSON(s []byte) error

type ResetElectionReq

type ResetElectionReq struct {
}

Resets the current state of a node to 'candidate'

func (*ResetElectionReq) Descriptor

func (*ResetElectionReq) Descriptor() ([]byte, []int)

func (*ResetElectionReq) ProtoMessage

func (*ResetElectionReq) ProtoMessage()

func (*ResetElectionReq) Reset

func (m *ResetElectionReq) Reset()

func (*ResetElectionReq) String

func (m *ResetElectionReq) String() string

type ResetElectionResp

type ResetElectionResp struct {
}

func (*ResetElectionResp) Descriptor

func (*ResetElectionResp) Descriptor() ([]byte, []int)

func (*ResetElectionResp) ProtoMessage

func (*ResetElectionResp) ProtoMessage()

func (*ResetElectionResp) Reset

func (m *ResetElectionResp) Reset()

func (*ResetElectionResp) String

func (m *ResetElectionResp) String() string

type ResignReq

type ResignReq struct {
}

Asks the node to resign as leader

func (*ResignReq) Descriptor

func (*ResignReq) Descriptor() ([]byte, []int)

func (*ResignReq) ProtoMessage

func (*ResignReq) ProtoMessage()

func (*ResignReq) Reset

func (m *ResignReq) Reset()

func (*ResignReq) String

func (m *ResignReq) String() string

type ResignResp

type ResignResp struct {
	// True if the receiver is leader and stepped down
	Success bool `protobuf:"varint,1,opt,name=success" json:"success,omitempty"`
}

func (*ResignResp) Descriptor

func (*ResignResp) Descriptor() ([]byte, []int)

func (*ResignResp) GetSuccess

func (m *ResignResp) GetSuccess() bool

func (*ResignResp) ProtoMessage

func (*ResignResp) ProtoMessage()

func (*ResignResp) Reset

func (m *ResignResp) Reset()

func (*ResignResp) String

func (m *ResignResp) String() string

type SendRPCFunc

type SendRPCFunc func(context.Context, string, RPCRequest, *RPCResponse) error

type SetPeersReq

type SetPeersReq struct {
	// A list of peers
	Peers []string `protobuf:"bytes,1,rep,name=peers" json:"peers,omitempty"`
}

Set the peers this node will consider during the election

func (*SetPeersReq) Descriptor

func (*SetPeersReq) Descriptor() ([]byte, []int)

func (*SetPeersReq) GetPeers

func (m *SetPeersReq) GetPeers() []string

func (*SetPeersReq) ProtoMessage

func (*SetPeersReq) ProtoMessage()

func (*SetPeersReq) Reset

func (m *SetPeersReq) Reset()

func (*SetPeersReq) String

func (m *SetPeersReq) String() string

type SetPeersResp

type SetPeersResp struct {
}

func (*SetPeersResp) Descriptor

func (*SetPeersResp) Descriptor() ([]byte, []int)

func (*SetPeersResp) ProtoMessage

func (*SetPeersResp) ProtoMessage()

func (*SetPeersResp) Reset

func (m *SetPeersResp) Reset()

func (*SetPeersResp) String

func (m *SetPeersResp) String() string

type VoteReq

type VoteReq struct {
	// The candidate who is requesting the targets vote
	Candidate string `protobuf:"bytes,1,opt,name=candidate" json:"candidate,omitempty"`
	// The term this vote is for.
	Term uint64 `protobuf:"varint,2,opt,name=term" json:"term,omitempty"`
}

A vote request sent to all peers at the start of an election.

func (*VoteReq) Descriptor

func (*VoteReq) Descriptor() ([]byte, []int)

func (*VoteReq) GetCandidate

func (m *VoteReq) GetCandidate() string

func (*VoteReq) GetTerm

func (m *VoteReq) GetTerm() uint64

func (*VoteReq) ProtoMessage

func (*VoteReq) ProtoMessage()

func (*VoteReq) Reset

func (m *VoteReq) Reset()

func (*VoteReq) String

func (m *VoteReq) String() string

type VoteResp

type VoteResp struct {
	// The candidate who responded
	Candidate string `protobuf:"bytes,1,opt,name=candidate" json:"candidate,omitempty"`
	// The term this vote response is for
	Term uint64 `protobuf:"varint,2,opt,name=term" json:"term,omitempty"`
	// If the Vote was granted by this node
	Granted bool `protobuf:"varint,3,opt,name=granted" json:"granted,omitempty"`
}

func (*VoteResp) Descriptor

func (*VoteResp) Descriptor() ([]byte, []int)

func (*VoteResp) GetCandidate

func (m *VoteResp) GetCandidate() string

func (*VoteResp) GetGranted

func (m *VoteResp) GetGranted() bool

func (*VoteResp) GetTerm

func (m *VoteResp) GetTerm() uint64

func (*VoteResp) ProtoMessage

func (*VoteResp) ProtoMessage()

func (*VoteResp) Reset

func (m *VoteResp) Reset()

func (*VoteResp) String

func (m *VoteResp) String() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL