func (t *Transport) SendSnapshot(m snap.Message) { p := t.peers[types.ID(m.To)] if p == nil { m.CloseWithError(errMemberNotFound) return } p.sendSnap(m) }
func (s *snapshotSender) send(merged snap.Message) { m := merged.Message start := time.Now() body := createSnapBody(merged) defer body.Close() u := s.picker.pick() req := createPostRequest(u, RaftSnapshotPrefix, body, "application/octet-stream", s.tr.URLs, s.from, s.cid) plog.Infof("start to send database snapshot [index: %d, to %s]...", m.Snapshot.Metadata.Index, types.ID(m.To)) err := s.post(req) defer merged.CloseWithError(err) if err != nil { plog.Warningf("database snapshot [index: %d, to: %s] failed to be sent out (%v)", m.Snapshot.Metadata.Index, types.ID(m.To), err) // errMemberRemoved is a critical error since a removed member should // always be stopped. So we use reportCriticalError to report it to errorc. if err == errMemberRemoved { reportCriticalError(err, s.errorc) } s.picker.unreachable(u) reportSentFailure(sendSnap, m) s.status.deactivate(failureType{source: sendSnap, action: "post"}, err.Error()) s.r.ReportUnreachable(m.To) // report SnapshotFailure to raft state machine. After raft state // machine knows about it, it would pause a while and retry sending // new snapshot message. s.r.ReportSnapshot(m.To, raft.SnapshotFailure) return } reportSentDuration(sendSnap, m, time.Since(start)) s.status.activate() s.r.ReportSnapshot(m.To, raft.SnapshotFinish) plog.Infof("database snapshot [index: %d, to: %s] sent out successfully", m.Snapshot.Metadata.Index, types.ID(m.To)) }
func (s *EtcdServer) sendMergedSnap(merged snap.Message) { atomic.AddInt64(&s.inflightSnapshots, 1) s.r.transport.SendSnapshot(merged) go func() { select { case ok := <-merged.CloseNotify(): // delay releasing inflight snapshot for another 30 seconds to // block log compaction. // If the follower still fails to catch up, it is probably just too slow // to catch up. We cannot avoid the snapshot cycle anyway. if ok { select { case <-time.After(releaseDelayAfterSnapshot): case <-s.done: } } atomic.AddInt64(&s.inflightSnapshots, -1) case <-s.done: return } }() }
func testSnapshotSend(t *testing.T, sm *snap.Message) (bool, []os.FileInfo) { d, err := ioutil.TempDir(os.TempDir(), "snapdir") if err != nil { t.Fatal(err) } defer os.RemoveAll(d) r := &fakeRaft{} tr := &Transport{pipelineRt: &http.Transport{}, ClusterID: types.ID(1), Raft: r} ch := make(chan struct{}, 1) h := &syncHandler{newSnapshotHandler(tr, r, snap.New(d), types.ID(1)), ch} srv := httptest.NewServer(h) defer srv.Close() picker := mustNewURLPicker(t, []string{srv.URL}) snapsend := newSnapshotSender(tr, picker, types.ID(1), newPeerStatus(types.ID(1))) defer snapsend.stop() snapsend.send(*sm) sent := false select { case <-time.After(time.Second): t.Fatalf("timed out sending snapshot") case sent = <-sm.CloseNotify(): } // wait for handler to finish accepting snapshot <-ch files, rerr := ioutil.ReadDir(d) if rerr != nil { t.Fatal(rerr) } return sent, files }
func (s *snapTransporter) SendSnapshot(m snap.Message) { ss := snap.New(s.snapDir) ss.SaveDBFrom(m.ReadCloser, m.Snapshot.Metadata.Index+1) m.CloseWithError(nil) s.snapDoneC <- m }