// NewStore returns a new store. It is useful to create a store inside // storage pkg. It should only be used for testing externally. func NewStore(b backend.Backend, le lease.Lessor) *store { s := &store{ b: b, kvindex: newTreeIndex(), le: le, currentRev: revision{main: 1}, compactMainRev: -1, fifoSched: schedule.NewFIFOScheduler(), stopc: make(chan struct{}), } if s.le != nil { s.le.SetRangeDeleter(s) } tx := s.b.BatchTx() tx.Lock() tx.UnsafeCreateBucket(keyBucketName) tx.UnsafeCreateBucket(metaBucketName) tx.Unlock() s.b.ForceCommit() if err := s.restore(); err != nil { // TODO: return the error instead of panic here? panic("failed to recover store from backend") } return s }
func (s *EtcdServer) run() { snap, err := s.r.raftStorage.Snapshot() if err != nil { plog.Panicf("get snapshot from raft storage error: %v", err) } s.r.start(s) // asynchronously accept apply packets, dispatch progress in-order sched := schedule.NewFIFOScheduler() ep := etcdProgress{ confState: snap.Metadata.ConfState, snapi: snap.Metadata.Index, appliedi: snap.Metadata.Index, } defer func() { s.r.stop() sched.Stop() // kv, lessor and backend can be nil if running without v3 enabled // or running unit tests. if s.lessor != nil { s.lessor.Stop() } if s.kv != nil { s.kv.Close() } if s.be != nil { s.be.Close() } close(s.done) }() var expiredLeaseC <-chan []*lease.Lease if s.lessor != nil { expiredLeaseC = s.lessor.ExpiredLeasesC() } for { select { case ap := <-s.r.apply(): f := func(context.Context) { s.applyAll(&ep, &ap) } sched.Schedule(f) case leases := <-expiredLeaseC: go func() { for _, l := range leases { s.LeaseRevoke(context.TODO(), &pb.LeaseRevokeRequest{ID: int64(l.ID)}) } }() case err := <-s.errorc: plog.Errorf("%s", err) plog.Infof("the data-dir used by this member must be removed.") return case <-s.stop: return } } }
func (s *store) Restore(b backend.Backend) error { s.mu.Lock() defer s.mu.Unlock() close(s.stopc) s.fifoSched.Stop() s.b = b s.kvindex = newTreeIndex() s.currentRev = revision{main: 1} s.compactMainRev = -1 s.tx = b.BatchTx() s.txnID = -1 s.fifoSched = schedule.NewFIFOScheduler() s.stopc = make(chan struct{}) return s.restore() }
func newFakeStore() *store { b := &fakeBackend{&fakeBatchTx{ Recorder: &testutil.RecorderBuffered{}, rangeRespc: make(chan rangeResp, 5)}} fi := &fakeIndex{ Recorder: &testutil.RecorderBuffered{}, indexGetRespc: make(chan indexGetResp, 1), indexRangeRespc: make(chan indexRangeResp, 1), indexRangeEventsRespc: make(chan indexRangeEventsResp, 1), indexCompactRespc: make(chan map[revision]struct{}, 1), } return &store{ b: b, le: &lease.FakeLessor{}, kvindex: fi, currentRev: revision{}, compactMainRev: -1, fifoSched: schedule.NewFIFOScheduler(), stopc: make(chan struct{}), } }
func (s *EtcdServer) run() { snap, err := s.r.raftStorage.Snapshot() if err != nil { plog.Panicf("get snapshot from raft storage error: %v", err) } var ( smu sync.RWMutex syncC <-chan time.Time ) setSyncC := func(ch <-chan time.Time) { smu.Lock() syncC = ch smu.Unlock() } getSyncC := func() (ch <-chan time.Time) { smu.RLock() ch = syncC smu.RUnlock() return } rh := &raftReadyHandler{ leadershipUpdate: func() { if !s.isLeader() { if s.lessor != nil { s.lessor.Demote() } if s.compactor != nil { s.compactor.Pause() } setSyncC(nil) } else { setSyncC(s.SyncTicker) } // TODO: remove the nil checking // current test utility does not provide the stats if s.stats != nil { s.stats.BecomeLeader() } if s.compactor != nil { s.compactor.Resume() } if s.r.td != nil { s.r.td.Reset() } }, } s.r.start(rh) // asynchronously accept apply packets, dispatch progress in-order sched := schedule.NewFIFOScheduler() ep := etcdProgress{ confState: snap.Metadata.ConfState, snapi: snap.Metadata.Index, appliedi: snap.Metadata.Index, } defer func() { s.wgMu.Lock() // block concurrent waitgroup adds in goAttach while stopping close(s.stopping) s.wgMu.Unlock() sched.Stop() // wait for gouroutines before closing raft so wal stays open s.wg.Wait() // must stop raft after scheduler-- etcdserver can leak rafthttp pipelines // by adding a peer after raft stops the transport s.r.stop() // kv, lessor and backend can be nil if running without v3 enabled // or running unit tests. if s.lessor != nil { s.lessor.Stop() } if s.kv != nil { s.kv.Close() } if s.be != nil { s.be.Close() } if s.compactor != nil { s.compactor.Stop() } close(s.done) }() var expiredLeaseC <-chan []*lease.Lease if s.lessor != nil { expiredLeaseC = s.lessor.ExpiredLeasesC() } for { select { case ap := <-s.r.apply(): var ci uint64 if len(ap.entries) != 0 { ci = ap.entries[len(ap.entries)-1].Index } if ap.snapshot.Metadata.Index > ci { ci = ap.snapshot.Metadata.Index } if ci != 0 { s.setCommittedIndex(ci) } f := func(context.Context) { s.applyAll(&ep, &ap) } sched.Schedule(f) case leases := <-expiredLeaseC: s.goAttach(func() { // Increases throughput of expired leases deletion process through parallelization c := make(chan struct{}, maxPendingRevokes) for _, lease := range leases { select { case c <- struct{}{}: case <-s.stopping: return } lid := lease.ID s.goAttach(func() { s.LeaseRevoke(context.TODO(), &pb.LeaseRevokeRequest{ID: int64(lid)}) <-c }) } }) case err := <-s.errorc: plog.Errorf("%s", err) plog.Infof("the data-dir used by this member must be removed.") return case <-getSyncC(): s.sync(s.Cfg.ReqTimeout()) case <-s.stop: return } } }
func (s *EtcdServer) run() { snap, err := s.r.raftStorage.Snapshot() if err != nil { plog.Panicf("get snapshot from raft storage error: %v", err) } s.r.start(s) // asynchronously accept apply packets, dispatch progress in-order sched := schedule.NewFIFOScheduler() ep := etcdProgress{ confState: snap.Metadata.ConfState, snapi: snap.Metadata.Index, appliedi: snap.Metadata.Index, } defer func() { close(s.stopping) sched.Stop() // wait for gouroutines before closing raft so wal stays open s.wg.Wait() // must stop raft after scheduler-- etcdserver can leak rafthttp pipelines // by adding a peer after raft stops the transport s.r.stop() // kv, lessor and backend can be nil if running without v3 enabled // or running unit tests. if s.lessor != nil { s.lessor.Stop() } if s.kv != nil { s.kv.Close() } if s.be != nil { s.be.Close() } if s.compactor != nil { s.compactor.Stop() } close(s.done) }() var expiredLeaseC <-chan []*lease.Lease if s.lessor != nil { expiredLeaseC = s.lessor.ExpiredLeasesC() } for { select { case ap := <-s.r.apply(): var ci uint64 if len(ap.entries) != 0 { ci = ap.entries[len(ap.entries)-1].Index } if ap.snapshot.Metadata.Index > ci { ci = ap.snapshot.Metadata.Index } if ci != 0 { s.setCommittedIndex(ci) } f := func(context.Context) { s.applyAll(&ep, &ap) } sched.Schedule(f) case leases := <-expiredLeaseC: s.goAttach(func() { // Increases throughput of expired leases deletion process through parallelization c := make(chan struct{}, maxPendingRevokes) for _, lease := range leases { select { case c <- struct{}{}: case <-s.stopping: return } lid := lease.ID s.goAttach(func() { s.LeaseRevoke(context.TODO(), &pb.LeaseRevokeRequest{ID: int64(lid)}) <-c }) } }) case err := <-s.errorc: plog.Errorf("%s", err) plog.Infof("the data-dir used by this member must be removed.") return case <-s.stop: return } } }