github.com/annchain/OG@v0.0.9/og/syncer/catchup_syncer.go (about)

     1  // Copyright © 2019 Annchain Authors <EMAIL ADDRESS>
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  package syncer
    15  
    16  import (
    17  	"github.com/annchain/OG/arefactor/common/goroutine"
    18  	"github.com/annchain/OG/og/types"
    19  	"sync"
    20  	"time"
    21  
    22  	// "github.com/annchain/OG/ffchan"
    23  	"github.com/annchain/OG/og"
    24  	"github.com/annchain/OG/og/downloader"
    25  	"github.com/sirupsen/logrus"
    26  )
    27  
    28  const (
    29  	//forceSyncCycle      = 10 * time.Second // Time interval to force syncs, even if few peers are available
    30  	minDesiredPeerCount = 5 // Amount of peers desired to start syncing
    31  
    32  	// This is the target size for the packs of transactions sent by pushPendingTxLoop.
    33  	// A pack can get larger than this if a single transactions exceeds this size.
    34  	txsyncPackSize = 100 * 1024
    35  
    36  	// TODO: this value will be set to optimal value in the future.
    37  	// If generating sequencer is very fast with few transactions, it should be bigger,
    38  	// otherwise it should be smaller
    39  	SyncerCheckTime = time.Second * 6
    40  
    41  	// when to stop sync once started
    42  	stopSyncHeightDiffThreashold uint64 = 0
    43  	// when to start sync
    44  	startSyncHeightDiffThreashold uint64 = 2
    45  )
    46  
    47  type CatchupSyncerStatus int
    48  
    49  func (m CatchupSyncerStatus) String() string {
    50  	switch m {
    51  	case Started:
    52  		return "CSSStarted"
    53  	case Stopped:
    54  		return "CSSStopped"
    55  	default:
    56  		return "Unknown"
    57  	}
    58  }
    59  
    60  const (
    61  	Started CatchupSyncerStatus = iota
    62  	Stopped
    63  )
    64  
    65  type CatchupSyncer struct {
    66  	NodeStatusDataProvider og.NodeStatusDataProvider
    67  	PeerProvider           og.PeerProvider
    68  	Hub                    *og.Hub
    69  
    70  	Downloader *downloader.Downloader
    71  	SyncMode   downloader.SyncMode
    72  
    73  	// should be enabled until quit
    74  	EnableEvent chan bool
    75  	Enabled     bool
    76  
    77  	quitLoopEvent chan bool
    78  	quit          chan bool
    79  
    80  	OnWorkingStateChanged         []chan CatchupSyncerStatus
    81  	OnNewTxiReceived              []chan types.Txi
    82  	NewPeerConnectedEventListener chan string
    83  	syncFlag                      bool
    84  	WorkState                     CatchupSyncerStatus
    85  	mu                            sync.RWMutex
    86  	BootStrapNode                 bool
    87  	currentBestHeight             uint64 //maybe incorect
    88  
    89  	initFlag  bool
    90  	peerAdded bool
    91  }
    92  
    93  func (c *CatchupSyncer) Init() {
    94  	c.EnableEvent = make(chan bool)
    95  	c.quitLoopEvent = make(chan bool)
    96  	c.NewPeerConnectedEventListener = make(chan string)
    97  	c.quit = make(chan bool)
    98  }
    99  
   100  func (c *CatchupSyncer) Start() {
   101  	goroutine.New(c.eventLoop)
   102  	goroutine.New(c.loopSync)
   103  }
   104  
   105  func (c *CatchupSyncer) Stop() {
   106  	close(c.quit)
   107  	close(c.quitLoopEvent)
   108  	// <-ffchan.NewTimeoutSender(c.quit, true, "catchupSyncerQuit", 1000).C
   109  	// <-ffchan.NewTimeoutSender(c.quitLoopEvent, true, "catchupSyncerQuitLoopEvent", 1000).C
   110  }
   111  
   112  func (CatchupSyncer) Name() string {
   113  	return "CatchupSyncer"
   114  }
   115  
   116  func (c *CatchupSyncer) isUpToDate(maxDiff uint64) bool {
   117  	_, bpHash, seqId, err := c.PeerProvider.BestPeerInfo()
   118  	if err != nil {
   119  		logrus.WithError(err).Debug("get best peer")
   120  		if c.BootStrapNode {
   121  			return true
   122  		}
   123  		return false
   124  	}
   125  	ourId := c.NodeStatusDataProvider.GetCurrentNodeStatus().CurrentId
   126  	logrus.WithField("bestPeer SeqId", seqId).
   127  		WithField("bestPeerHash", bpHash).
   128  		WithField("our SeqId", ourId).
   129  		Trace("checking uptodate")
   130  
   131  	if seqId <= ourId+maxDiff {
   132  		log.WithField("bestPeer SeqId", seqId).
   133  			WithField("bestPeerHash", bpHash).
   134  			WithField("our SeqId", ourId).
   135  			Debug("we are now up to date")
   136  		return true
   137  	} else {
   138  		logrus.WithField("bestPeer SeqId", seqId).
   139  			WithField("bestPeerHash", bpHash).
   140  			WithField("our SeqId", ourId).
   141  			Debug("we are yet up to date")
   142  	}
   143  	return false
   144  }
   145  
   146  func (c *CatchupSyncer) loopSync() {
   147  	c.Downloader.Start()
   148  	defer c.Downloader.Terminate()
   149  	for {
   150  		select {
   151  		case <-c.quit:
   152  			log.Info("CatchupSyncer loopSync received quit message. Quitting...")
   153  			return
   154  		case peer := <-c.NewPeerConnectedEventListener:
   155  			c.peerAdded = true
   156  			log.WithField("peer", peer).Info("new peer connected")
   157  			if !c.Enabled {
   158  				log.Debug("catchupSyncer not enabled")
   159  				continue
   160  			}
   161  			goroutine.New(func() {
   162  				c.syncToLatest()
   163  			})
   164  		case <-time.After(SyncerCheckTime):
   165  			if !c.Enabled {
   166  				log.Debug("catchup syncer not enabled")
   167  				continue
   168  			}
   169  			goroutine.New(func() {
   170  				c.syncToLatest()
   171  			})
   172  		}
   173  
   174  	}
   175  }
   176  
   177  func (c *CatchupSyncer) isSyncing() bool {
   178  	c.mu.Lock()
   179  	defer c.mu.Unlock()
   180  	return c.syncFlag
   181  }
   182  
   183  //getWorkState
   184  func (c *CatchupSyncer) getWorkState() CatchupSyncerStatus {
   185  	c.mu.Lock()
   186  	defer c.mu.Unlock()
   187  	return c.WorkState
   188  }
   189  
   190  func (c *CatchupSyncer) setSyncFlag() {
   191  	c.mu.Lock()
   192  	defer c.mu.Unlock()
   193  	c.syncFlag = true
   194  }
   195  
   196  func (c *CatchupSyncer) unsetSyncFlag() {
   197  	c.mu.Lock()
   198  	defer c.mu.Unlock()
   199  	c.syncFlag = false
   200  }
   201  
   202  func (c *CatchupSyncer) CacheNewTxEnabled() bool {
   203  	if c.getWorkState() == Stopped {
   204  		return true
   205  	}
   206  	ourSeqId := c.NodeStatusDataProvider.GetHeight()
   207  	if ourSeqId+startSyncHeightDiffThreashold*3 < c.currentBestHeight {
   208  		return false
   209  	}
   210  	return true
   211  
   212  }
   213  
   214  func (c *CatchupSyncer) syncToLatest() error {
   215  	if c.isSyncing() {
   216  		log.Trace("catchup syncing task is busy")
   217  		return nil
   218  	}
   219  	c.setSyncFlag()
   220  	defer c.unsetSyncFlag()
   221  	//get best peer ,and sync with this peer until we catchup
   222  
   223  	// must sync to latest at the beginning
   224  	var diff uint64 = 0
   225  	for !c.isUpToDate(diff) {
   226  		if !c.initFlag && c.peerAdded {
   227  			c.NotifyWorkingStateChanged(Started, true)
   228  			c.initFlag = true
   229  		} else {
   230  			c.NotifyWorkingStateChanged(Started, false)
   231  		}
   232  		diff = stopSyncHeightDiffThreashold
   233  
   234  		bpId, bpHash, seqId, err := c.PeerProvider.BestPeerInfo()
   235  		if err != nil {
   236  			log.WithError(err).Warn("picking up best peer")
   237  			return err
   238  		}
   239  		ourId := c.NodeStatusDataProvider.GetCurrentNodeStatus().CurrentId
   240  
   241  		log.WithField("peerId", bpId).WithField("seq", seqId).WithField("ourId", ourId).
   242  			Debug("catchup sync with best peer")
   243  		c.currentBestHeight = seqId
   244  		// Run the sync cycle, and disable fast sync if we've went past the pivot block
   245  		if err := c.Downloader.Synchronise(bpId, bpHash, seqId, c.SyncMode); err != nil {
   246  			log.WithError(err).Warn("catchup sync failed")
   247  			return err
   248  		}
   249  		logrus.WithField("seqId", seqId).Debug("finished downloader synchronize")
   250  		//bpHash, seqId, err = c.PeerProvider.GetPeerHead(bpId)
   251  		//if err != nil {
   252  		//	logrus.WithError(err).Warn("sync failed")
   253  		//	return err
   254  		//}
   255  	}
   256  	c.NotifyWorkingStateChanged(Stopped, false)
   257  	// allow a maximum of startSyncHeightDiffThreashold behind
   258  	diff = startSyncHeightDiffThreashold
   259  	return nil
   260  }
   261  
   262  //we don't need to broadcast this ,because we broadcast all of our latest sequencer head when it change
   263  /*
   264  func (c *CatchupSyncer) notifyProgress() {
   265  	nodeStatus := c.NodeStatusDataProvider.GetCurrentOgStatus()
   266  	if nodeStatus.CurrentHeight > 0 {
   267  		// We've completed a sync cycle, notify all peers of new state. This path is
   268  		// essential in star-topology networks where a gateway node needs to notify
   269  		// all its out-of-date peers of the availability of a new block. This failure
   270  		// scenario will most often crop up in private and hackathon networks with
   271  		// degenerate connectivity, but it should be healthy for the mainnet too to
   272  		// more reliably update peers or the local TD state.
   273  		hash := nodeStatus.CurrentBlock
   274  		msg := p2p_message.MessageSequencerHeader{Hash: &hash, Number: nodeStatus.CurrentHeight}
   275  		data, _ := msg.MarshalMsg(nil)
   276  		c.Hub.BroadcastMessage(p2p_message.MessageTypeSequencerHeader, data)
   277  	}
   278  }
   279  
   280  */
   281  func (c *CatchupSyncer) eventLoop() {
   282  	for {
   283  		select {
   284  		case v := <-c.EnableEvent:
   285  			log.WithField("enable", v).Info("catchup syncer got enable event")
   286  			c.Enabled = v
   287  		case <-c.quitLoopEvent:
   288  			log.Debug("catchup syncer eventLoop received quit message. Quitting...")
   289  			return
   290  		}
   291  	}
   292  }
   293  
   294  //NotifyWorkingStateChanged if starts status is true ,stops status is  false
   295  func (c *CatchupSyncer) NotifyWorkingStateChanged(status CatchupSyncerStatus, force bool) {
   296  	c.mu.Lock()
   297  	defer c.mu.Unlock()
   298  	if c.WorkState == status && !force {
   299  		return
   300  	}
   301  	c.WorkState = status
   302  	for _, ch := range c.OnWorkingStateChanged {
   303  		ch <- status
   304  		// <-ffchan.NewTimeoutSender(ch, status, "NotifyWorkingStateChanged", 1000).C
   305  	}
   306  }