mirror of
https://github.com/datarhei/core.git
synced 2025-11-02 20:24:02 +08:00
171 lines
2.7 KiB
Go
171 lines
2.7 KiB
Go
package cluster
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"sync"
|
|
|
|
"github.com/datarhei/core/v16/restream/app"
|
|
|
|
"github.com/hashicorp/raft"
|
|
)
|
|
|
|
type Store interface {
|
|
raft.FSM
|
|
|
|
ProcessList() []app.Config
|
|
GetProcess(id string) (app.Config, error)
|
|
}
|
|
|
|
type operation string
|
|
|
|
const (
|
|
opAddProcess operation = "addProcess"
|
|
opRemoveProcess operation = "removeProcess"
|
|
)
|
|
|
|
type command struct {
|
|
Operation operation
|
|
Data interface{}
|
|
}
|
|
|
|
type StoreNode struct {
|
|
ID string
|
|
Address string
|
|
}
|
|
|
|
type addProcessCommand struct {
|
|
app.Config
|
|
}
|
|
|
|
type removeProcessCommand struct {
|
|
ID string
|
|
}
|
|
|
|
// Implement a FSM
|
|
type store struct {
|
|
lock sync.RWMutex
|
|
Process map[string]app.Config
|
|
}
|
|
|
|
func NewStore() (Store, error) {
|
|
return &store{
|
|
Process: map[string]app.Config{},
|
|
}, nil
|
|
}
|
|
|
|
func (s *store) Apply(log *raft.Log) interface{} {
|
|
fmt.Printf("a log entry came in (index=%d, term=%d): %s\n", log.Index, log.Term, string(log.Data))
|
|
|
|
c := command{}
|
|
|
|
err := json.Unmarshal(log.Data, &c)
|
|
if err != nil {
|
|
fmt.Printf("invalid log entry\n")
|
|
return nil
|
|
}
|
|
|
|
fmt.Printf("op: %s\n", c.Operation)
|
|
fmt.Printf("op: %+v\n", c)
|
|
|
|
switch c.Operation {
|
|
case opAddProcess:
|
|
b, _ := json.Marshal(c.Data)
|
|
cmd := addProcessCommand{}
|
|
json.Unmarshal(b, &cmd)
|
|
|
|
s.lock.Lock()
|
|
s.Process[cmd.ID] = cmd.Config
|
|
s.lock.Unlock()
|
|
case opRemoveProcess:
|
|
b, _ := json.Marshal(c.Data)
|
|
cmd := removeProcessCommand{}
|
|
json.Unmarshal(b, &cmd)
|
|
|
|
s.lock.Lock()
|
|
delete(s.Process, cmd.ID)
|
|
s.lock.Unlock()
|
|
}
|
|
|
|
s.lock.RLock()
|
|
fmt.Printf("\n==> %+v\n\n", s.Process)
|
|
s.lock.RUnlock()
|
|
return nil
|
|
}
|
|
|
|
func (s *store) Snapshot() (raft.FSMSnapshot, error) {
|
|
fmt.Printf("a snapshot is requested\n")
|
|
|
|
s.lock.Lock()
|
|
defer s.lock.Unlock()
|
|
|
|
data, err := json.Marshal(s)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &fsmSnapshot{
|
|
data: data,
|
|
}, nil
|
|
}
|
|
|
|
func (s *store) Restore(snapshot io.ReadCloser) error {
|
|
fmt.Printf("a snapshot is restored\n")
|
|
|
|
defer snapshot.Close()
|
|
|
|
s.lock.Lock()
|
|
defer s.lock.Unlock()
|
|
|
|
dec := json.NewDecoder(snapshot)
|
|
if err := dec.Decode(s); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *store) ProcessList() []app.Config {
|
|
s.lock.RLock()
|
|
defer s.lock.RUnlock()
|
|
|
|
processes := []app.Config{}
|
|
|
|
for _, cfg := range s.Process {
|
|
processes = append(processes, *cfg.Clone())
|
|
}
|
|
|
|
return processes
|
|
}
|
|
|
|
func (s *store) GetProcess(id string) (app.Config, error) {
|
|
s.lock.RLock()
|
|
defer s.lock.RUnlock()
|
|
|
|
cfg, ok := s.Process[id]
|
|
if !ok {
|
|
return app.Config{}, fmt.Errorf("not found")
|
|
}
|
|
|
|
return *cfg.Clone(), nil
|
|
}
|
|
|
|
type fsmSnapshot struct {
|
|
data []byte
|
|
}
|
|
|
|
func (s *fsmSnapshot) Persist(sink raft.SnapshotSink) error {
|
|
if _, err := sink.Write(s.data); err != nil {
|
|
sink.Cancel()
|
|
return err
|
|
}
|
|
|
|
sink.Close()
|
|
return nil
|
|
}
|
|
|
|
func (s *fsmSnapshot) Release() {
|
|
s.data = nil
|
|
}
|