tendermint/consensus/wal.go

113 lines
2.8 KiB
Go
Raw Normal View History

2016-01-18 11:10:05 -08:00
package consensus
import (
"time"
2016-10-28 15:01:14 -07:00
auto "github.com/tendermint/go-autofile"
2016-01-18 11:10:05 -08:00
. "github.com/tendermint/go-common"
"github.com/tendermint/go-wire"
"github.com/tendermint/tendermint/types"
)
//--------------------------------------------------------
// types and functions for savings consensus messages
2016-10-28 15:01:14 -07:00
type TimedWALMessage struct {
Time time.Time `json:"time"`
Msg WALMessage `json:"msg"`
2016-01-18 11:10:05 -08:00
}
2016-10-28 15:01:14 -07:00
type WALMessage interface{}
2016-01-18 11:10:05 -08:00
var _ = wire.RegisterInterface(
2016-10-28 15:01:14 -07:00
struct{ WALMessage }{},
wire.ConcreteType{types.EventDataRoundState{}, 0x01},
2016-01-18 11:10:05 -08:00
wire.ConcreteType{msgInfo{}, 0x02},
wire.ConcreteType{timeoutInfo{}, 0x03},
)
//--------------------------------------------------------
// Simple write-ahead logger
// Write ahead logger writes msgs to disk before they are processed.
// Can be used for crash-recovery and deterministic replay
2016-01-18 12:57:57 -08:00
// TODO: currently the wal is overwritten during replay catchup
// give it a mode so it's either reading or appending - must read to end to start appending again
2016-01-18 11:10:05 -08:00
type WAL struct {
2016-10-28 15:01:14 -07:00
BaseService
2016-10-28 15:01:14 -07:00
group *auto.Group
light bool // ignore block parts
2016-01-18 11:10:05 -08:00
}
2016-10-30 03:55:27 -07:00
func NewWAL(walDir string, light bool) (*WAL, error) {
2016-11-21 19:16:19 -08:00
group, err := auto.OpenGroup(walDir + "/wal")
2016-01-18 11:10:05 -08:00
if err != nil {
return nil, err
}
2016-10-28 15:01:14 -07:00
wal := &WAL{
group: group,
light: light,
}
wal.BaseService = *NewBaseService(log, "WAL", wal)
2016-11-15 22:15:39 -08:00
_, err = wal.Start()
return wal, err
}
func (wal *WAL) OnStart() error {
wal.BaseService.OnStart()
size, err := wal.group.Head.Size()
if err != nil {
return err
} else if size == 0 {
wal.writeHeight(1)
}
2016-11-21 19:16:19 -08:00
_, err = wal.group.Start()
return err
2016-01-18 11:10:05 -08:00
}
2016-10-28 15:01:14 -07:00
func (wal *WAL) OnStop() {
wal.BaseService.OnStop()
2016-11-21 19:16:19 -08:00
wal.group.Stop()
2016-08-17 20:08:43 -07:00
}
2016-01-18 11:10:05 -08:00
// called in newStep and for each pass in receiveRoutine
2016-10-28 15:01:14 -07:00
func (wal *WAL) Save(wmsg WALMessage) {
2016-10-28 11:58:09 -07:00
if wal == nil {
return
}
if wal.light {
// in light mode we only write new steps, timeouts, and our own votes (no proposals, block parts)
2016-10-28 15:01:14 -07:00
if mi, ok := wmsg.(msgInfo); ok {
2016-10-28 11:58:09 -07:00
if mi.PeerKey != "" {
return
}
}
2016-10-28 11:58:09 -07:00
}
2016-10-28 15:01:14 -07:00
// Write #HEIGHT: XYZ if new height
if edrs, ok := wmsg.(types.EventDataRoundState); ok {
if edrs.Step == RoundStepNewHeight.String() {
2016-11-15 22:15:39 -08:00
wal.writeHeight(edrs.Height)
2016-10-28 15:01:14 -07:00
}
2016-01-18 11:10:05 -08:00
}
2016-10-28 15:01:14 -07:00
// Write the wal message
var wmsgBytes = wire.JSONBytes(TimedWALMessage{time.Now(), wmsg})
err := wal.group.WriteLine(string(wmsgBytes))
2016-01-18 11:10:05 -08:00
if err != nil {
2016-10-28 15:01:14 -07:00
PanicQ(Fmt("Error writing msg to consensus wal. Error: %v \n\nMessage: %v", err, wmsg))
2016-01-18 11:10:05 -08:00
}
// TODO: only flush when necessary
if err := wal.group.Flush(); err != nil {
PanicQ(Fmt("Error flushing consensus wal buf to file. Error: %v \n", err))
}
2016-01-18 11:10:05 -08:00
}
2016-11-15 22:15:39 -08:00
func (wal *WAL) writeHeight(height int) {
wal.group.WriteLine(Fmt("#HEIGHT: %v", height))
2016-12-22 12:01:02 -08:00
// TODO: only flush when necessary
if err := wal.group.Flush(); err != nil {
PanicQ(Fmt("Error flushing consensus wal buf to file. Error: %v \n", err))
}
2016-11-15 22:15:39 -08:00
}