influxdb/log_entry.go

194 lines
4.6 KiB
Go
Raw Normal View History

2013-04-14 21:37:33 +00:00
package raft
import (
"bufio"
"bytes"
2013-04-28 21:23:21 +00:00
"encoding/json"
2013-04-16 02:47:59 +00:00
"errors"
2013-04-14 21:37:33 +00:00
"fmt"
2013-04-28 21:23:21 +00:00
"hash/crc32"
2013-04-14 21:37:33 +00:00
"io"
)
//------------------------------------------------------------------------------
//
// Typedefs
//
//------------------------------------------------------------------------------
// A log entry stores a single item in the log.
type LogEntry struct {
log *Log
2013-05-10 14:47:24 +00:00
Index uint64 `json:"index"`
Term uint64 `json:"term"`
2013-05-08 20:22:08 +00:00
Command Command `json:"command"`
2013-04-14 21:37:33 +00:00
}
2013-05-10 03:50:57 +00:00
// A temporary interface used for unmarshaling log entries.
type logEntryRawMessage struct {
2013-05-10 14:47:24 +00:00
Index uint64 `json:"index"`
Term uint64 `json:"term"`
Name string `json:"name"`
2013-05-10 03:50:57 +00:00
Command json.RawMessage `json:"command"`
}
2013-04-14 21:37:33 +00:00
//------------------------------------------------------------------------------
//
// Constructor
//
//------------------------------------------------------------------------------
// Creates a new log entry associated with a log.
2013-04-16 02:47:59 +00:00
func NewLogEntry(log *Log, index uint64, term uint64, command Command) *LogEntry {
2013-04-14 21:37:33 +00:00
return &LogEntry{
2013-04-28 21:23:21 +00:00
log: log,
2013-05-08 20:22:08 +00:00
Index: index,
Term: term,
Command: command,
2013-04-14 21:37:33 +00:00
}
}
//------------------------------------------------------------------------------
//
// Methods
//
//------------------------------------------------------------------------------
//--------------------------------------
// Encoding
//--------------------------------------
// Encodes the log entry to a buffer.
func (e *LogEntry) Encode(w io.Writer) error {
2013-04-16 02:47:59 +00:00
if w == nil {
return errors.New("raft.LogEntry: Writer required to encode")
}
2013-05-08 20:22:08 +00:00
encodedCommand, err := json.Marshal(e.Command)
2013-04-14 21:37:33 +00:00
if err != nil {
return err
}
// Write log line to temporary buffer.
var b bytes.Buffer
2013-05-08 20:22:08 +00:00
if _, err = fmt.Fprintf(&b, "%016x %016x %s %s\n", e.Index, e.Term, e.Command.CommandName(), encodedCommand); err != nil {
2013-04-14 21:37:33 +00:00
return err
}
// Generate checksum.
checksum := crc32.ChecksumIEEE(b.Bytes())
// Write log entry with checksum.
2013-04-16 02:47:59 +00:00
_, err = fmt.Fprintf(w, "%08x %s", checksum, b.String())
2013-04-14 21:37:33 +00:00
return err
}
2013-04-16 04:02:08 +00:00
// Decodes the log entry from a buffer. Returns the number of bytes read.
func (e *LogEntry) Decode(r io.Reader) (pos int, err error) {
pos = 0
2013-04-28 21:23:21 +00:00
2013-04-16 02:47:59 +00:00
if r == nil {
2013-04-16 04:02:08 +00:00
err = errors.New("raft.LogEntry: Reader required to decode")
return
2013-04-16 02:47:59 +00:00
}
2013-04-28 21:23:21 +00:00
2013-04-14 21:37:33 +00:00
// Read the expected checksum first.
var checksum uint32
2013-04-16 04:02:08 +00:00
if _, err = fmt.Fscanf(r, "%08x", &checksum); err != nil {
err = fmt.Errorf("raft.LogEntry: Unable to read checksum: %v", err)
return
2013-04-16 02:47:59 +00:00
}
2013-04-16 04:02:08 +00:00
pos += 8
2013-04-14 21:37:33 +00:00
// Read the rest of the line.
2013-04-16 02:47:59 +00:00
bufr := bufio.NewReader(r)
if c, _ := bufr.ReadByte(); c != ' ' {
2013-04-16 04:02:08 +00:00
err = fmt.Errorf("raft.LogEntry: Expected space, received %02x", c)
return
2013-04-16 02:47:59 +00:00
}
2013-04-16 04:02:08 +00:00
pos += 1
2013-04-16 02:47:59 +00:00
line, err := bufr.ReadString('\n')
2013-04-16 04:02:08 +00:00
pos += len(line)
2013-04-14 21:37:33 +00:00
if err == io.EOF {
2013-04-16 04:02:08 +00:00
err = fmt.Errorf("raft.LogEntry: Unexpected EOF")
return
2013-04-14 21:37:33 +00:00
} else if err != nil {
2013-04-16 04:02:08 +00:00
err = fmt.Errorf("raft.LogEntry: Unable to read line: %v", err)
return
2013-04-14 21:37:33 +00:00
}
b := bytes.NewBufferString(line)
// Verify checksum.
bchecksum := crc32.ChecksumIEEE(b.Bytes())
if checksum != bchecksum {
2013-04-16 04:02:08 +00:00
err = fmt.Errorf("raft.LogEntry: Invalid checksum: Expected %08x, calculated %08x", checksum, bchecksum)
return
2013-04-14 21:37:33 +00:00
}
// Read term, index and command name.
var commandName string
2013-05-08 20:22:08 +00:00
if _, err = fmt.Fscanf(b, "%016x %016x %s ", &e.Index, &e.Term, &commandName); err != nil {
2013-04-16 04:02:08 +00:00
err = fmt.Errorf("raft.LogEntry: Unable to scan: %v", err)
return
2013-04-14 21:37:33 +00:00
}
// Instantiate command by name.
2013-06-03 02:43:40 +00:00
command, err := NewCommand(commandName)
2013-04-14 21:37:33 +00:00
if err != nil {
2013-04-16 04:02:08 +00:00
err = fmt.Errorf("raft.LogEntry: Unable to instantiate command (%s): %v", commandName, err)
return
2013-04-14 21:37:33 +00:00
}
// Deserialize command.
if err = json.NewDecoder(b).Decode(&command); err != nil {
2013-04-16 04:02:08 +00:00
err = fmt.Errorf("raft.LogEntry: Unable to decode: %v", err)
return
2013-04-14 21:37:33 +00:00
}
2013-05-08 20:22:08 +00:00
e.Command = command
2013-04-28 21:23:21 +00:00
2013-04-16 02:47:59 +00:00
// Make sure there's only an EOF remaining.
2013-04-16 04:02:08 +00:00
c, err := b.ReadByte()
if err != io.EOF {
err = fmt.Errorf("raft.LogEntry: Expected EOL, received %02x", c)
return
2013-04-14 21:37:33 +00:00
}
2013-04-16 04:02:08 +00:00
err = nil
return
2013-04-14 21:37:33 +00:00
}
2013-05-10 03:50:57 +00:00
//--------------------------------------
// Encoding
//--------------------------------------
// Encodes a log entry into JSON.
func (e *LogEntry) MarshalJSON() ([]byte, error) {
obj := map[string]interface{}{
2013-05-10 14:47:24 +00:00
"index": e.Index,
"term": e.Term,
2013-05-10 03:50:57 +00:00
}
if e.Command != nil {
obj["name"] = e.Command.CommandName()
obj["command"] = e.Command
}
return json.Marshal(obj)
}
// Decodes a log entry from a JSON byte array.
2013-05-10 14:47:24 +00:00
func (e *LogEntry) UnmarshalJSON(data []byte) error {
2013-05-10 03:50:57 +00:00
// Extract base log entry info.
obj := &logEntryRawMessage{}
json.Unmarshal(data, obj)
e.Index, e.Term = obj.Index, obj.Term
2013-05-10 14:47:24 +00:00
2013-05-10 03:50:57 +00:00
// Create a command based on the name.
var err error
2013-06-03 02:43:40 +00:00
if e.Command, err = NewCommand(obj.Name); err != nil {
2013-05-10 03:50:57 +00:00
return err
}
json.Unmarshal(obj.Command, e.Command)
2013-05-10 14:47:24 +00:00
2013-05-10 03:50:57 +00:00
return nil
}