github.com/aergoio/aergo@v1.3.1/syncer/syncerservice.go (about) 1 package syncer 2 3 import ( 4 "github.com/aergoio/aergo/chain" 5 "github.com/aergoio/aergo/p2p/p2putil" 6 "runtime/debug" 7 8 "github.com/aergoio/aergo-lib/log" 9 cfg "github.com/aergoio/aergo/config" 10 "github.com/aergoio/aergo/pkg/component" 11 12 "fmt" 13 "reflect" 14 "testing" 15 "time" 16 17 "github.com/aergoio/aergo-actor/actor" 18 "github.com/aergoio/aergo/message" 19 "github.com/aergoio/aergo/types" 20 "github.com/pkg/errors" 21 ) 22 23 type Syncer struct { 24 *component.BaseComponent 25 26 Seq uint64 27 cfg *cfg.Config 28 syncerCfg *SyncerConfig 29 chain types.ChainAccessor 30 31 isRunning bool 32 ctx *types.SyncContext 33 34 finder *Finder 35 hashFetcher *HashFetcher 36 blockFetcher *BlockFetcher 37 38 compRequester component.IComponentRequester //for test 39 } 40 41 type SyncerConfig struct { 42 maxHashReqSize uint64 43 maxBlockReqSize int 44 maxPendingConn int 45 maxBlockReqTasks int 46 47 fetchTimeOut time.Duration 48 49 useFullScanOnly bool 50 51 debugContext *SyncerDebug 52 } 53 type SyncerDebug struct { 54 t *testing.T 55 expAncestor int 56 targetNo uint64 57 expErrResult error 58 59 debugHashFetcher bool 60 debugFinder bool 61 62 BfWaitTime time.Duration 63 64 logAllPeersBad bool 65 logBadPeers map[int]bool //register bad peers for unit test 66 } 67 68 var ( 69 logger = log.NewLogger("syncer") 70 NameFinder = "Finder" 71 NameHashFetcher = "HashFetcher" 72 NameBlockFetcher = "BlockFetcher" 73 NameBlockProcessor = "BlockProcessor" 74 SyncerCfg = &SyncerConfig{ 75 maxHashReqSize: DfltHashReqSize, 76 maxBlockReqSize: DfltBlockFetchSize, 77 maxPendingConn: MaxBlockPendingTasks, 78 maxBlockReqTasks: DfltBlockFetchTasks, 79 fetchTimeOut: DfltFetchTimeOut, 80 useFullScanOnly: false} 81 ) 82 83 var ( 84 ErrFinderInternal = errors.New("error finder internal") 85 ErrSyncerPanic = errors.New("syncer panic") 86 ) 87 88 type ErrSyncMsg struct { 89 msg interface{} 90 str string 91 } 92 93 func (ec *ErrSyncMsg) Error() string { 94 return fmt.Sprintf("Error sync message: type=%T, desc=%s", ec.msg, ec.str) 95 } 96 97 func NewSyncer(cfg *cfg.Config, chain types.ChainAccessor, syncerCfg *SyncerConfig) *Syncer { 98 if syncerCfg == nil { 99 syncerCfg = SyncerCfg 100 } 101 102 syncer := &Syncer{cfg: cfg, syncerCfg: syncerCfg} 103 104 syncer.BaseComponent = component.NewBaseComponent(message.SyncerSvc, syncer, logger) 105 syncer.compRequester = syncer.BaseComponent 106 syncer.chain = chain 107 syncer.Seq = 1 108 109 logger.Info().Uint64("seq", syncer.Seq).Msg("Syncer started") 110 111 return syncer 112 } 113 114 // BeforeStart initialize chain database and generate empty genesis block if necessary 115 func (syncer *Syncer) BeforeStart() { 116 } 117 118 // AfterStart ... do nothing 119 func (syncer *Syncer) AfterStart() { 120 121 } 122 123 func (syncer *Syncer) BeforeStop() { 124 if syncer.isRunning { 125 logger.Info().Msg("syncer BeforeStop") 126 syncer.Reset(nil) 127 } 128 } 129 130 func (syncer *Syncer) Reset(err error) { 131 if syncer.isRunning { 132 logger.Info().Uint64("targetNo", syncer.ctx.TargetNo).Msg("syncer stop#1") 133 134 syncer.finder.stop() 135 syncer.hashFetcher.stop() 136 syncer.blockFetcher.stop() 137 138 syncer.finder = nil 139 syncer.hashFetcher = nil 140 syncer.blockFetcher = nil 141 syncer.isRunning = false 142 143 syncer.notifyStop(err) 144 145 syncer.ctx = nil 146 } 147 148 logger.Info().Msg("syncer stopped") 149 } 150 151 func (syncer *Syncer) notifyStop(err error) { 152 if syncer.ctx == nil || syncer.ctx.NotifyC == nil { 153 return 154 } 155 156 logger.Info().Err(err).Msg("notify syncer stop") 157 158 select { 159 case syncer.ctx.NotifyC <- err: 160 default: 161 logger.Debug().Msg("failed to notify syncer stop") 162 } 163 } 164 165 func (syncer *Syncer) GetSeq() uint64 { 166 return syncer.Seq 167 } 168 169 func (syncer *Syncer) IncSeq() uint64 { 170 syncer.Seq++ 171 return syncer.Seq 172 } 173 174 func (syncer *Syncer) getCompRequester() component.IComponentRequester { 175 if syncer.compRequester != nil { 176 return syncer.compRequester 177 } else { 178 return syncer.BaseComponent 179 } 180 } 181 182 // This api used for test to set stub IComponentRequester 183 func (syncer *Syncer) SetRequester(stubRequester component.IComponentRequester) { 184 syncer.compRequester = stubRequester 185 } 186 187 // Receive actor message 188 func (syncer *Syncer) Receive(context actor.Context) { 189 //drop garbage message 190 if !syncer.isRunning { 191 switch context.Message().(type) { 192 case *message.GetSyncAncestorRsp, 193 *message.FinderResult, 194 *message.GetHashesRsp, 195 *message.GetHashByNoRsp, 196 *message.GetBlockChunks, 197 *message.GetBlockChunksRsp, 198 *message.AddBlockRsp, 199 *message.SyncStop, 200 *message.CloseFetcher: 201 return 202 } 203 } 204 205 syncer.handleMessage(context.Message()) 206 } 207 208 func (syncer *Syncer) verifySeq(inmsg interface{}) bool { 209 isMatch := func(seq uint64) bool { 210 return syncer.Seq == seq 211 } 212 213 var seq uint64 214 var match bool 215 216 switch msg := inmsg.(type) { 217 case *message.GetAnchorsRsp: 218 seq = msg.Seq 219 match = isMatch(seq) 220 case *message.GetSyncAncestorRsp: 221 seq = msg.Seq 222 match = isMatch(seq) 223 case *message.FinderResult: 224 seq = msg.Seq 225 match = isMatch(seq) 226 case *message.GetHashesRsp: 227 seq = msg.Seq 228 match = isMatch(seq) 229 case *message.GetHashByNoRsp: 230 seq = msg.Seq 231 match = isMatch(seq) 232 case *message.GetBlockChunksRsp: 233 seq = msg.Seq 234 match = isMatch(seq) 235 case *message.SyncStop: 236 seq = msg.Seq 237 match = isMatch(seq) 238 case *message.CloseFetcher: 239 seq = msg.Seq 240 match = isMatch(seq) 241 default: 242 match = true 243 } 244 245 if !match { 246 logger.Debug().Msgf("syncer(seq=%d) message(%T, seq=%d) is dropped", syncer.GetSeq(), inmsg, seq) 247 } 248 249 return match 250 } 251 252 func (syncer *Syncer) handleMessage(inmsg interface{}) { 253 defer syncer.RecoverSyncerSelf() 254 255 if !syncer.verifySeq(inmsg) { 256 return 257 } 258 259 switch msg := inmsg.(type) { 260 case *message.SyncStart: 261 err := syncer.handleSyncStart(msg) 262 if err != nil { 263 logger.Error().Err(err).Msg("SyncStart failed") 264 } 265 case *message.GetSyncAncestorRsp: 266 syncer.handleAncestorRsp(msg) 267 case *message.GetHashByNoRsp: 268 syncer.handleGetHashByNoRsp(msg) 269 case *message.FinderResult: 270 err := syncer.handleFinderResult(msg) 271 if err != nil { 272 syncer.Reset(err) 273 logger.Error().Err(err).Msg("FinderResult failed") 274 } 275 case *message.GetHashesRsp: 276 syncer.hashFetcher.GetHahsesRsp(msg) 277 278 case *message.GetBlockChunksRsp: 279 err := syncer.blockFetcher.handleBlockRsp(msg) 280 if err != nil { 281 syncer.Reset(err) 282 logger.Error().Err(err).Msg("GetBlockChunksRsp failed") 283 } 284 case *message.AddBlockRsp: 285 err := syncer.blockFetcher.handleBlockRsp(msg) 286 if err != nil { 287 syncer.Reset(err) 288 logger.Error().Err(err).Msg("AddBlockRsp failed") 289 } 290 case *message.SyncStop: 291 if msg.Err == nil { 292 logger.Info().Str("from", msg.FromWho).Msg("syncer try to stop successfully") 293 } else { 294 logger.Error().Str("from", msg.FromWho).Err(msg.Err).Msg("syncer try to stop by error") 295 } 296 syncer.Reset(msg.Err) 297 case *message.CloseFetcher: 298 if msg.FromWho == NameHashFetcher { 299 syncer.hashFetcher.stop() 300 } else if msg.FromWho == NameBlockFetcher { 301 syncer.blockFetcher.stop() 302 } else { 303 logger.Error().Msg("invalid closing module message to syncer") 304 } 305 case actor.SystemMessage, 306 actor.AutoReceiveMessage, 307 actor.NotInfluenceReceiveTimeout: 308 str := fmt.Sprintf("Received message. (%v) %s", reflect.TypeOf(msg), msg) 309 logger.Debug().Msg(str) 310 case *actor.Started, *actor.Stopping, *actor.Stopped, *component.CompStatReq: // donothing 311 default: 312 str := fmt.Sprintf("Missed message. (%v) %s", reflect.TypeOf(msg), msg) 313 logger.Debug().Msg(str) 314 } 315 } 316 317 func (syncer *Syncer) handleSyncStart(msg *message.SyncStart) error { 318 var err error 319 var bestBlock *types.Block 320 321 logger.Debug().Uint64("targetNo", msg.TargetNo).Str("peer", p2putil.ShortForm(msg.PeerID)).Msg("syncer requested") 322 323 if syncer.isRunning { 324 logger.Debug().Uint64("targetNo", msg.TargetNo).Msg("skipped syncer is running") 325 return nil 326 } 327 328 //TODO skip sync in reorgnizing 329 bestBlock, _ = syncer.chain.GetBestBlock() 330 if err != nil { 331 logger.Error().Err(err).Msg("error getting block in syncer") 332 return err 333 } 334 335 bestBlockNo := bestBlock.GetHeader().BlockNo 336 337 if msg.TargetNo <= bestBlockNo { 338 logger.Debug().Uint64("targetNo", msg.TargetNo).Uint64("bestNo", bestBlockNo). 339 Msg("skipped syncer. requested no is too low") 340 return nil 341 } 342 343 syncer.IncSeq() 344 345 logger.Info().Uint64("seq", syncer.GetSeq()).Uint64("targetNo", msg.TargetNo).Uint64("bestNo", bestBlockNo).Msg("syncer started") 346 347 //TODO BP stop 348 syncer.ctx = types.NewSyncCtx(syncer.GetSeq(), msg.PeerID, msg.TargetNo, bestBlockNo, msg.NotifyC) 349 syncer.isRunning = true 350 351 syncer.finder = newFinder(syncer.ctx, syncer.getCompRequester(), syncer.chain, syncer.syncerCfg) 352 syncer.finder.start() 353 354 return err 355 } 356 357 func (syncer *Syncer) handleAncestorRsp(msg *message.GetSyncAncestorRsp) { 358 var ancestorNo uint64 359 360 if msg.Ancestor != nil { 361 ancestorNo = msg.Ancestor.No 362 } 363 364 logger.Debug().Uint64("no", ancestorNo).Msg("syncer received ancestor response") 365 366 if syncer.finder == nil { 367 logger.Debug().Msg("finder already stopped. so drop unexpected AncestorRsp message") 368 return 369 } 370 371 //set ancestor in types.SyncContext 372 select { 373 case syncer.finder.lScanCh <- msg.Ancestor: 374 logger.Debug().Uint64("seq", msg.Seq).Msg("syncer transfer response to finder") 375 default: 376 logger.Debug().Uint64("seq", msg.Seq).Msg("syncer dropped response of finder") 377 } 378 } 379 380 func (syncer *Syncer) handleGetHashByNoRsp(msg *message.GetHashByNoRsp) { 381 logger.Debug().Msg("syncer received gethashbyno response") 382 383 //set ancestor in types.SyncContext 384 syncer.finder.GetHashByNoRsp(msg) 385 } 386 387 func (syncer *Syncer) handleFinderResult(msg *message.FinderResult) error { 388 logger.Debug().Msg("syncer received finder result message") 389 390 if err := chain.TestDebugger.Check(chain.DEBUG_SYNCER_CRASH, 0, nil); err != nil { 391 chain.TestDebugger.Unset(chain.DEBUG_SYNCER_CRASH) 392 return err 393 } 394 395 if msg.Err != nil || msg.Ancestor == nil { 396 logger.Error().Err(msg.Err).Msg("Find Ancestor failed") 397 return ErrFinderInternal 398 } 399 400 ancestor, err := syncer.chain.GetBlock(msg.Ancestor.Hash) 401 if err != nil { 402 logger.Error().Err(err).Msg("error getting ancestor block in syncer") 403 return err 404 } 405 406 //set ancestor in types.SyncContext 407 syncer.ctx.SetAncestor(ancestor) 408 409 syncer.finder.stop() 410 syncer.finder = nil 411 412 if syncer.syncerCfg.debugContext != nil && syncer.syncerCfg.debugContext.debugFinder { 413 return nil 414 } 415 416 syncer.blockFetcher = newBlockFetcher(syncer.ctx, syncer.getCompRequester(), syncer.syncerCfg) 417 syncer.hashFetcher = newHashFetcher(syncer.ctx, syncer.getCompRequester(), syncer.blockFetcher.hfCh, syncer.syncerCfg) 418 419 syncer.blockFetcher.Start() 420 syncer.hashFetcher.Start() 421 422 return nil 423 } 424 425 func (syncer *Syncer) Statistics() *map[string]interface{} { 426 var start, end, total, added, blockfetched uint64 427 428 if syncer.ctx != nil { 429 end = syncer.ctx.TargetNo 430 if syncer.ctx.CommonAncestor != nil { 431 total = syncer.ctx.TotalCnt 432 start = syncer.ctx.CommonAncestor.BlockNo() 433 } 434 } else { 435 return &map[string]interface{}{ 436 "running": syncer.isRunning, 437 "total": total, 438 "start": start, 439 "end": end, 440 "block_added": added, 441 "block_fetched": blockfetched, 442 } 443 } 444 445 if syncer.blockFetcher != nil { 446 lastblock := syncer.blockFetcher.stat.getLastAddBlock() 447 added = lastblock.BlockNo() 448 if syncer.blockFetcher.stat.getMaxChunkRsp() != nil { 449 blockfetched = syncer.blockFetcher.stat.getMaxChunkRsp().BlockNo() 450 } 451 } 452 453 return &map[string]interface{}{ 454 "running": syncer.isRunning, 455 "total": total, 456 "start": start, 457 "end": end, 458 "block_added": added, 459 "block_fetched": blockfetched, 460 } 461 } 462 463 func (syncer *Syncer) RecoverSyncerSelf() { 464 if r := recover(); r != nil { 465 logger.Error().Str("dest", "SYNCER").Str("callstack", string(debug.Stack())).Msg("syncer recovered it's panic") 466 syncer.Reset(ErrSyncerPanic) 467 } 468 } 469 470 func stopSyncer(compRequester component.IComponentRequester, seq uint64, who string, err error) { 471 logger.Info().Str("who", who).Err(err).Msg("request syncer stop") 472 473 compRequester.TellTo(message.SyncerSvc, &message.SyncStop{Seq: seq, FromWho: who, Err: err}) 474 } 475 476 func closeFetcher(compRequester component.IComponentRequester, seq uint64, who string) { 477 compRequester.TellTo(message.SyncerSvc, &message.CloseFetcher{Seq: seq, FromWho: who}) 478 } 479 480 func RecoverSyncer(name string, seq uint64, compRequester component.IComponentRequester, finalize func()) { 481 if r := recover(); r != nil { 482 logger.Error().Str("child", name).Str("callstack", string(debug.Stack())).Msg("syncer recovered child panic") 483 stopSyncer(compRequester, seq, name, ErrSyncerPanic) 484 } 485 486 if finalize != nil { 487 finalize() 488 } 489 }