// start prepares and starts raftNode in a new goroutine. It is no longer safe // to modify the fields after it has been started. // TODO: Ideally raftNode should get rid of the passed in server structure. func (r *raftNode) start(s *EtcdServer) { r.s = s r.raftStorage.raftStarted = true r.applyc = make(chan apply) r.stopped = make(chan struct{}) r.done = make(chan struct{}) go func() { var syncC <-chan time.Time defer r.onStop() for { select { case <-r.ticker: r.Tick() case rd := <-r.Ready(): if rd.SoftState != nil { if lead := atomic.LoadUint64(&r.lead); rd.SoftState.Lead != raft.None && lead != rd.SoftState.Lead { r.mu.Lock() r.lt = time.Now() r.mu.Unlock() } atomic.StoreUint64(&r.lead, rd.SoftState.Lead) if rd.RaftState == raft.StateLeader { syncC = r.s.SyncTicker // TODO: remove the nil checking // current test utility does not provide the stats if r.s.stats != nil { r.s.stats.BecomeLeader() } } else { syncC = nil } } apply := apply{ entries: rd.CommittedEntries, snapshot: rd.Snapshot, done: make(chan struct{}), } select { case r.applyc <- apply: case <-r.stopped: return } if !raft.IsEmptySnap(rd.Snapshot) { if err := r.storage.SaveSnap(rd.Snapshot); err != nil { plog.Fatalf("raft save snapshot error: %v", err) } r.raftStorage.ApplySnapshot(rd.Snapshot) plog.Infof("raft applied incoming snapshot at index %d", rd.Snapshot.Metadata.Index) } if err := r.storage.Save(rd.HardState, rd.Entries); err != nil { plog.Fatalf("raft save state and entries error: %v", err) } r.raftStorage.Append(rd.Entries) r.s.send(rd.Messages) select { case <-apply.done: case <-r.stopped: return } r.Advance() case <-syncC: r.s.sync(r.s.cfg.ReqTimeout()) case <-r.stopped: return } } }() }
func (s *Snapshotter) SaveSnap(snapshot raftpb.Snapshot) error { if raft.IsEmptySnap(snapshot) { return nil } return s.save(&snapshot) }