func (r *raftNode) run() { var syncC <-chan time.Time defer r.stop() for { select { case <-r.ticker: r.Tick() case rd := <-r.Ready(): if rd.SoftState != nil { 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.s.done: return } if !raft.IsEmptySnap(rd.Snapshot) { if err := r.storage.SaveSnap(rd.Snapshot); err != nil { log.Fatalf("etcdraft: save snapshot error: %v", err) } r.raftStorage.ApplySnapshot(rd.Snapshot) log.Printf("etcdraft: applied incoming snapshot at index %d", rd.Snapshot.Metadata.Index) } if err := r.storage.Save(rd.HardState, rd.Entries); err != nil { log.Fatalf("etcdraft: save state and entries error: %v", err) } r.raftStorage.Append(rd.Entries) r.s.send(rd.Messages) <-apply.done r.Advance() case <-syncC: r.s.sync(defaultSyncTimeout) case <-r.s.done: return } } }
func (s *EtcdServer) run() { snap, err := s.r.raftStorage.Snapshot() if err != nil { log.Panicf("etcdserver: get snapshot from raft storage error: %v", err) } confState := snap.Metadata.ConfState snapi := snap.Metadata.Index appliedi := snapi // TODO: get rid of the raft initialization in etcd server s.r.s = s s.r.applyc = make(chan apply) go s.r.run() defer close(s.done) var shouldstop bool for { select { case apply := <-s.r.apply(): // apply snapshot if !raft.IsEmptySnap(apply.snapshot) { if apply.snapshot.Metadata.Index <= appliedi { log.Panicf("etcdserver: snapshot index [%d] should > appliedi[%d] + 1", apply.snapshot.Metadata.Index, appliedi) } if err := s.store.Recovery(apply.snapshot.Data); err != nil { log.Panicf("recovery store error: %v", err) } // Avoid snapshot recovery overwriting newer cluster and // transport setting, which may block the communication. if s.Cluster.index < apply.snapshot.Metadata.Index { s.Cluster.Recover() // recover raft transport s.r.transport.RemoveAllPeers() for _, m := range s.Cluster.Members() { if m.ID == s.ID() { continue } s.r.transport.AddPeer(m.ID, m.PeerURLs) } } appliedi = apply.snapshot.Metadata.Index snapi = appliedi confState = apply.snapshot.Metadata.ConfState log.Printf("etcdserver: recovered from incoming snapshot at index %d", snapi) } // apply entries if len(apply.entries) != 0 { firsti := apply.entries[0].Index if firsti > appliedi+1 { log.Panicf("etcdserver: first index of committed entry[%d] should <= appliedi[%d] + 1", firsti, appliedi) } var ents []raftpb.Entry if appliedi+1-firsti < uint64(len(apply.entries)) { ents = apply.entries[appliedi+1-firsti:] } if appliedi, shouldstop = s.apply(ents, &confState); shouldstop { go s.stopWithDelay(10*100*time.Millisecond, fmt.Errorf("the member has been permanently removed from the cluster")) } } // wait for the raft routine to finish the disk writes before triggering a // snapshot. or applied index might be greater than the last index in raft // storage, since the raft routine might be slower than apply routine. apply.done <- struct{}{} // trigger snapshot if appliedi-snapi > s.snapCount { log.Printf("etcdserver: start to snapshot (applied: %d, lastsnap: %d)", appliedi, snapi) s.snapshot(appliedi, confState) snapi = appliedi } case err := <-s.errorc: log.Printf("etcdserver: %s", err) log.Printf("etcdserver: the data-dir used by this member must be removed.") return case <-s.stop: return } } // TODO: wait for the stop of raft node routine? }
func (s *Snapshotter) SaveSnap(snapshot raftpb.Snapshot) error { if raft.IsEmptySnap(snapshot) { return nil } return s.save(&snapshot) }