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  }