You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

166 lines
4.0 KiB

blockchain: add v2 reactor (#4361) The work includes the reactor which ties together all the seperate routines involved in the design of the blockchain v2 refactor. This PR replaces #4067 which got far too large and messy after a failed attempt to rebase. ## Commits: * Blockchainv 2 reactor: + I cleaner copy of the work done in #4067 which fell too far behind and was a nightmare to rebase. + The work includes the reactor which ties together all the seperate routines involved in the design of the blockchain v2 refactor. * fixes after merge * reorder iIO interface methodset * change iO -> IO * panic before send nil block * rename switchToConsensus -> trySwitchToConsensus * rename tdState -> tmState * Update blockchain/v2/reactor.go Co-Authored-By: Bot from GolangCI <42910462+golangcibot@users.noreply.github.com> * remove peer when it sends a block unsolicited * check for not ready in markReceived * fix error * fix the pcFinished event * typo fix * add documentation for processor fields * simplify time.Since * try and make the linter happy * some doc updates * fix channel diagram * Update adr-043-blockchain-riri-org.md * panic on nil switch * liting fixes * account for nil block in bBlockResponseMessage * panic on duplicate block enqueued by processor * linting * goimport reactor_test.go Co-authored-by: Bot from GolangCI <42910462+golangcibot@users.noreply.github.com> Co-authored-by: Anca Zamfir <ancazamfir@users.noreply.github.com> Co-authored-by: Marko <marbar3778@yahoo.com> Co-authored-by: Anton Kaliaev <anton.kalyaev@gmail.com>
5 years ago
5 years ago
Blockchain v2 Scheduler (#4043) * Add processor prototype * Change processor API + expose a simple `handle` function which mutates internal state * schedule event handling * rename schedule -> scheduler * fill in handle function * processor tests * fix gofmt and ohter golangci issues * scopelint var on range scope * add check for short block received * small test reorg * ci fix changes * go.mod revert * some cleanup and review comments * scheduler fixes and unit tests, also small processor changes. changed scPeerPruned to include a list of pruned peers touchPeer to check peer state and remove the blocks from blockStates if the peer removal causes the max peer height to be lower. remove the block at sc.initHeight changed peersInactiveSince, peersSlowerThan, getPeersAtHeight check peer state prunablePeers to return a sorted list of peers lastRate in markReceived() attempted to divide by 0, temp fix. fixed allBlocksProcessed conditions maxHeight() and minHeight() to return sc.initHeight if no ready peers present make selectPeer() deterministic. added handleBlockProcessError() added termination cond. (sc.allBlocksProcessed()) to handleTryPrunePeer() and others. changed pcBlockVerificationFailure to include peer of H+2 block along with the one for H+1 changed the processor to call purgePeer on block verification failure. fixed processor tests added scheduler tests. * typo and ci fixes * remove height from scBlockRequest, golangci fixes * limit on blockState map, updated tests * remove unused * separate test for maxHeight(), used for sched. validation * use Math.Min * fix golangci * Document the semantics of blockStates in the scheduler * better docs * distinguish between unknown and invalid blockstate * Standardize peer filtering methods * feedback * s/getPeersAtHeight/getPeersAtHeightOrAbove * small notes * Update blockchain/v2/scheduler.go Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * Update comments based on feedback * Add enum offset * panic on nil block in processor * remove unused max height calculation * format shorter line
5 years ago
5 years ago
5 years ago
5 years ago
blockchain: add v2 reactor (#4361) The work includes the reactor which ties together all the seperate routines involved in the design of the blockchain v2 refactor. This PR replaces #4067 which got far too large and messy after a failed attempt to rebase. ## Commits: * Blockchainv 2 reactor: + I cleaner copy of the work done in #4067 which fell too far behind and was a nightmare to rebase. + The work includes the reactor which ties together all the seperate routines involved in the design of the blockchain v2 refactor. * fixes after merge * reorder iIO interface methodset * change iO -> IO * panic before send nil block * rename switchToConsensus -> trySwitchToConsensus * rename tdState -> tmState * Update blockchain/v2/reactor.go Co-Authored-By: Bot from GolangCI <42910462+golangcibot@users.noreply.github.com> * remove peer when it sends a block unsolicited * check for not ready in markReceived * fix error * fix the pcFinished event * typo fix * add documentation for processor fields * simplify time.Since * try and make the linter happy * some doc updates * fix channel diagram * Update adr-043-blockchain-riri-org.md * panic on nil switch * liting fixes * account for nil block in bBlockResponseMessage * panic on duplicate block enqueued by processor * linting * goimport reactor_test.go Co-authored-by: Bot from GolangCI <42910462+golangcibot@users.noreply.github.com> Co-authored-by: Anca Zamfir <ancazamfir@users.noreply.github.com> Co-authored-by: Marko <marbar3778@yahoo.com> Co-authored-by: Anton Kaliaev <anton.kalyaev@gmail.com>
5 years ago
Blockchain v2 Scheduler (#4043) * Add processor prototype * Change processor API + expose a simple `handle` function which mutates internal state * schedule event handling * rename schedule -> scheduler * fill in handle function * processor tests * fix gofmt and ohter golangci issues * scopelint var on range scope * add check for short block received * small test reorg * ci fix changes * go.mod revert * some cleanup and review comments * scheduler fixes and unit tests, also small processor changes. changed scPeerPruned to include a list of pruned peers touchPeer to check peer state and remove the blocks from blockStates if the peer removal causes the max peer height to be lower. remove the block at sc.initHeight changed peersInactiveSince, peersSlowerThan, getPeersAtHeight check peer state prunablePeers to return a sorted list of peers lastRate in markReceived() attempted to divide by 0, temp fix. fixed allBlocksProcessed conditions maxHeight() and minHeight() to return sc.initHeight if no ready peers present make selectPeer() deterministic. added handleBlockProcessError() added termination cond. (sc.allBlocksProcessed()) to handleTryPrunePeer() and others. changed pcBlockVerificationFailure to include peer of H+2 block along with the one for H+1 changed the processor to call purgePeer on block verification failure. fixed processor tests added scheduler tests. * typo and ci fixes * remove height from scBlockRequest, golangci fixes * limit on blockState map, updated tests * remove unused * separate test for maxHeight(), used for sched. validation * use Math.Min * fix golangci * Document the semantics of blockStates in the scheduler * better docs * distinguish between unknown and invalid blockstate * Standardize peer filtering methods * feedback * s/getPeersAtHeight/getPeersAtHeightOrAbove * small notes * Update blockchain/v2/scheduler.go Co-Authored-By: Anton Kaliaev <anton.kalyaev@gmail.com> * Update comments based on feedback * Add enum offset * panic on nil block in processor * remove unused max height calculation * format shorter line
5 years ago
  1. package v2
  2. import (
  3. "fmt"
  4. "strings"
  5. "sync/atomic"
  6. "github.com/Workiva/go-datastructures/queue"
  7. "github.com/tendermint/tendermint/libs/log"
  8. )
  9. type handleFunc = func(event Event) (Event, error)
  10. const historySize = 25
  11. // Routine is a structure that models a finite state machine as serialized
  12. // stream of events processed by a handle function. This Routine structure
  13. // handles the concurrency and messaging guarantees. Events are sent via
  14. // `send` are handled by the `handle` function to produce an iterator
  15. // `next()`. Calling `stop()` on a routine will conclude processing of all
  16. // sent events and produce `final()` event representing the terminal state.
  17. type Routine struct {
  18. name string
  19. handle handleFunc
  20. queue *queue.PriorityQueue
  21. history []Event
  22. out chan Event
  23. fin chan error
  24. rdy chan struct{}
  25. running *uint32
  26. logger log.Logger
  27. metrics *Metrics
  28. }
  29. func newRoutine(name string, handleFunc handleFunc, bufferSize int) *Routine {
  30. return &Routine{
  31. name: name,
  32. handle: handleFunc,
  33. queue: queue.NewPriorityQueue(bufferSize, true),
  34. history: make([]Event, 0, historySize),
  35. out: make(chan Event, bufferSize),
  36. rdy: make(chan struct{}, 1),
  37. fin: make(chan error, 1),
  38. running: new(uint32),
  39. logger: log.NewNopLogger(),
  40. metrics: NopMetrics(),
  41. }
  42. }
  43. func (rt *Routine) setLogger(logger log.Logger) {
  44. rt.logger = logger
  45. }
  46. // nolint:unused
  47. func (rt *Routine) setMetrics(metrics *Metrics) {
  48. rt.metrics = metrics
  49. }
  50. func (rt *Routine) start() {
  51. rt.logger.Info(fmt.Sprintf("%s: run", rt.name))
  52. running := atomic.CompareAndSwapUint32(rt.running, uint32(0), uint32(1))
  53. if !running {
  54. panic(fmt.Sprintf("%s is already running", rt.name))
  55. }
  56. close(rt.rdy)
  57. defer func() {
  58. if r := recover(); r != nil {
  59. var (
  60. b strings.Builder
  61. j int
  62. )
  63. for i := len(rt.history) - 1; i >= 0; i-- {
  64. fmt.Fprintf(&b, "%d: %+v\n", j, rt.history[i])
  65. j++
  66. }
  67. panic(fmt.Sprintf("%v\nlast events:\n%v", r, b.String()))
  68. }
  69. stopped := atomic.CompareAndSwapUint32(rt.running, uint32(1), uint32(0))
  70. if !stopped {
  71. panic(fmt.Sprintf("%s is failed to stop", rt.name))
  72. }
  73. }()
  74. for {
  75. events, err := rt.queue.Get(1)
  76. if err == queue.ErrDisposed {
  77. rt.terminate(nil)
  78. return
  79. } else if err != nil {
  80. rt.terminate(err)
  81. return
  82. }
  83. oEvent, err := rt.handle(events[0].(Event))
  84. rt.metrics.EventsHandled.With("routine", rt.name).Add(1)
  85. if err != nil {
  86. rt.terminate(err)
  87. return
  88. }
  89. rt.metrics.EventsOut.With("routine", rt.name).Add(1)
  90. rt.logger.Debug(fmt.Sprintf("%s: produced %T %+v", rt.name, oEvent, oEvent))
  91. // Skip rTrySchedule and rProcessBlock events as they clutter the history
  92. // due to their frequency.
  93. switch events[0].(type) {
  94. case rTrySchedule:
  95. case rProcessBlock:
  96. default:
  97. rt.history = append(rt.history, events[0].(Event))
  98. if len(rt.history) > historySize {
  99. rt.history = rt.history[1:]
  100. }
  101. }
  102. rt.out <- oEvent
  103. }
  104. }
  105. // XXX: look into returning OpError in the net package
  106. func (rt *Routine) send(event Event) bool {
  107. rt.logger.Debug(fmt.Sprintf("%s: received %T %+v", rt.name, event, event))
  108. if !rt.isRunning() {
  109. return false
  110. }
  111. err := rt.queue.Put(event)
  112. if err != nil {
  113. rt.metrics.EventsShed.With("routine", rt.name).Add(1)
  114. rt.logger.Error(fmt.Sprintf("%s: send failed, queue was full/stopped", rt.name))
  115. return false
  116. }
  117. rt.metrics.EventsSent.With("routine", rt.name).Add(1)
  118. return true
  119. }
  120. func (rt *Routine) isRunning() bool {
  121. return atomic.LoadUint32(rt.running) == 1
  122. }
  123. func (rt *Routine) next() chan Event {
  124. return rt.out
  125. }
  126. func (rt *Routine) ready() chan struct{} {
  127. return rt.rdy
  128. }
  129. func (rt *Routine) stop() {
  130. if !rt.isRunning() { // XXX: this should check rt.queue.Disposed()
  131. return
  132. }
  133. rt.logger.Info(fmt.Sprintf("%s: stop", rt.name))
  134. rt.queue.Dispose() // this should block until all queue items are free?
  135. }
  136. func (rt *Routine) final() chan error {
  137. return rt.fin
  138. }
  139. // XXX: Maybe get rid of this
  140. func (rt *Routine) terminate(reason error) {
  141. // We don't close the rt.out channel here, to avoid spinning on the closed channel
  142. // in the event loop.
  143. rt.fin <- reason
  144. }