Documentation
¶
Index ¶
- func AppendMessageIsNewer(state *State, msg *AppendMessage) bool
- func AppendMessageIsUpToDate(state *State, msg *AppendMessage) bool
- func AppendMessageLogIsLonger(state *State, msg *AppendMessage) bool
- func ApplyData(entry LogEntry) error
- func CanVoteFor(state *State, msg *RequestVoteMessage) bool
- func ConfigureLog(logLevel *string)
- func GenerateHash(message json.RawMessage) (string, error)
- func NumMajority(state *State) int
- func PrevLogsMatch(state *State, msg *AppendMessage) bool
- func VoteMsgIsUpToDate(state *State, msg *RequestVoteMessage) bool
- type AppendMessage
- type AppendMessageReply
- type Broker
- func (broker *Broker) AppendMessage(ap AppendMessage, apr *AppendMessageReply) error
- func (broker *Broker) Execute(ctx context.Context)
- func (broker *Broker) NextRole(ctx context.Context) (Role, error)
- func (broker *Broker) RequestVote(rvm RequestVoteMessage, rvp *RequestVoteReply) error
- func (broker *Broker) StartClientServer()
- func (broker *Broker) StartRPCServer()
- func (broker *Broker) StopClientServer()
- func (broker *Broker) StopRPCServer()
- func (broker *Broker) SwitchRole(role Role)
- type Candidate
- func (candidate *Candidate) AppendMessage(message AppendMessage, reply *AppendMessageReply) error
- func (candidate *Candidate) Execute(ctx context.Context)
- func (candidate *Candidate) GetChangeSignal() chan *RoleTransition
- func (candidate *Candidate) GetState() *State
- func (candidate *Candidate) HandleClient(w http.ResponseWriter, r *http.Request)
- func (candidate *Candidate) Init(state *State) *Candidate
- func (candidate *Candidate) RequestVote(msg RequestVoteMessage, rply *RequestVoteReply) error
- func (candidate *Candidate) SendRequest(ctx context.Context, vote_status chan bool, element string)
- type ConfigData
- type Follower
- func (follower *Follower) AppendMessage(message AppendMessage, reply *AppendMessageReply) error
- func (follower *Follower) Apply()
- func (follower *Follower) Execute(ctx context.Context)
- func (follower *Follower) GetChangeSignal() chan *RoleTransition
- func (follower *Follower) GetState() *State
- func (follower *Follower) HandleClient(w http.ResponseWriter, r *http.Request)
- func (follower *Follower) Init(state *State) *Follower
- func (follower *Follower) RequestVote(msg RequestVoteMessage, rply *RequestVoteReply) error
- type Leader
- func (leader *Leader) AppendMessage(msg AppendMessage, rply *AppendMessageReply) error
- func (leader *Leader) Apply()
- func (leader *Leader) Execute(ctx context.Context)
- func (leader *Leader) GetChangeSignal() chan *RoleTransition
- func (leader *Leader) GetState() *State
- func (leader *Leader) GetStatus(hash string) StatusResponse
- func (leader *Leader) HandleClient(w http.ResponseWriter, r *http.Request)
- func (leader *Leader) Init(state *State) *Leader
- func (leader *Leader) Replicate(parent_ctx context.Context, msg LogEntry)
- func (leader *Leader) RequestVote(msg RequestVoteMessage, rply *RequestVoteReply) error
- func (leader *Leader) SendHeartbeats(ctx context.Context)
- type LogEntry
- type MessageData
- type PeerAddress
- type RequestVoteMessage
- type RequestVoteReply
- type Role
- type RoleTransition
- type State
- type StatusResponse
- type SubmitResponse
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AppendMessageIsNewer ¶
func AppendMessageIsNewer(state *State, msg *AppendMessage) bool
func AppendMessageIsUpToDate ¶
func AppendMessageIsUpToDate(state *State, msg *AppendMessage) bool
Compare the terms of the message and the machine.
func AppendMessageLogIsLonger ¶
func AppendMessageLogIsLonger(state *State, msg *AppendMessage) bool
when two leaders can't decide which one should be a leader, it resolves to which one has more committed messages
func CanVoteFor ¶
func CanVoteFor(state *State, msg *RequestVoteMessage) bool
func ConfigureLog ¶
func ConfigureLog(logLevel *string)
func GenerateHash ¶
func GenerateHash(message json.RawMessage) (string, error)
func NumMajority ¶
Get the number of machines needed for a majority, does remove the calling machine from the majority. AKA the number of separate machines needed for a majority.
func PrevLogsMatch ¶
func PrevLogsMatch(state *State, msg *AppendMessage) bool
If the log at the previous index does not contain the same term, then return false
func VoteMsgIsUpToDate ¶
func VoteMsgIsUpToDate(state *State, msg *RequestVoteMessage) bool
We may be facing issues with this function (maybe with the append message functions too), because of the following excerpt from the Raft paper: "Raft determines which of two logs is more up-to-date by comparing the index and term of the last entries in the logs. If the logs have last entries with different terms, then the log with the later term is more up-to-date. If the logs end with the same term, then whichever log is longer is more up-to-date."
Types ¶
type AppendMessage ¶
type AppendMessageReply ¶
type Broker ¶
type Broker struct {
Role Role
// contains filtered or unexported fields
}
A wrapper for the state/role so that RPC calls don't have to be specific about the concrete type it is making the call to. All its methods forward to the Role struct it has an instance of.
func (*Broker) AppendMessage ¶
func (broker *Broker) AppendMessage(ap AppendMessage, apr *AppendMessageReply) error
func (*Broker) RequestVote ¶
func (broker *Broker) RequestVote(rvm RequestVoteMessage, rvp *RequestVoteReply) error
func (*Broker) StartClientServer ¶
func (broker *Broker) StartClientServer()
func (*Broker) StartRPCServer ¶
func (broker *Broker) StartRPCServer()
func (*Broker) StopClientServer ¶
func (broker *Broker) StopClientServer()
func (*Broker) StopRPCServer ¶
func (broker *Broker) StopRPCServer()
func (*Broker) SwitchRole ¶
type Candidate ¶
type Candidate struct {
State *State
ChangeSignal chan *RoleTransition
}
func NewCandidate ¶
func NewCandidate() *Candidate
func (*Candidate) AppendMessage ¶
func (candidate *Candidate) AppendMessage(message AppendMessage, reply *AppendMessageReply) error
Another machine has turned into a leader. It has done so with a majority of votes, so now this machine should also accept the leader. Turn to follower if term is equal to or less than the message term
func (*Candidate) GetChangeSignal ¶
func (candidate *Candidate) GetChangeSignal() chan *RoleTransition
func (*Candidate) HandleClient ¶
func (candidate *Candidate) HandleClient(w http.ResponseWriter, r *http.Request)
func (*Candidate) RequestVote ¶
func (candidate *Candidate) RequestVote(msg RequestVoteMessage, rply *RequestVoteReply) error
type ConfigData ¶
type ConfigData struct {
Old []PeerAddress `json:"old"`
New []PeerAddress `json:"new"`
}
type Follower ¶
type Follower struct {
State *State
ChangeSignal chan *RoleTransition
// contains filtered or unexported fields
}
func NewFollower ¶
func NewFollower() *Follower
func (*Follower) AppendMessage ¶
func (follower *Follower) AppendMessage(message AppendMessage, reply *AppendMessageReply) error
func (*Follower) GetChangeSignal ¶
func (follower *Follower) GetChangeSignal() chan *RoleTransition
func (*Follower) HandleClient ¶
func (follower *Follower) HandleClient(w http.ResponseWriter, r *http.Request)
func (*Follower) RequestVote ¶
func (follower *Follower) RequestVote(msg RequestVoteMessage, rply *RequestVoteReply) error
type Leader ¶
type Leader struct {
State *State
// essentially a queue of messages that we have to try and append
MessageQueue chan LogEntry
ChangeSignal chan *RoleTransition
// contains filtered or unexported fields
}
func (*Leader) AppendMessage ¶
func (leader *Leader) AppendMessage(msg AppendMessage, rply *AppendMessageReply) error
func (*Leader) GetChangeSignal ¶
func (leader *Leader) GetChangeSignal() chan *RoleTransition
func (*Leader) GetStatus ¶
func (leader *Leader) GetStatus(hash string) StatusResponse
func (*Leader) HandleClient ¶
func (leader *Leader) HandleClient(w http.ResponseWriter, r *http.Request)
HandleClient routes incoming client requests to the appropriate handler.
Endpoints:
POST /api
Submit a new log entry (write, update, delete) to the cluster.
Request body:
{
"type": "data",
"message": {
"path": "documents/hello.txt",
"blob": "hello world",
"operation": "write"
}
}
Response (200):
{
"hash": "abc123...",
"timestamp": "2024-01-01T00:00:00.000000000Z"
}
Examples:
Write a file:
curl -X POST http://localhost:8080/api \
-H "Content-Type: application/json" \
-d '{"type":"data","message":{"path":"docs/hello.txt","blob":"hello world","operation":"write"}}'
Update a file:
curl -X POST http://localhost:8080/api \
-H "Content-Type: application/json" \
-d '{"type":"data","message":{"path":"docs/hello.txt","blob":"updated content","operation":"update"}}'
Delete a file:
curl -X POST http://localhost:8080/api \
-H "Content-Type: application/json" \
-d '{"type":"data","message":{"path":"docs/hello.txt","blob":"","operation":"delete"}}'
GET /status?hash=<hash>
Poll for the status of a previously submitted log entry.
Response (200):
{ "status": "success", "hash": "abc123..." }
{ "status": "pending", "hash": "abc123..." }
{ "status": "failed", "hash": "abc123..." }
Example:
curl "http://localhost:8080/status?hash=abc123..."
GET /file?path=<path>
Read a file from the committed state machine.
Response (200): "file contents here"
Response (404): "File not found."
Example:
curl "http://localhost:8080/file?path=docs/hello.txt"
GET /files
List all files in the data directory.
Response (200):
["docs/hello.txt", "docs/world.txt"]
func (*Leader) Replicate ¶
In its current form, this implementation may cause issues, cancelling normally functioning appends when a majority is achieved. This needs further testing, but it should still offer certainty that a majority has replicated any one log message.
func (*Leader) RequestVote ¶
func (leader *Leader) RequestVote(msg RequestVoteMessage, rply *RequestVoteReply) error
func (*Leader) SendHeartbeats ¶
type LogEntry ¶
type LogEntry struct {
Term int `json:"term"`
// Type specifies what type of message this should be interpreted as. This
// may be a config change, update to storage, or potential snapshot data
Type string `json:"type"`
Message json.RawMessage `json:"message"`
// Hash is generated from the message content and used to track
// the status of the entry. Persisted so a new leader can recover this.
Hash string `json:"hash"`
Timestamp time.Time `json:"timestamp"`
}
type MessageData ¶
type PeerAddress ¶
type PeerAddress struct {
Host string `json:"host"`
RPCPort int `json:"rpcport"`
HTTPPort int `json:"httpport"`
}
func (PeerAddress) HTTPAddr ¶
func (p PeerAddress) HTTPAddr() string
func (PeerAddress) RPCAddr ¶
func (p PeerAddress) RPCAddr() string
type RequestVoteMessage ¶
type RequestVoteMessage struct {
Term int
CandidateId PeerAddress
LastLogIndex int
LastLogTerm int
}
type RequestVoteReply ¶
type Role ¶
type Role interface {
// RPC methods
RequestVote(RequestVoteMessage, *RequestVoteReply) error
AppendMessage(AppendMessage, *AppendMessageReply) error
// Used to implement the actual logic of the replicas/individual roles
// Returns the next role to transition to
Execute(context.Context)
HandleClient(w http.ResponseWriter, r *http.Request)
GetChangeSignal() chan *RoleTransition
// Used as a type check to ensure that the role has a relation
// to the underlying state object of the replica
GetState() *State
}
type RoleTransition ¶
type RoleTransition struct {
RoleName string // "leader", "candidate", "follower", or "" for shutdown
State *State
}
RoleTransition represents a request to transition to a new role
type State ¶
type State struct {
// used to redirect clients to the appropriate leader
Leader PeerAddress `json:"leader"`
// the running replica's host/port
PeerAddress PeerAddress `json:"address"`
// mostly used for debugging and testing, stores a string representation of the current role
Role string `json:"role"`
// the set of servers participating in consensus
Config []PeerAddress `json:"config"`
// persistent state
Log []LogEntry `json:"log"`
CommitTerm int `json:"commitTerm"`
VotedFor PeerAddress `json:"votedFor"`
// volatile state
CommitIndex int `json:"commitIndex"`
LastApplied int `json:"lastApplied"`
// timeout in milliseconds
ElectionTimeout int `json:"electionTimeout"`
// Index 0 is the minimum timeout, and index 1 is the maximum, used in
// candidate replicas to calculate how long they should time out for after
// losing an election
RandomizedTimeout []int `json:"randomizedTimeout"`
}
func EmptyState ¶
func EmptyState() State
func (*State) Debug_Print ¶
func (state *State) Debug_Print()
type StatusResponse ¶
type StatusResponse struct {
Status string `json:"status"` // "success", "pending", "failed"
Hash string `json:"hash"`
}
Response sent back when a client polls for status
type SubmitResponse ¶
Response sent back to the client when they first submit a message