99 lines
1.5 KiB
Go
99 lines
1.5 KiB
Go
package raft
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"time"
|
|
|
|
"git.giftfish.de/ston1th/raftbbolt/msgpack"
|
|
"github.com/hashicorp/raft"
|
|
)
|
|
|
|
const (
|
|
retainSnapshotCount = 2
|
|
raftTimeout = 10 * time.Second
|
|
)
|
|
|
|
type Op uint8
|
|
|
|
const (
|
|
SET Op = iota
|
|
DELETE
|
|
)
|
|
|
|
type raftCmd struct {
|
|
Op Op
|
|
K string
|
|
V []byte
|
|
}
|
|
|
|
type fsm kv
|
|
|
|
func (f *fsm) Apply(l *raft.Log) interface{} {
|
|
var c raftCmd
|
|
if err := msgpack.Unmarshal(l.Data, &c); err != nil {
|
|
panic(fmt.Sprintf("failed to unmarshal command: %s", err.Error()))
|
|
}
|
|
|
|
switch c.Op {
|
|
case SET:
|
|
f.applySet(c.K, c.V)
|
|
case DELETE:
|
|
f.applyDelete(c.K)
|
|
default:
|
|
panic(fmt.Sprintf("unrecognized command op: %s", c.Op))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (f *fsm) Snapshot() (raft.FSMSnapshot, error) {
|
|
f.mu.Lock()
|
|
defer f.mu.Unlock()
|
|
m := make(kvm)
|
|
for k, v := range f.m {
|
|
m[k] = v
|
|
}
|
|
return &fsmSnapshot{m: m}, nil
|
|
}
|
|
|
|
func (f *fsm) Restore(rc io.ReadCloser) error {
|
|
o := make(kvm)
|
|
if err := msgpack.NewDecoder(rc).Decode(&o); err != nil {
|
|
return err
|
|
}
|
|
f.m = o
|
|
return nil
|
|
}
|
|
|
|
func (f *fsm) applySet(k string, v []byte) {
|
|
f.mu.Lock()
|
|
f.m[k] = v
|
|
f.mu.Unlock()
|
|
}
|
|
|
|
func (f *fsm) applyDelete(k string) {
|
|
f.mu.Lock()
|
|
delete(f.m, k)
|
|
f.mu.Unlock()
|
|
}
|
|
|
|
type fsmSnapshot struct {
|
|
m kvm
|
|
}
|
|
|
|
func (f *fsmSnapshot) Persist(sink raft.SnapshotSink) error {
|
|
err := func() error {
|
|
err := msgpack.NewEncoder(sink).Encode(f.m)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return sink.Close()
|
|
}()
|
|
|
|
if err != nil {
|
|
sink.Cancel()
|
|
}
|
|
return err
|
|
}
|
|
|
|
func (f *fsmSnapshot) Release() {}
|