-
Notifications
You must be signed in to change notification settings - Fork 176
/
follower_loop.go
91 lines (80 loc) · 2.81 KB
/
follower_loop.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package hotstuff
import (
"time"
"github.com/rs/zerolog"
"github.com/onflow/flow-go/consensus/hotstuff/model"
"github.com/onflow/flow-go/consensus/hotstuff/runner"
"github.com/onflow/flow-go/model/flow"
"github.com/onflow/flow-go/utils/logging"
)
// FollowerLoop implements interface FollowerLoop
type FollowerLoop struct {
log zerolog.Logger
followerLogic FollowerLogic
proposals chan *model.Proposal
runner runner.SingleRunner // lock for preventing concurrent state transitions
}
// NewFollowerLoop creates an instance of EventLoop
func NewFollowerLoop(log zerolog.Logger, followerLogic FollowerLogic) (*FollowerLoop, error) {
return &FollowerLoop{
log: log,
followerLogic: followerLogic,
proposals: make(chan *model.Proposal),
runner: runner.NewSingleRunner(),
}, nil
}
// SubmitProposal feeds a new block proposal (header) into the FollowerLoop.
// This method blocks until the proposal is accepted to the event queue.
//
// Block proposals must be submitted in order, i.e. a proposal's parent must
// have been previously processed by the FollowerLoop.
func (fl *FollowerLoop) SubmitProposal(proposalHeader *flow.Header, parentView uint64) {
received := time.Now()
proposal := model.ProposalFromFlow(proposalHeader, parentView)
fl.proposals <- proposal
// the busy duration is measured as how long it takes from a block being
// received to a block being handled by the event handler.
busyDuration := time.Since(received)
fl.log.Debug().Hex("block_id", logging.ID(proposal.Block.BlockID)).
Uint64("view", proposal.Block.View).
Dur("busy_duration", busyDuration).
Msg("busy duration to handle a proposal")
}
// loop will synchronously processes all events.
// All errors from FollowerLogic are fatal:
// * known critical error: some prerequisites of the HotStuff follower have been broken
// * unknown critical error: bug-related
func (fl *FollowerLoop) loop() {
shutdownSignal := fl.runner.ShutdownSignal()
for {
select { // to ensure we are not skipping over a termination signal
case <-shutdownSignal:
return
default:
}
select {
case p := <-fl.proposals:
err := fl.followerLogic.AddBlock(p)
if err != nil { // all errors are fatal
fl.log.Error().
Hex("block_id", logging.ID(p.Block.BlockID)).
Uint64("view", p.Block.View).
Err(err).
Msg("terminating FollowerLoop")
return
}
case <-shutdownSignal:
return
}
}
}
// Ready implements interface module.ReadyDoneAware
// Method call will starts the FollowerLoop's internal processing loop.
// Multiple calls are handled gracefully and the follower will only start once.
func (fl *FollowerLoop) Ready() <-chan struct{} {
return fl.runner.Start(fl.loop)
}
// Done implements interface module.ReadyDoneAware
func (fl *FollowerLoop) Done() <-chan struct{} {
return fl.runner.Abort()
}