@ -15,6 +15,11 @@ import (
"github.com/tendermint/tendermint/types"
)
const (
// chBufferSize is the buffer size of all event channels.
chBufferSize int = 1000
)
//-------------------------------------
type bcBlockRequestMessage struct {
@ -129,15 +134,16 @@ type blockStore interface {
type BlockchainReactor struct {
p2p . BaseReactor
fastSync bool // if true, enable fast sync on start
events chan Event // XXX: Rename eventsFromPeers
scheduler * Routine
processor * Routine
logger log . Logger
fastSync bool // if true, enable fast sync on start
stateSynced bool // set to true when SwitchToFastSync is called by state sync
scheduler * Routine
processor * Routine
logger log . Logger
mtx sync . RWMutex
maxPeerHeight int64
syncHeight int64
events chan Event // non-nil during a fast sync
reporter behaviour . Reporter
io iIO
@ -154,9 +160,8 @@ type blockApplier interface {
}
// XXX: unify naming in this package around tmState
// XXX: V1 stores a copy of state as initialState, which is never mutated. Is that nessesary?
func newReactor ( state state . State , store blockStore , reporter behaviour . Reporter ,
blockApplier blockApplier , bufferSize int , fastSync bool ) * BlockchainReactor {
blockApplier blockApplier , fastSync bool ) * BlockchainReactor {
scheduler := newScheduler ( state . LastBlockHeight , time . Now ( ) )
pContext := newProcessorContext ( store , blockApplier , state )
// TODO: Fix naming to just newProcesssor
@ -164,9 +169,8 @@ func newReactor(state state.State, store blockStore, reporter behaviour.Reporter
processor := newPcState ( pContext )
return & BlockchainReactor {
events : make ( chan Event , bufferSize ) ,
scheduler : newRoutine ( "scheduler" , scheduler . handle , bufferSize ) ,
processor : newRoutine ( "processor" , processor . handle , bufferSize ) ,
scheduler : newRoutine ( "scheduler" , scheduler . handle , chBufferSize ) ,
processor : newRoutine ( "processor" , processor . handle , chBufferSize ) ,
store : store ,
reporter : reporter ,
logger : log . NewNopLogger ( ) ,
@ -181,7 +185,7 @@ func NewBlockchainReactor(
store blockStore ,
fastSync bool ) * BlockchainReactor {
reporter := behaviour . NewMockReporter ( )
return newReactor ( state , store , reporter , blockApplier , 1000 , fastSync )
return newReactor ( state , store , reporter , blockApplier , fastSync )
}
// SetSwitch implements Reactor interface.
@ -226,13 +230,54 @@ func (r *BlockchainReactor) SetLogger(logger log.Logger) {
func ( r * BlockchainReactor ) Start ( ) error {
r . reporter = behaviour . NewSwitchReporter ( r . BaseReactor . Switch )
if r . fastSync {
go r . scheduler . start ( )
go r . processor . start ( )
go r . demux ( )
err := r . startSync ( nil )
if err != nil {
return fmt . Errorf ( "failed to start fast sync: %w" , err )
}
}
return nil
}
// startSync begins a fast sync, signalled by r.events being non-nil. If state is non-nil,
// the scheduler and processor is updated with this state on startup.
func ( r * BlockchainReactor ) startSync ( state * state . State ) error {
r . mtx . Lock ( )
defer r . mtx . Unlock ( )
if r . events != nil {
return errors . New ( "fast sync already in progress" )
}
r . events = make ( chan Event , chBufferSize )
go r . scheduler . start ( )
go r . processor . start ( )
if state != nil {
<- r . scheduler . ready ( )
<- r . processor . ready ( )
r . scheduler . send ( bcResetState { state : * state } )
r . processor . send ( bcResetState { state : * state } )
}
go r . demux ( r . events )
return nil
}
// endSync ends a fast sync
func ( r * BlockchainReactor ) endSync ( ) {
r . mtx . Lock ( )
defer r . mtx . Unlock ( )
if r . events != nil {
close ( r . events )
}
r . events = nil
r . scheduler . stop ( )
r . processor . stop ( )
}
// SwitchToFastSync is called by the state sync reactor when switching to fast sync.
func ( r * BlockchainReactor ) SwitchToFastSync ( state state . State ) error {
r . stateSynced = true
state = state . Copy ( )
return r . startSync ( & state )
}
// reactor generated ticker events:
// ticker for cleaning peers
type rTryPrunePeer struct {
@ -299,7 +344,14 @@ type bcRemovePeer struct {
reason interface { }
}
func ( r * BlockchainReactor ) demux ( ) {
// resets the scheduler and processor state, e.g. following a switch from state syncing
type bcResetState struct {
priorityHigh
state state . State
}
// Takes the channel as a parameter to avoid race conditions on r.events.
func ( r * BlockchainReactor ) demux ( events <- chan Event ) {
var lastRate = 0.0
var lastHundred = time . Now ( )
@ -330,6 +382,7 @@ func (r *BlockchainReactor) demux() {
doStatusTk = time . NewTicker ( statusFreq )
)
defer doStatusTk . Stop ( )
doStatusCh <- struct { } { } // immediately broadcast to get status of existing peers
// XXX: Extract timers to make testing atemporal
for {
@ -367,7 +420,7 @@ func (r *BlockchainReactor) demux() {
r . io . broadcastStatusRequest ( r . store . Base ( ) , r . SyncHeight ( ) )
// Events from peers. Closing the channel signals event loop termination.
case event , ok := <- r . events :
case event , ok := <- events :
if ! ok {
r . logger . Info ( "Stopping event processing" )
return
@ -379,10 +432,10 @@ func (r *BlockchainReactor) demux() {
case bcAddNewPeer , bcRemovePeer , bcBlockResponse , bcNoBlockResponse :
r . scheduler . send ( event )
default :
r . logger . Error ( "Received unknown event" , "event" , fmt . Sprintf ( "%T" , event ) )
r . logger . Error ( "Received unexpected event" , "event" , fmt . Sprintf ( "%T" , event ) )
}
// Incremental events fo rm scheduler
// Incremental events fro m scheduler
case event := <- r . scheduler . next ( ) :
switch event := event . ( type ) {
case scBlockReceived :
@ -395,9 +448,13 @@ func (r *BlockchainReactor) demux() {
case scFinishedEv :
r . processor . send ( event )
r . scheduler . stop ( )
case scSchedulerFail :
r . logger . Error ( "Scheduler failure" , "err" , event . reason . Error ( ) )
case scPeersPruned :
r . logger . Debug ( "Pruned peers" , "count" , len ( event . peers ) )
case noOpEvent :
default :
r . logger . Error ( "Received unknown scheduler event" , "event" , fmt . Sprintf ( "%T" , event ) )
r . logger . Error ( "Received unexpected scheduler event" , "event" , fmt . Sprintf ( "%T" , event ) )
}
// Incremental events from processor
@ -407,7 +464,7 @@ func (r *BlockchainReactor) demux() {
r . setSyncHeight ( event . height )
if r . syncHeight % 100 == 0 {
lastRate = 0.9 * lastRate + 0.1 * ( 100 / time . Since ( lastHundred ) . Seconds ( ) )
r . logger . Info ( "Fast Syncc Rate" , "height" , r . syncHeight ,
r . logger . Info ( "Fast Sync Rate" , "height" , r . syncHeight ,
"max_peer_height" , r . maxPeerHeight , "blocks/s" , lastRate )
lastHundred = time . Now ( )
}
@ -415,11 +472,15 @@ func (r *BlockchainReactor) demux() {
case pcBlockVerificationFailure :
r . scheduler . send ( event )
case pcFinished :
r . io . trySwitchToConsensus ( event . tmState , event . blocksSynced > 0 )
r . processor . stop ( )
r . logger . Info ( "Fast sync complete, switching to consensus" )
if ! r . io . trySwitchToConsensus ( event . tmState , event . blocksSynced > 0 || r . stateSynced ) {
r . logger . Error ( "Failed to switch to consensus reactor" )
}
r . endSync ( )
return
case noOpEvent :
default :
r . logger . Error ( "Received unknown processor event" , "event" , fmt . Sprintf ( "%T" , event ) )
r . logger . Error ( "Received unexpected processor event" , "event" , fmt . Sprintf ( "%T" , event ) )
}
// Terminal event from scheduler
@ -446,11 +507,7 @@ func (r *BlockchainReactor) demux() {
// Stop implements cmn.Service interface.
func ( r * BlockchainReactor ) Stop ( ) error {
r . logger . Info ( "reactor stopping" )
r . scheduler . stop ( )
r . processor . stop ( )
close ( r . events )
r . endSync ( )
r . logger . Info ( "reactor stopped" )
return nil
}
@ -523,18 +580,30 @@ func (r *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
}
case * bcStatusResponseMessage :
r . events <- bcStatusResponse { peerID : src . ID ( ) , base : msg . Base , height : msg . Height }
r . mtx . RLock ( )
if r . events != nil {
r . events <- bcStatusResponse { peerID : src . ID ( ) , base : msg . Base , height : msg . Height }
}
r . mtx . RUnlock ( )
case * bcBlockResponseMessage :
r . events <- bcBlockResponse {
peerID : src . ID ( ) ,
block : msg . Block ,
size : int64 ( len ( msgBytes ) ) ,
time : time . Now ( ) ,
r . mtx . RLock ( )
if r . events != nil {
r . events <- bcBlockResponse {
peerID : src . ID ( ) ,
block : msg . Block ,
size : int64 ( len ( msgBytes ) ) ,
time : time . Now ( ) ,
}
}
r . mtx . RUnlock ( )
case * bcNoBlockResponseMessage :
r . events <- bcNoBlockResponse { peerID : src . ID ( ) , height : msg . Height , time : time . Now ( ) }
r . mtx . RLock ( )
if r . events != nil {
r . events <- bcNoBlockResponse { peerID : src . ID ( ) , height : msg . Height , time : time . Now ( ) }
}
r . mtx . RUnlock ( )
}
}
@ -544,16 +613,23 @@ func (r *BlockchainReactor) AddPeer(peer p2p.Peer) {
if err != nil {
r . logger . Error ( "Could not send status message to peer new" , "src" , peer . ID , "height" , r . SyncHeight ( ) )
}
r . events <- bcAddNewPeer { peerID : peer . ID ( ) }
r . mtx . RLock ( )
defer r . mtx . RUnlock ( )
if r . events != nil {
r . events <- bcAddNewPeer { peerID : peer . ID ( ) }
}
}
// RemovePeer implements Reactor interface.
func ( r * BlockchainReactor ) RemovePeer ( peer p2p . Peer , reason interface { } ) {
event := bcRemovePeer {
peerID : peer . ID ( ) ,
reason : reason ,
r . mtx . RLock ( )
defer r . mtx . RUnlock ( )
if r . events != nil {
r . events <- bcRemovePeer {
peerID : peer . ID ( ) ,
reason : reason ,
}
}
r . events <- event
}
// GetChannels implements Reactor