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 }