2013-04-14 21:37:33 +00:00
package raft
import (
2013-04-16 02:47:59 +00:00
"bufio"
2013-04-14 21:37:33 +00:00
"errors"
"fmt"
"io"
"os"
"reflect"
2013-04-16 04:19:29 +00:00
"sync"
2013-04-14 21:37:33 +00:00
)
//------------------------------------------------------------------------------
//
// Typedefs
//
//------------------------------------------------------------------------------
// A log is a collection of log entries that are persisted to durable storage.
type Log struct {
file * os . File
entries [ ] * LogEntry
commitIndex uint64
commandTypes map [ string ] Command
2013-04-16 04:19:29 +00:00
mutex sync . Mutex
2013-04-14 21:37:33 +00:00
}
//------------------------------------------------------------------------------
//
// Constructor
//
//------------------------------------------------------------------------------
// Creates a new log.
func NewLog ( ) * Log {
return & Log {
commandTypes : make ( map [ string ] Command ) ,
}
}
2013-04-28 04:51:17 +00:00
//------------------------------------------------------------------------------
//
// Accessors
//
//------------------------------------------------------------------------------
//--------------------------------------
// Log Indices
//--------------------------------------
// The current index in the log.
func ( l * Log ) CurrentIndex ( ) uint64 {
l . mutex . Lock ( )
defer l . mutex . Unlock ( )
if len ( l . entries ) == 0 {
return 0
}
return l . entries [ len ( l . entries ) - 1 ] . index
}
// The next index in the log.
func ( l * Log ) NextIndex ( ) uint64 {
return l . CurrentIndex ( ) + 1
}
// The last committed index in the log.
func ( l * Log ) CommitIndex ( ) uint64 {
return l . commitIndex
}
//--------------------------------------
// Log Terms
//--------------------------------------
// The current term in the log.
func ( l * Log ) CurrentTerm ( ) uint64 {
l . mutex . Lock ( )
defer l . mutex . Unlock ( )
if len ( l . entries ) == 0 {
return 0
}
return l . entries [ len ( l . entries ) - 1 ] . term
}
2013-04-14 21:37:33 +00:00
//------------------------------------------------------------------------------
//
// Methods
//
//------------------------------------------------------------------------------
//--------------------------------------
// Commands
//--------------------------------------
// Instantiates a new command by type name. Returns an error if the command type
// has not been registered already.
func ( l * Log ) NewCommand ( name string ) ( Command , error ) {
// Find the registered command.
command := l . commandTypes [ name ]
if command == nil {
return nil , fmt . Errorf ( "raft.Log: Unregistered command type: %s" , name )
}
// Make a copy of the command.
2013-04-16 02:47:59 +00:00
v := reflect . New ( reflect . Indirect ( reflect . ValueOf ( command ) ) . Type ( ) ) . Interface ( )
copy , ok := v . ( Command )
2013-04-14 21:37:33 +00:00
if ! ok {
2013-04-28 04:51:17 +00:00
panic ( fmt . Sprintf ( "raft.Log: Unable to copy command: %s (%v)" , command . CommandName ( ) , reflect . ValueOf ( v ) . Kind ( ) . String ( ) ) )
2013-04-14 21:37:33 +00:00
}
return copy , nil
}
// Adds a command type to the log. The instance passed in will be copied and
// deserialized each time a new log entry is read. This function will panic
// if a command type with the same name already exists.
func ( l * Log ) AddCommandType ( command Command ) {
if command == nil {
panic ( fmt . Sprintf ( "raft.Log: Command type cannot be nil" ) )
2013-04-28 04:51:17 +00:00
} else if l . commandTypes [ command . CommandName ( ) ] != nil {
panic ( fmt . Sprintf ( "raft.Log: Command type already exists: %s" , command . CommandName ( ) ) )
2013-04-14 21:37:33 +00:00
}
2013-04-28 04:51:17 +00:00
l . commandTypes [ command . CommandName ( ) ] = command
2013-04-14 21:37:33 +00:00
}
//--------------------------------------
// State
//--------------------------------------
// Opens the log file and reads existing entries. The log can remain open and
// continue to append entries to the end of the log.
func ( l * Log ) Open ( path string ) error {
2013-04-16 04:19:29 +00:00
l . mutex . Lock ( )
defer l . mutex . Unlock ( )
2013-04-14 21:37:33 +00:00
// Read all the entries from the log if one exists.
2013-04-16 04:02:08 +00:00
var lastIndex int = 0
2013-04-14 21:37:33 +00:00
if _ , err := os . Stat ( path ) ; ! os . IsNotExist ( err ) {
// Open the log file.
file , err := os . Open ( path )
if err != nil {
return err
}
defer file . Close ( )
2013-04-16 02:47:59 +00:00
reader := bufio . NewReader ( file )
2013-04-14 21:37:33 +00:00
// Read the file and decode entries.
2013-04-16 02:47:59 +00:00
for {
if _ , err := reader . Peek ( 1 ) ; err == io . EOF {
break
}
2013-04-14 21:37:33 +00:00
// Instantiate log entry and decode into it.
entry := NewLogEntry ( l , 0 , 0 , nil )
2013-04-16 04:02:08 +00:00
n , err := entry . Decode ( reader )
2013-04-16 02:47:59 +00:00
if err != nil {
2013-04-16 04:02:08 +00:00
warn ( "raft.Log: %v" , err )
warn ( "raft.Log: Recovering (%d)" , lastIndex )
file . Close ( )
if err = os . Truncate ( path , int64 ( lastIndex ) ) ; err != nil {
return fmt . Errorf ( "raft.Log: Unable to recover: %v" , err )
}
break
2013-04-14 21:37:33 +00:00
}
2013-04-16 04:19:29 +00:00
l . commitIndex = entry . index
2013-04-16 04:02:08 +00:00
lastIndex += n
2013-04-14 21:37:33 +00:00
// Append entry.
l . entries = append ( l . entries , entry )
}
2013-04-16 02:47:59 +00:00
file . Close ( )
2013-04-14 21:37:33 +00:00
}
// Open the file for appending.
var err error
l . file , err = os . OpenFile ( path , os . O_APPEND | os . O_CREATE | os . O_WRONLY , 0600 )
if err != nil {
return err
}
return nil
}
// Closes the log file.
func ( l * Log ) Close ( ) {
2013-04-16 04:19:29 +00:00
l . mutex . Lock ( )
defer l . mutex . Unlock ( )
2013-04-14 21:37:33 +00:00
if l . file != nil {
l . file . Close ( )
l . file = nil
}
l . entries = make ( [ ] * LogEntry , 0 )
}
//--------------------------------------
2013-04-28 04:51:17 +00:00
// Entries
2013-04-14 21:37:33 +00:00
//--------------------------------------
2013-04-28 04:51:17 +00:00
// Creates a log entry associated with this log.
func ( l * Log ) CreateEntry ( term uint64 , command Command ) ( * LogEntry ) {
return NewLogEntry ( l , l . NextIndex ( ) , term , command )
}
2013-04-16 04:19:29 +00:00
// Updates the commit index and writes entries after that index to the stable
// storage.
func ( l * Log ) SetCommitIndex ( index uint64 ) error {
l . mutex . Lock ( )
defer l . mutex . Unlock ( )
// Do not allow previous indices to be committed again.
if index < l . commitIndex {
return fmt . Errorf ( "raft.Log: Commit index (%d) ahead of requested commit index (%d)" , l . commitIndex , index )
}
// Find all entries whose index is between the previous index and the current index.
for _ , entry := range l . entries {
if entry . index > l . commitIndex && entry . index <= index {
// Write to storage.
if err := entry . Encode ( l . file ) ; err != nil {
return err
}
// Update commit index.
l . commitIndex = entry . index
}
}
return nil
}
//--------------------------------------
// Append
//--------------------------------------
2013-04-14 21:37:33 +00:00
// Writes a single log entry to the end of the log.
func ( l * Log ) Append ( entry * LogEntry ) error {
2013-04-16 04:19:29 +00:00
l . mutex . Lock ( )
defer l . mutex . Unlock ( )
2013-04-14 21:37:33 +00:00
if l . file == nil {
return errors . New ( "raft.Log: Log is not open" )
}
// Make sure the term and index are greater than the previous.
if len ( l . entries ) > 0 {
lastEntry := l . entries [ len ( l . entries ) - 1 ]
if entry . term < lastEntry . term {
return fmt . Errorf ( "raft.Log: Cannot append entry with earlier term (%x:%x < %x:%x)" , entry . term , entry . index , lastEntry . term , lastEntry . index )
} else if entry . index == lastEntry . index && entry . index <= lastEntry . index {
return fmt . Errorf ( "raft.Log: Cannot append entry with earlier index in the same term (%x:%x < %x:%x)" , entry . term , entry . index , lastEntry . term , lastEntry . index )
}
}
// Append to entries list if stored on disk.
l . entries = append ( l . entries , entry )
return nil
}