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 }