mirror of
https://github.com/etcd-io/etcd.git
synced 2024-09-27 06:25:44 +00:00
Merge pull request #1236 from unihorn/153
wal: record node id at the head of WAL file
This commit is contained in:
commit
f8b338d423
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
It has these top-level messages:
|
It has these top-level messages:
|
||||||
Request
|
Request
|
||||||
|
Info
|
||||||
*/
|
*/
|
||||||
package etcdserverpb
|
package etcdserverpb
|
||||||
|
|
||||||
@ -51,6 +52,15 @@ func (m *Request) Reset() { *m = Request{} }
|
|||||||
func (m *Request) String() string { return proto.CompactTextString(m) }
|
func (m *Request) String() string { return proto.CompactTextString(m) }
|
||||||
func (*Request) ProtoMessage() {}
|
func (*Request) ProtoMessage() {}
|
||||||
|
|
||||||
|
type Info struct {
|
||||||
|
ID uint64 `protobuf:"varint,1,req" json:"ID"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Info) Reset() { *m = Info{} }
|
||||||
|
func (m *Info) String() string { return proto.CompactTextString(m) }
|
||||||
|
func (*Info) ProtoMessage() {}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
}
|
}
|
||||||
func (m *Request) Unmarshal(data []byte) error {
|
func (m *Request) Unmarshal(data []byte) error {
|
||||||
@ -378,6 +388,63 @@ func (m *Request) Unmarshal(data []byte) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func (m *Info) Unmarshal(data []byte) error {
|
||||||
|
l := len(data)
|
||||||
|
index := 0
|
||||||
|
for index < l {
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if index >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[index]
|
||||||
|
index++
|
||||||
|
wire |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNum := int32(wire >> 3)
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
switch fieldNum {
|
||||||
|
case 1:
|
||||||
|
if wireType != 0 {
|
||||||
|
return code_google_com_p_gogoprotobuf_proto.ErrWrongType
|
||||||
|
}
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if index >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[index]
|
||||||
|
index++
|
||||||
|
m.ID |= (uint64(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
var sizeOfWire int
|
||||||
|
for {
|
||||||
|
sizeOfWire++
|
||||||
|
wire >>= 7
|
||||||
|
if wire == 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index -= sizeOfWire
|
||||||
|
skippy, err := code_google_com_p_gogoprotobuf_proto.Skip(data[index:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if (index + skippy) > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.XXX_unrecognized = append(m.XXX_unrecognized, data[index:index+skippy]...)
|
||||||
|
index += skippy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
func (m *Request) Size() (n int) {
|
func (m *Request) Size() (n int) {
|
||||||
var l int
|
var l int
|
||||||
_ = l
|
_ = l
|
||||||
@ -408,6 +475,15 @@ func (m *Request) Size() (n int) {
|
|||||||
}
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
func (m *Info) Size() (n int) {
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
n += 1 + sovEtcdserver(uint64(m.ID))
|
||||||
|
if m.XXX_unrecognized != nil {
|
||||||
|
n += len(m.XXX_unrecognized)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
func sovEtcdserver(x uint64) (n int) {
|
func sovEtcdserver(x uint64) (n int) {
|
||||||
for {
|
for {
|
||||||
@ -533,6 +609,29 @@ func (m *Request) MarshalTo(data []byte) (n int, err error) {
|
|||||||
}
|
}
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
func (m *Info) Marshal() (data []byte, err error) {
|
||||||
|
size := m.Size()
|
||||||
|
data = make([]byte, size)
|
||||||
|
n, err := m.MarshalTo(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return data[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Info) MarshalTo(data []byte) (n int, err error) {
|
||||||
|
var i int
|
||||||
|
_ = i
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
data[i] = 0x8
|
||||||
|
i++
|
||||||
|
i = encodeVarintEtcdserver(data, i, uint64(m.ID))
|
||||||
|
if m.XXX_unrecognized != nil {
|
||||||
|
i += copy(data[i:], m.XXX_unrecognized)
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
func encodeFixed64Etcdserver(data []byte, offset int, v uint64) int {
|
func encodeFixed64Etcdserver(data []byte, offset int, v uint64) int {
|
||||||
data[offset] = uint8(v)
|
data[offset] = uint8(v)
|
||||||
data[offset+1] = uint8(v >> 8)
|
data[offset+1] = uint8(v >> 8)
|
||||||
|
@ -25,3 +25,7 @@ message Request {
|
|||||||
required int64 Time = 15 [(gogoproto.nullable) = false];
|
required int64 Time = 15 [(gogoproto.nullable) = false];
|
||||||
required bool Stream = 16 [(gogoproto.nullable) = false];
|
required bool Stream = 16 [(gogoproto.nullable) = false];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message Info {
|
||||||
|
required uint64 ID = 1 [(gogoproto.nullable) = false];
|
||||||
|
}
|
||||||
|
@ -116,7 +116,12 @@ func NewServer(cfg *ServerConfig) *EtcdServer {
|
|||||||
} else if (cfg.ClusterState) != ClusterStateValueNew {
|
} else if (cfg.ClusterState) != ClusterStateValueNew {
|
||||||
log.Fatalf("etcd: initial cluster state unset and no wal or discovery URL found")
|
log.Fatalf("etcd: initial cluster state unset and no wal or discovery URL found")
|
||||||
}
|
}
|
||||||
if w, err = wal.Create(waldir); err != nil {
|
i := pb.Info{ID: m.ID}
|
||||||
|
b, err := i.Marshal()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if w, err = wal.Create(waldir, b); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
// TODO: add context for PeerURLs
|
// TODO: add context for PeerURLs
|
||||||
@ -140,13 +145,17 @@ func NewServer(cfg *ServerConfig) *EtcdServer {
|
|||||||
if w, err = wal.OpenAtIndex(waldir, index); err != nil {
|
if w, err = wal.OpenAtIndex(waldir, index); err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
wid, st, ents, err := w.ReadAll()
|
md, st, ents, err := w.ReadAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
var info pb.Info
|
||||||
|
if err := info.Unmarshal(md); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
// TODO(xiangli): save/recovery nodeID?
|
// TODO(xiangli): save/recovery nodeID?
|
||||||
if wid != 0 {
|
if info.ID != m.ID {
|
||||||
log.Fatalf("unexpected nodeid %d: nodeid should always be zero until we save nodeid into wal", wid)
|
log.Fatalf("unexpected nodeid %x, want %x: nodeid should always be the same until we support name/peerURLs update or dynamic configuration", info.ID, m.ID)
|
||||||
}
|
}
|
||||||
n = raft.RestartNode(m.ID, cfg.Cluster.IDs(), 10, 1, snapshot, st, ents)
|
n = raft.RestartNode(m.ID, cfg.Cluster.IDs(), 10, 1, snapshot, st, ents)
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
raft.proto
|
raft.proto
|
||||||
|
|
||||||
It has these top-level messages:
|
It has these top-level messages:
|
||||||
Info
|
|
||||||
Entry
|
Entry
|
||||||
Snapshot
|
Snapshot
|
||||||
Message
|
Message
|
||||||
@ -98,15 +97,6 @@ func (x *ConfChangeType) UnmarshalJSON(data []byte) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Info struct {
|
|
||||||
ID uint64 `protobuf:"varint,1,req" json:"ID"`
|
|
||||||
XXX_unrecognized []byte `json:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Info) Reset() { *m = Info{} }
|
|
||||||
func (m *Info) String() string { return proto.CompactTextString(m) }
|
|
||||||
func (*Info) ProtoMessage() {}
|
|
||||||
|
|
||||||
type Entry struct {
|
type Entry struct {
|
||||||
Type EntryType `protobuf:"varint,1,req,enum=raftpb.EntryType" json:"Type"`
|
Type EntryType `protobuf:"varint,1,req,enum=raftpb.EntryType" json:"Type"`
|
||||||
Term uint64 `protobuf:"varint,2,req" json:"Term"`
|
Term uint64 `protobuf:"varint,2,req" json:"Term"`
|
||||||
@ -177,63 +167,6 @@ func init() {
|
|||||||
proto.RegisterEnum("raftpb.EntryType", EntryType_name, EntryType_value)
|
proto.RegisterEnum("raftpb.EntryType", EntryType_name, EntryType_value)
|
||||||
proto.RegisterEnum("raftpb.ConfChangeType", ConfChangeType_name, ConfChangeType_value)
|
proto.RegisterEnum("raftpb.ConfChangeType", ConfChangeType_name, ConfChangeType_value)
|
||||||
}
|
}
|
||||||
func (m *Info) Unmarshal(data []byte) error {
|
|
||||||
l := len(data)
|
|
||||||
index := 0
|
|
||||||
for index < l {
|
|
||||||
var wire uint64
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if index >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[index]
|
|
||||||
index++
|
|
||||||
wire |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fieldNum := int32(wire >> 3)
|
|
||||||
wireType := int(wire & 0x7)
|
|
||||||
switch fieldNum {
|
|
||||||
case 1:
|
|
||||||
if wireType != 0 {
|
|
||||||
return code_google_com_p_gogoprotobuf_proto.ErrWrongType
|
|
||||||
}
|
|
||||||
for shift := uint(0); ; shift += 7 {
|
|
||||||
if index >= l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
b := data[index]
|
|
||||||
index++
|
|
||||||
m.ID |= (uint64(b) & 0x7F) << shift
|
|
||||||
if b < 0x80 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
var sizeOfWire int
|
|
||||||
for {
|
|
||||||
sizeOfWire++
|
|
||||||
wire >>= 7
|
|
||||||
if wire == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
index -= sizeOfWire
|
|
||||||
skippy, err := code_google_com_p_gogoprotobuf_proto.Skip(data[index:])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if (index + skippy) > l {
|
|
||||||
return io.ErrUnexpectedEOF
|
|
||||||
}
|
|
||||||
m.XXX_unrecognized = append(m.XXX_unrecognized, data[index:index+skippy]...)
|
|
||||||
index += skippy
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
func (m *Entry) Unmarshal(data []byte) error {
|
func (m *Entry) Unmarshal(data []byte) error {
|
||||||
l := len(data)
|
l := len(data)
|
||||||
index := 0
|
index := 0
|
||||||
@ -878,15 +811,6 @@ func (m *ConfChange) Unmarshal(data []byte) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
func (m *Info) Size() (n int) {
|
|
||||||
var l int
|
|
||||||
_ = l
|
|
||||||
n += 1 + sovRaft(uint64(m.ID))
|
|
||||||
if m.XXX_unrecognized != nil {
|
|
||||||
n += len(m.XXX_unrecognized)
|
|
||||||
}
|
|
||||||
return n
|
|
||||||
}
|
|
||||||
func (m *Entry) Size() (n int) {
|
func (m *Entry) Size() (n int) {
|
||||||
var l int
|
var l int
|
||||||
_ = l
|
_ = l
|
||||||
@ -984,29 +908,6 @@ func sovRaft(x uint64) (n int) {
|
|||||||
func sozRaft(x uint64) (n int) {
|
func sozRaft(x uint64) (n int) {
|
||||||
return sovRaft(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
return sovRaft(uint64((x << 1) ^ uint64((int64(x) >> 63))))
|
||||||
}
|
}
|
||||||
func (m *Info) Marshal() (data []byte, err error) {
|
|
||||||
size := m.Size()
|
|
||||||
data = make([]byte, size)
|
|
||||||
n, err := m.MarshalTo(data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return data[:n], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Info) MarshalTo(data []byte) (n int, err error) {
|
|
||||||
var i int
|
|
||||||
_ = i
|
|
||||||
var l int
|
|
||||||
_ = l
|
|
||||||
data[i] = 0x8
|
|
||||||
i++
|
|
||||||
i = encodeVarintRaft(data, i, uint64(m.ID))
|
|
||||||
if m.XXX_unrecognized != nil {
|
|
||||||
i += copy(data[i:], m.XXX_unrecognized)
|
|
||||||
}
|
|
||||||
return i, nil
|
|
||||||
}
|
|
||||||
func (m *Entry) Marshal() (data []byte, err error) {
|
func (m *Entry) Marshal() (data []byte, err error) {
|
||||||
size := m.Size()
|
size := m.Size()
|
||||||
data = make([]byte, size)
|
data = make([]byte, size)
|
||||||
|
@ -8,10 +8,6 @@ option (gogoproto.unmarshaler_all) = true;
|
|||||||
option (gogoproto.goproto_getters_all) = false;
|
option (gogoproto.goproto_getters_all) = false;
|
||||||
option (gogoproto.goproto_enum_prefix_all) = false;
|
option (gogoproto.goproto_enum_prefix_all) = false;
|
||||||
|
|
||||||
message Info {
|
|
||||||
required uint64 ID = 1 [(gogoproto.nullable) = false];
|
|
||||||
}
|
|
||||||
|
|
||||||
enum EntryType {
|
enum EntryType {
|
||||||
EntryNormal = 0;
|
EntryNormal = 0;
|
||||||
EntryConfChange = 1;
|
EntryConfChange = 1;
|
||||||
|
@ -58,19 +58,11 @@ func (d *decoder) close() error {
|
|||||||
return d.c.Close()
|
return d.c.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func mustUnmarshalInfo(d []byte) raftpb.Info {
|
|
||||||
var i raftpb.Info
|
|
||||||
if err := i.Unmarshal(d); err != nil {
|
|
||||||
// crc matched, but we cannot unmarshal the struct?!
|
|
||||||
// we must be the next winner of the $1B lottery.
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func mustUnmarshalEntry(d []byte) raftpb.Entry {
|
func mustUnmarshalEntry(d []byte) raftpb.Entry {
|
||||||
var e raftpb.Entry
|
var e raftpb.Entry
|
||||||
if err := e.Unmarshal(d); err != nil {
|
if err := e.Unmarshal(d); err != nil {
|
||||||
|
// crc matched, but we cannot unmarshal the struct?!
|
||||||
|
// we must be the next winner of the $1B lottery.
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
return e
|
return e
|
||||||
|
@ -27,6 +27,11 @@ import (
|
|||||||
"github.com/coreos/etcd/wal/walpb"
|
"github.com/coreos/etcd/wal/walpb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
infoData = []byte("\b\xef\xfd\x02")
|
||||||
|
infoRecord = append([]byte("\x0e\x00\x00\x00\x00\x00\x00\x00\b\x01\x10\x99\xb5\xe4\xd0\x03\x1a\x04"), infoData...)
|
||||||
|
)
|
||||||
|
|
||||||
func TestReadRecord(t *testing.T) {
|
func TestReadRecord(t *testing.T) {
|
||||||
badInfoRecord := make([]byte, len(infoRecord))
|
badInfoRecord := make([]byte, len(infoRecord))
|
||||||
copy(badInfoRecord, infoRecord)
|
copy(badInfoRecord, infoRecord)
|
||||||
|
59
wal/wal.go
59
wal/wal.go
@ -23,6 +23,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"reflect"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
"github.com/coreos/etcd/raft"
|
"github.com/coreos/etcd/raft"
|
||||||
@ -31,7 +32,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
infoType int64 = iota + 1
|
metadataType int64 = iota + 1
|
||||||
entryType
|
entryType
|
||||||
stateType
|
stateType
|
||||||
crcType
|
crcType
|
||||||
@ -41,11 +42,11 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrIDMismatch = errors.New("wal: unmatch id")
|
ErrMetadataConflict = errors.New("wal: conflicting metadata found")
|
||||||
ErrFileNotFound = errors.New("wal: file not found")
|
ErrFileNotFound = errors.New("wal: file not found")
|
||||||
ErrIndexNotFound = errors.New("wal: index not found in file")
|
ErrIndexNotFound = errors.New("wal: index not found in file")
|
||||||
ErrCRCMismatch = errors.New("wal: crc mismatch")
|
ErrCRCMismatch = errors.New("wal: crc mismatch")
|
||||||
crcTable = crc32.MakeTable(crc32.Castagnoli)
|
crcTable = crc32.MakeTable(crc32.Castagnoli)
|
||||||
)
|
)
|
||||||
|
|
||||||
// WAL is a logical repersentation of the stable storage.
|
// WAL is a logical repersentation of the stable storage.
|
||||||
@ -55,6 +56,7 @@ var (
|
|||||||
// The WAL will be ready for appending after reading out all the previous records.
|
// The WAL will be ready for appending after reading out all the previous records.
|
||||||
type WAL struct {
|
type WAL struct {
|
||||||
dir string // the living directory of the underlay files
|
dir string // the living directory of the underlay files
|
||||||
|
md []byte // metadata recorded at the head of each WAL
|
||||||
|
|
||||||
ri uint64 // index of entry to start reading
|
ri uint64 // index of entry to start reading
|
||||||
decoder *decoder // decoder to decode records
|
decoder *decoder // decoder to decode records
|
||||||
@ -65,8 +67,9 @@ type WAL struct {
|
|||||||
encoder *encoder // encoder to encode records
|
encoder *encoder // encoder to encode records
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create creates a WAL ready for appending records.
|
// Create creates a WAL ready for appending records. The given metadata is
|
||||||
func Create(dirpath string) (*WAL, error) {
|
// recorded at the head of each WAL file, and can be retrieved with ReadAll.
|
||||||
|
func Create(dirpath string, metadata []byte) (*WAL, error) {
|
||||||
if Exist(dirpath) {
|
if Exist(dirpath) {
|
||||||
return nil, os.ErrExist
|
return nil, os.ErrExist
|
||||||
}
|
}
|
||||||
@ -82,6 +85,7 @@ func Create(dirpath string) (*WAL, error) {
|
|||||||
}
|
}
|
||||||
w := &WAL{
|
w := &WAL{
|
||||||
dir: dirpath,
|
dir: dirpath,
|
||||||
|
md: metadata,
|
||||||
seq: 0,
|
seq: 0,
|
||||||
f: f,
|
f: f,
|
||||||
encoder: newEncoder(f, 0),
|
encoder: newEncoder(f, 0),
|
||||||
@ -89,6 +93,9 @@ func Create(dirpath string) (*WAL, error) {
|
|||||||
if err := w.saveCrc(0); err != nil {
|
if err := w.saveCrc(0); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err := w.encoder.encode(&walpb.Record{Type: metadataType, Data: metadata}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return w, nil
|
return w, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,7 +161,7 @@ func OpenAtIndex(dirpath string, index uint64) (*WAL, error) {
|
|||||||
// ReadAll reads out all records of the current WAL.
|
// ReadAll reads out all records of the current WAL.
|
||||||
// If it cannot read out the expected entry, it will return ErrIndexNotFound.
|
// If it cannot read out the expected entry, it will return ErrIndexNotFound.
|
||||||
// After ReadAll, the WAL will be ready for appending new records.
|
// After ReadAll, the WAL will be ready for appending new records.
|
||||||
func (w *WAL) ReadAll() (id uint64, state raftpb.HardState, ents []raftpb.Entry, err error) {
|
func (w *WAL) ReadAll() (metadata []byte, state raftpb.HardState, ents []raftpb.Entry, err error) {
|
||||||
rec := &walpb.Record{}
|
rec := &walpb.Record{}
|
||||||
decoder := w.decoder
|
decoder := w.decoder
|
||||||
|
|
||||||
@ -168,44 +175,44 @@ func (w *WAL) ReadAll() (id uint64, state raftpb.HardState, ents []raftpb.Entry,
|
|||||||
w.enti = e.Index
|
w.enti = e.Index
|
||||||
case stateType:
|
case stateType:
|
||||||
state = mustUnmarshalState(rec.Data)
|
state = mustUnmarshalState(rec.Data)
|
||||||
case infoType:
|
case metadataType:
|
||||||
i := mustUnmarshalInfo(rec.Data)
|
if metadata != nil && !reflect.DeepEqual(metadata, rec.Data) {
|
||||||
if id != 0 && id != i.ID {
|
|
||||||
state.Reset()
|
state.Reset()
|
||||||
return 0, state, nil, ErrIDMismatch
|
return nil, state, nil, ErrMetadataConflict
|
||||||
}
|
}
|
||||||
id = i.ID
|
metadata = rec.Data
|
||||||
case crcType:
|
case crcType:
|
||||||
crc := decoder.crc.Sum32()
|
crc := decoder.crc.Sum32()
|
||||||
// current crc of decoder must match the crc of the record.
|
// current crc of decoder must match the crc of the record.
|
||||||
// do no need to match 0 crc, since the decoder is a new one at this case.
|
// do no need to match 0 crc, since the decoder is a new one at this case.
|
||||||
if crc != 0 && rec.Validate(crc) != nil {
|
if crc != 0 && rec.Validate(crc) != nil {
|
||||||
state.Reset()
|
state.Reset()
|
||||||
return 0, state, nil, ErrCRCMismatch
|
return nil, state, nil, ErrCRCMismatch
|
||||||
}
|
}
|
||||||
decoder.updateCRC(rec.Crc)
|
decoder.updateCRC(rec.Crc)
|
||||||
default:
|
default:
|
||||||
state.Reset()
|
state.Reset()
|
||||||
return 0, state, nil, fmt.Errorf("unexpected block type %d", rec.Type)
|
return nil, state, nil, fmt.Errorf("unexpected block type %d", rec.Type)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != io.EOF {
|
if err != io.EOF {
|
||||||
state.Reset()
|
state.Reset()
|
||||||
return 0, state, nil, err
|
return nil, state, nil, err
|
||||||
}
|
}
|
||||||
if w.enti < w.ri {
|
if w.enti < w.ri {
|
||||||
state.Reset()
|
state.Reset()
|
||||||
return 0, state, nil, ErrIndexNotFound
|
return nil, state, nil, ErrIndexNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// close decoder, disable reading
|
// close decoder, disable reading
|
||||||
w.decoder.close()
|
w.decoder.close()
|
||||||
w.ri = 0
|
w.ri = 0
|
||||||
|
|
||||||
|
w.md = metadata
|
||||||
// create encoder (chain crc with the decoder), enable appending
|
// create encoder (chain crc with the decoder), enable appending
|
||||||
w.encoder = newEncoder(w.f, w.decoder.lastCRC())
|
w.encoder = newEncoder(w.f, w.decoder.lastCRC())
|
||||||
w.decoder = nil
|
w.decoder = nil
|
||||||
return id, state, ents, nil
|
return metadata, state, ents, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cut closes current file written and creates a new one ready to append.
|
// Cut closes current file written and creates a new one ready to append.
|
||||||
@ -224,7 +231,10 @@ func (w *WAL) Cut() error {
|
|||||||
w.seq++
|
w.seq++
|
||||||
prevCrc := w.encoder.crc.Sum32()
|
prevCrc := w.encoder.crc.Sum32()
|
||||||
w.encoder = newEncoder(w.f, prevCrc)
|
w.encoder = newEncoder(w.f, prevCrc)
|
||||||
return w.saveCrc(prevCrc)
|
if err := w.saveCrc(prevCrc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return w.encoder.encode(&walpb.Record{Type: metadataType, Data: w.md})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WAL) Sync() error {
|
func (w *WAL) Sync() error {
|
||||||
@ -243,15 +253,6 @@ func (w *WAL) Close() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *WAL) SaveInfo(i *raftpb.Info) error {
|
|
||||||
b, err := i.Marshal()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
rec := &walpb.Record{Type: infoType, Data: b}
|
|
||||||
return w.encoder.encode(rec)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *WAL) SaveEntry(e *raftpb.Entry) error {
|
func (w *WAL) SaveEntry(e *raftpb.Entry) error {
|
||||||
b, err := e.Marshal()
|
b, err := e.Marshal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -27,11 +27,6 @@ import (
|
|||||||
"github.com/coreos/etcd/raft/raftpb"
|
"github.com/coreos/etcd/raft/raftpb"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
infoData = []byte("\b\xef\xfd\x02")
|
|
||||||
infoRecord = append([]byte("\x0e\x00\x00\x00\x00\x00\x00\x00\b\x01\x10\x99\xb5\xe4\xd0\x03\x1a\x04"), infoData...)
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNew(t *testing.T) {
|
func TestNew(t *testing.T) {
|
||||||
p, err := ioutil.TempDir(os.TempDir(), "waltest")
|
p, err := ioutil.TempDir(os.TempDir(), "waltest")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -39,7 +34,7 @@ func TestNew(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer os.RemoveAll(p)
|
defer os.RemoveAll(p)
|
||||||
|
|
||||||
w, err := Create(p)
|
w, err := Create(p, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("err = %v, want nil", err)
|
t.Fatalf("err = %v, want nil", err)
|
||||||
}
|
}
|
||||||
@ -57,7 +52,7 @@ func TestNewForInitedDir(t *testing.T) {
|
|||||||
defer os.RemoveAll(p)
|
defer os.RemoveAll(p)
|
||||||
|
|
||||||
os.Create(path.Join(p, walName(0, 0)))
|
os.Create(path.Join(p, walName(0, 0)))
|
||||||
if _, err = Create(p); err == nil || err != os.ErrExist {
|
if _, err = Create(p, nil); err == nil || err != os.ErrExist {
|
||||||
t.Errorf("err = %v, want %v", err, os.ErrExist)
|
t.Errorf("err = %v, want %v", err, os.ErrExist)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -123,7 +118,7 @@ func TestCut(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer os.RemoveAll(p)
|
defer os.RemoveAll(p)
|
||||||
|
|
||||||
w, err := Create(p)
|
w, err := Create(p, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
@ -161,14 +156,10 @@ func TestRecover(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer os.RemoveAll(p)
|
defer os.RemoveAll(p)
|
||||||
|
|
||||||
w, err := Create(p)
|
w, err := Create(p, []byte("metadata"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
i := &raftpb.Info{ID: uint64(0xBAD0)}
|
|
||||||
if err = w.SaveInfo(i); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
ents := []raftpb.Entry{{Index: 0, Term: 0}, {Index: 1, Term: 1, Data: []byte{1}}, {Index: 2, Term: 2, Data: []byte{2}}}
|
ents := []raftpb.Entry{{Index: 0, Term: 0}, {Index: 1, Term: 1, Data: []byte{1}}, {Index: 2, Term: 2, Data: []byte{2}}}
|
||||||
for _, e := range ents {
|
for _, e := range ents {
|
||||||
if err = w.SaveEntry(&e); err != nil {
|
if err = w.SaveEntry(&e); err != nil {
|
||||||
@ -186,13 +177,13 @@ func TestRecover(t *testing.T) {
|
|||||||
if w, err = OpenAtIndex(p, 0); err != nil {
|
if w, err = OpenAtIndex(p, 0); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
id, state, entries, err := w.ReadAll()
|
metadata, state, entries, err := w.ReadAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if id != i.ID {
|
if !reflect.DeepEqual(metadata, []byte("metadata")) {
|
||||||
t.Errorf("id = %d, want %d", id, i.ID)
|
t.Errorf("metadata = %s, want %s", metadata, "metadata")
|
||||||
}
|
}
|
||||||
if !reflect.DeepEqual(entries, ents) {
|
if !reflect.DeepEqual(entries, ents) {
|
||||||
t.Errorf("ents = %+v, want %+v", entries, ents)
|
t.Errorf("ents = %+v, want %+v", entries, ents)
|
||||||
@ -278,14 +269,10 @@ func TestRecoverAfterCut(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer os.RemoveAll(p)
|
defer os.RemoveAll(p)
|
||||||
|
|
||||||
w, err := Create(p)
|
w, err := Create(p, []byte("metadata"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
info := &raftpb.Info{ID: uint64(0xBAD1)}
|
|
||||||
if err = w.SaveInfo(info); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
// TODO(unihorn): remove this when cut can operate on an empty file
|
// TODO(unihorn): remove this when cut can operate on an empty file
|
||||||
if err = w.SaveEntry(&raftpb.Entry{}); err != nil {
|
if err = w.SaveEntry(&raftpb.Entry{}); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
@ -301,9 +288,6 @@ func TestRecoverAfterCut(t *testing.T) {
|
|||||||
if err = w.Cut(); err != nil {
|
if err = w.Cut(); err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
if err = w.SaveInfo(info); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
w.Close()
|
w.Close()
|
||||||
|
|
||||||
@ -323,13 +307,13 @@ func TestRecoverAfterCut(t *testing.T) {
|
|||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
id, _, entries, err := w.ReadAll()
|
metadata, _, entries, err := w.ReadAll()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("#%d: err = %v, want nil", i, err)
|
t.Errorf("#%d: err = %v, want nil", i, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if id != info.ID {
|
if !reflect.DeepEqual(metadata, []byte("metadata")) {
|
||||||
t.Errorf("#%d: id = %d, want %d", i, id, info.ID)
|
t.Errorf("#%d: metadata = %s, want %s", i, metadata, "metadata")
|
||||||
}
|
}
|
||||||
for j, e := range entries {
|
for j, e := range entries {
|
||||||
if e.Index != uint64(j+i) {
|
if e.Index != uint64(j+i) {
|
||||||
@ -346,7 +330,7 @@ func TestOpenAtUncommittedIndex(t *testing.T) {
|
|||||||
}
|
}
|
||||||
defer os.RemoveAll(p)
|
defer os.RemoveAll(p)
|
||||||
|
|
||||||
w, err := Create(p)
|
w, err := Create(p, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user