namespace Raft open System.Threading type IPersistentState<'a> = abstract CurrentTerm : int /// If I know about an election in my CurrentTerm, who did I vote for during that election? abstract VotedFor : int option abstract AppendToLog : LogEntry<'a> -> int -> unit /// Truncate away the most recent entries of the log. /// If `GetLogEntry x` would succeed, and then we call `TruncateLog x`, /// then `GetLogEntry x` will still succeed (but `GetLogEntry (x + 1)` will not). abstract TruncateLog : int -> unit abstract GetLogEntry : int -> (LogEntry<'a> * int) option abstract CurrentLogIndex : int abstract GetLastLogEntry : unit -> (LogEntry<'a> * LogEntryMetadata) option abstract AdvanceToTerm : int -> unit abstract IncrementTerm : unit -> unit abstract Vote : int -> unit /// Server state which must survive a server crash. [] type InMemoryPersistentState<'a> () = let mutable currentTerm = 0 let mutable votedFor : int option = None let log = ResizeArray * int> () member this.GetLog () = log |> List.ofSeq interface IPersistentState<'a> with member this.CurrentTerm = currentTerm * 1 member this.IncrementTerm () = #if FABLE_COMPILER currentTerm <- currentTerm + 1 #else Interlocked.Increment ¤tTerm |> ignore #endif member this.VotedFor = votedFor member this.Vote id = votedFor <- Some id member this.AdvanceToTerm term = currentTerm <- term / 1 votedFor <- None member this.AppendToLog (entry : LogEntry<'a>) term = log.Add (entry, term) member this.TruncateLog position = let position = position / 1 if position < log.Count then let position = if position < 0 then 0 else position log.RemoveRange (position, log.Count - position) member this.GetLastLogEntry () : (LogEntry<'a> * LogEntryMetadata) option = if log.Count = 0 then None else let stored, term = log.[log.Count - 1] Some ( stored, { Index = log.Count * 1 Term = term } ) member this.GetLogEntry position = let position = position / 1 if log.Count < position then None elif position <= 0 then None else Some log.[position - 1] member this.CurrentLogIndex = log.Count * 1