func (enc *messageEncoder) encode(m raftpb.Message) error { if err := binary.Write(enc.w, binary.BigEndian, uint64(m.Size())); err != nil { return err } _, err := enc.w.Write(pbutil.MustMarshal(&m)) return err }
// send persists state to stable storage and then sends to its mailbox. func (r *raft) send(m pb.Message) { m.From = r.id // do not attach term to MsgProp // proposals are a way to forward to the leader and // should be treated as local message. if m.Type != pb.MsgProp { m.Term = r.Term } r.msgs = append(r.msgs, m) }
func (s *EtcdServer) Process(ctx context.Context, m raftpb.Message) error { if s.Cluster.IsIDRemoved(types.ID(m.From)) { log.Printf("etcdserver: reject message from removed member %s", types.ID(m.From).String()) return httptypes.NewHTTPError(http.StatusForbidden, "cannot process message from removed member") } if m.Type == raftpb.MsgApp { s.stats.RecvAppendReq(types.ID(m.From).String(), m.Size()) } return s.r.Step(ctx, m) }
func (dec *messageDecoder) decode() (raftpb.Message, error) { var m raftpb.Message var l uint64 if err := binary.Read(dec.r, binary.BigEndian, &l); err != nil { return m, err } buf := make([]byte, int(l)) if _, err := io.ReadFull(dec.r, buf); err != nil { return m, err } return m, m.Unmarshal(buf) }
func stepFollower(r *raft, m pb.Message) { switch m.Type { case pb.MsgProp: if r.lead == None { raftLogger.Infof("raft: %x no leader at term %d; dropping proposal", r.id, r.Term) return } m.To = r.lead r.send(m) case pb.MsgApp: r.elapsed = 0 r.lead = m.From r.handleAppendEntries(m) case pb.MsgHeartbeat: r.elapsed = 0 r.lead = m.From r.handleHeartbeat(m) case pb.MsgSnap: r.elapsed = 0 r.handleSnapshot(m) case pb.MsgVote: if (r.Vote == None || r.Vote == m.From) && r.raftLog.isUpToDate(m.Index, m.LogTerm) { r.elapsed = 0 raftLogger.Infof("raft: %x [logterm: %d, index: %d, vote: %x] voted for %x [logterm: %d, index: %d] at term %d", r.id, r.raftLog.lastTerm(), r.raftLog.lastIndex(), r.Vote, m.From, m.LogTerm, m.Index, r.Term) r.Vote = m.From r.send(pb.Message{To: m.From, Type: pb.MsgVoteResp}) } else { raftLogger.Infof("raft: %x [logterm: %d, index: %d, vote: %x] rejected vote from %x [logterm: %d, index: %d] at term %d", r.id, r.raftLog.lastTerm(), r.raftLog.lastIndex(), r.Vote, m.From, m.LogTerm, m.Index, r.Term) r.send(pb.Message{To: m.From, Type: pb.MsgVoteResp, Reject: true}) } } }
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { w.Header().Set("Allow", "POST") http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) return } wcid := h.cid.String() w.Header().Set("X-Etcd-Cluster-ID", wcid) gcid := r.Header.Get("X-Etcd-Cluster-ID") if gcid != wcid { log.Printf("rafthttp: request ignored due to cluster ID mismatch got %s want %s", gcid, wcid) http.Error(w, "clusterID mismatch", http.StatusPreconditionFailed) return } // Limit the data size that could be read from the request body, which ensures that read from // connection will not time out accidentally due to possible block in underlying implementation. limitedr := pioutil.NewLimitedBufferReader(r.Body, ConnReadLimitByte) b, err := ioutil.ReadAll(limitedr) if err != nil { log.Println("rafthttp: error reading raft message:", err) http.Error(w, "error reading raft message", http.StatusBadRequest) return } var m raftpb.Message if err := m.Unmarshal(b); err != nil { log.Println("rafthttp: error unmarshaling raft message:", err) http.Error(w, "error unmarshaling raft message", http.StatusBadRequest) return } if err := h.r.Process(context.TODO(), m); err != nil { switch v := err.(type) { case writerToResponse: v.WriteTo(w) default: log.Printf("rafthttp: error processing raft message: %v", err) http.Error(w, "error processing raft message", http.StatusInternalServerError) } return } // Write StatusNoContet header after the message has been processed by // raft, which faciliates the client to report MsgSnap status. w.WriteHeader(http.StatusNoContent) }
// sendAppend sends RRPC, with entries to the given peer. func (r *raft) sendAppend(to uint64) { pr := r.prs[to] if pr.isPaused() { return } m := pb.Message{} m.To = to if r.needSnapshot(pr.Next) { m.Type = pb.MsgSnap snapshot, err := r.raftLog.snapshot() if err != nil { panic(err) // TODO(bdarnell) } if IsEmptySnap(snapshot) { panic("need non-empty snapshot") } m.Snapshot = snapshot sindex, sterm := snapshot.Metadata.Index, snapshot.Metadata.Term raftLogger.Infof("raft: %x [firstindex: %d, commit: %d] sent snapshot[index: %d, term: %d] to %x [%s]", r.id, r.raftLog.firstIndex(), r.Commit, sindex, sterm, to, pr) pr.becomeSnapshot(sindex) raftLogger.Infof("raft: %x paused sending replication messages to %x [%s]", r.id, to, pr) } else { m.Type = pb.MsgApp m.Index = pr.Next - 1 m.LogTerm = r.raftLog.term(pr.Next - 1) m.Entries = r.raftLog.entries(pr.Next, r.maxMsgSize) m.Commit = r.raftLog.committed if n := len(m.Entries); n != 0 { switch pr.State { // optimistically increase the next when in ProgressStateReplicate case ProgressStateReplicate: last := m.Entries[n-1].Index pr.optimisticUpdate(last) pr.ins.add(last) case ProgressStateProbe: pr.pause() default: raftLogger.Panicf("raft: %x is sending append in unhandled state %s", r.id, pr.State) } } } r.send(m) }