influxdb/log.go

263 lines
6.6 KiB
Go
Raw Normal View History

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
}