Format with Fantomas
This commit is contained in:
@@ -10,16 +10,14 @@ module TestServer =
|
|||||||
[<Test>]
|
[<Test>]
|
||||||
let foo () =
|
let foo () =
|
||||||
let cluster = InMemoryCluster.make<int> 5
|
let cluster = InMemoryCluster.make<int> 5
|
||||||
|
|
||||||
let logger, logs =
|
let logger, logs =
|
||||||
let logs = ResizeArray ()
|
let logs = ResizeArray ()
|
||||||
let logLine (s : string) =
|
let logLine (s : string) = lock logs (fun () -> logs.Add s)
|
||||||
lock logs (fun () ->
|
|
||||||
logs.Add s
|
|
||||||
)
|
|
||||||
let freezeLogs () =
|
let freezeLogs () =
|
||||||
lock logs (fun () ->
|
lock logs (fun () -> logs |> Seq.toList)
|
||||||
logs |> Seq.toList
|
|
||||||
)
|
|
||||||
logLine, freezeLogs
|
logLine, freezeLogs
|
||||||
|
|
||||||
let sendMessage = cluster.Servers.[0].OutboundMessageChannel
|
let sendMessage = cluster.Servers.[0].OutboundMessageChannel
|
||||||
@@ -29,15 +27,10 @@ module TestServer =
|
|||||||
{
|
{
|
||||||
CandidateTerm = 0<Term>
|
CandidateTerm = 0<Term>
|
||||||
CandidateId = 1<ServerId>
|
CandidateId = 1<ServerId>
|
||||||
ReplyChannel =
|
ReplyChannel = fun message -> logger (sprintf "Received message for term %i" message.VoterTerm)
|
||||||
fun message ->
|
|
||||||
logger (sprintf "Received message for term %i" message.VoterTerm)
|
|
||||||
CandidateLastLogEntry = 0<LogIndex>, 0<Term>
|
CandidateLastLogEntry = 0<LogIndex>, 0<Term>
|
||||||
}
|
}
|
||||||
|> Message.RequestVote
|
|> Message.RequestVote
|
||||||
|> sendMessage 0<ServerId>
|
|> sendMessage 0<ServerId>
|
||||||
|
|
||||||
logs ()
|
logs () |> shouldEqual [ "Received message for term 0" ]
|
||||||
|> shouldEqual [
|
|
||||||
"Received message for term 0"
|
|
||||||
]
|
|
||||||
|
@@ -2,6 +2,6 @@ namespace Raft.AssemblyInfo
|
|||||||
|
|
||||||
open System.Runtime.CompilerServices
|
open System.Runtime.CompilerServices
|
||||||
|
|
||||||
[<assembly: InternalsVisibleTo("Raft.Test")>]
|
[<assembly : InternalsVisibleTo("Raft.Test")>]
|
||||||
|
|
||||||
do ()
|
do ()
|
||||||
|
@@ -1,9 +1,14 @@
|
|||||||
namespace Raft
|
namespace Raft
|
||||||
|
|
||||||
/// LogIndex is indexed from 1. We use 0 to indicate "before any history has started".
|
/// LogIndex is indexed from 1. We use 0 to indicate "before any history has started".
|
||||||
[<Measure>] type LogIndex
|
[<Measure>]
|
||||||
[<Measure>] type Term
|
type LogIndex
|
||||||
[<Measure>] type ServerId
|
|
||||||
|
[<Measure>]
|
||||||
|
type Term
|
||||||
|
|
||||||
|
[<Measure>]
|
||||||
|
type ServerId
|
||||||
|
|
||||||
/// Server state which need not survive a server crash.
|
/// Server state which need not survive a server crash.
|
||||||
type VolatileState =
|
type VolatileState =
|
||||||
@@ -63,6 +68,12 @@ type AppendEntriesReply =
|
|||||||
Success : bool
|
Success : bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type LogEntry =
|
||||||
|
{
|
||||||
|
Index : int<LogIndex>
|
||||||
|
Term : int<Term>
|
||||||
|
}
|
||||||
|
|
||||||
/// I am the leader. Followers, update your state as follows.
|
/// I am the leader. Followers, update your state as follows.
|
||||||
type AppendEntriesMessage<'a> =
|
type AppendEntriesMessage<'a> =
|
||||||
{
|
{
|
||||||
@@ -71,7 +82,7 @@ type AppendEntriesMessage<'a> =
|
|||||||
/// I am your leader! This is me! (so everyone knows where to send clients to)
|
/// I am your leader! This is me! (so everyone knows where to send clients to)
|
||||||
LeaderId : int<ServerId>
|
LeaderId : int<ServerId>
|
||||||
/// The entry immediately preceding the entry I'm sending you, so you can tell if we've got out of sync.
|
/// The entry immediately preceding the entry I'm sending you, so you can tell if we've got out of sync.
|
||||||
PrevLogEntry : {| Index : int<LogIndex> ; Term : int<Term> |}
|
PrevLogEntry : LogEntry
|
||||||
/// Followers, append this entry to your log. (Or, if None, this is just a heartbeat.)
|
/// Followers, append this entry to your log. (Or, if None, this is just a heartbeat.)
|
||||||
/// It was determined at the given term - recall that we might need to bring a restarted node up to speed
|
/// It was determined at the given term - recall that we might need to bring a restarted node up to speed
|
||||||
/// with what happened during terms that took place while it was down.
|
/// with what happened during terms that took place while it was down.
|
||||||
@@ -83,6 +94,7 @@ type AppendEntriesMessage<'a> =
|
|||||||
type Message<'a> =
|
type Message<'a> =
|
||||||
| AppendEntries of AppendEntriesMessage<'a>
|
| AppendEntries of AppendEntriesMessage<'a>
|
||||||
| RequestVote of RequestVoteMessage
|
| RequestVote of RequestVoteMessage
|
||||||
|
|
||||||
member this.Term =
|
member this.Term =
|
||||||
match this with
|
match this with
|
||||||
| AppendEntries m -> m.LeaderTerm
|
| AppendEntries m -> m.LeaderTerm
|
||||||
@@ -106,12 +118,10 @@ type Server<'a> =
|
|||||||
module Server =
|
module Server =
|
||||||
|
|
||||||
let inline private getLogEntry<'a> (index : int<LogIndex>) (arr : 'a array) : 'a option =
|
let inline private getLogEntry<'a> (index : int<LogIndex>) (arr : 'a array) : 'a option =
|
||||||
arr
|
arr |> Array.tryItem ((index - 1<LogIndex>) / 1<LogIndex>)
|
||||||
|> Array.tryItem ((index - 1<LogIndex>) / 1<LogIndex>)
|
|
||||||
|
|
||||||
let inline private truncateLog<'a> (finalIndex : int<LogIndex>) (arr : 'a array) : 'a array =
|
let inline private truncateLog<'a> (finalIndex : int<LogIndex>) (arr : 'a array) : 'a array =
|
||||||
arr
|
arr |> Array.truncate (finalIndex / 1<LogIndex>)
|
||||||
|> Array.truncate (finalIndex / 1<LogIndex>)
|
|
||||||
|
|
||||||
let inline private replaceLog<'a> (index : int<LogIndex>) (elt : 'a) (arr : 'a array) : 'a array =
|
let inline private replaceLog<'a> (index : int<LogIndex>) (elt : 'a) (arr : 'a array) : 'a array =
|
||||||
let toRet = Array.copy arr
|
let toRet = Array.copy arr
|
||||||
@@ -151,8 +161,9 @@ module Server =
|
|||||||
CurrentTerm = message.Term
|
CurrentTerm = message.Term
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// TODO when persistence is modelled: persist this
|
// TODO when persistence is modelled: persist this
|
||||||
else s
|
else
|
||||||
|
s
|
||||||
|
|
||||||
match message with
|
match message with
|
||||||
| RequestVote message ->
|
| RequestVote message ->
|
||||||
@@ -166,8 +177,7 @@ module Server =
|
|||||||
// Is the candidate advertising a later term than our last-persisted write was made at?
|
// Is the candidate advertising a later term than our last-persisted write was made at?
|
||||||
// (That would mean it's far in the future of us.)
|
// (That would mean it's far in the future of us.)
|
||||||
match Array.tryLast s.PersistentState.Log with
|
match Array.tryLast s.PersistentState.Log with
|
||||||
| Some (_, ourLastTerm) ->
|
| Some (_, ourLastTerm) -> snd message.CandidateLastLogEntry > ourLastTerm
|
||||||
snd message.CandidateLastLogEntry > ourLastTerm
|
|
||||||
| None ->
|
| None ->
|
||||||
// We have persisted no history at all!
|
// We have persisted no history at all!
|
||||||
true
|
true
|
||||||
@@ -177,7 +187,8 @@ module Server =
|
|||||||
match Array.tryLast s.PersistentState.Log with
|
match Array.tryLast s.PersistentState.Log with
|
||||||
| Some (_, ourLastTerm) ->
|
| Some (_, ourLastTerm) ->
|
||||||
snd message.CandidateLastLogEntry = ourLastTerm
|
snd message.CandidateLastLogEntry = ourLastTerm
|
||||||
&& fst message.CandidateLastLogEntry >= (Array.length s.PersistentState.Log) * 1<LogIndex>
|
&& fst message.CandidateLastLogEntry
|
||||||
|
>= (Array.length s.PersistentState.Log) * 1<LogIndex>
|
||||||
| None ->
|
| None ->
|
||||||
// We've persisted no history; the candidate needs to also be at the start of history,
|
// We've persisted no history; the candidate needs to also be at the start of history,
|
||||||
// or else we'd have already considered them in the `messageSupersedesMe` check.
|
// or else we'd have already considered them in the `messageSupersedesMe` check.
|
||||||
@@ -257,7 +268,12 @@ module Server =
|
|||||||
let heartbeat (message : AppendEntriesMessage<'a>) (s : Server<'a>) =
|
let heartbeat (message : AppendEntriesMessage<'a>) (s : Server<'a>) =
|
||||||
// Just a heartbeat; no change to our log is required.
|
// Just a heartbeat; no change to our log is required.
|
||||||
let toReturn =
|
let toReturn =
|
||||||
{ s with VolatileState = { s.VolatileState with CommitIndex = message.LeaderCommitIndex } }
|
{ s with
|
||||||
|
VolatileState =
|
||||||
|
{ s.VolatileState with
|
||||||
|
CommitIndex = message.LeaderCommitIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
Success = true
|
Success = true
|
||||||
@@ -370,7 +386,10 @@ module Server =
|
|||||||
| Candidate ->
|
| Candidate ->
|
||||||
// We've already verified that the message was sent from a leader in the current term, so we have
|
// We've already verified that the message was sent from a leader in the current term, so we have
|
||||||
// lost the election.
|
// lost the election.
|
||||||
let s = { s with Type = Follower }
|
let s =
|
||||||
|
{ s with
|
||||||
|
Type = Follower
|
||||||
|
}
|
||||||
// TODO: why does this assertion hold?
|
// TODO: why does this assertion hold?
|
||||||
assert (logIsConsistent message s)
|
assert (logIsConsistent message s)
|
||||||
acceptRequest s
|
acceptRequest s
|
||||||
@@ -392,12 +411,14 @@ module InMemoryCluster =
|
|||||||
let locker = obj ()
|
let locker = obj ()
|
||||||
|
|
||||||
let messageChannel (serverId : int<ServerId>) (message : Message<'a>) : unit =
|
let messageChannel (serverId : int<ServerId>) (message : Message<'a>) : unit =
|
||||||
lock locker (fun () ->
|
lock
|
||||||
let newServer = Server.processMessage message servers.[serverId / 1<ServerId>]
|
locker
|
||||||
servers.[serverId / 1<ServerId>] <- newServer
|
(fun () ->
|
||||||
)
|
let newServer = Server.processMessage message servers.[serverId / 1<ServerId>]
|
||||||
|
servers.[serverId / 1<ServerId>] <- newServer
|
||||||
|
)
|
||||||
|
|
||||||
for s in 0..servers.Length - 1 do
|
for s in 0 .. servers.Length - 1 do
|
||||||
servers.[s] <- Server.create messageChannel
|
servers.[s] <- Server.create messageChannel
|
||||||
|
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user