github.com/tenywen/fabric@v1.0.0-beta.0.20170620030522-a5b1ed380643/orderer/solo/consensus.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package solo 18 19 import ( 20 "time" 21 22 "github.com/hyperledger/fabric/orderer/multichain" 23 cb "github.com/hyperledger/fabric/protos/common" 24 "github.com/op/go-logging" 25 ) 26 27 var logger = logging.MustGetLogger("orderer/solo") 28 29 type consenter struct{} 30 31 type chain struct { 32 support multichain.ConsenterSupport 33 batchTimeout time.Duration 34 sendChan chan *cb.Envelope 35 exitChan chan struct{} 36 } 37 38 // New creates a new consenter for the solo consensus scheme. 39 // The solo consensus scheme is very simple, and allows only one consenter for a given chain (this process). 40 // It accepts messages being delivered via Enqueue, orders them, and then uses the blockcutter to form the messages 41 // into blocks before writing to the given ledger 42 func New() multichain.Consenter { 43 return &consenter{} 44 } 45 46 func (solo *consenter) HandleChain(support multichain.ConsenterSupport, metadata *cb.Metadata) (multichain.Chain, error) { 47 return newChain(support), nil 48 } 49 50 func newChain(support multichain.ConsenterSupport) *chain { 51 return &chain{ 52 batchTimeout: support.SharedConfig().BatchTimeout(), 53 support: support, 54 sendChan: make(chan *cb.Envelope), 55 exitChan: make(chan struct{}), 56 } 57 } 58 59 func (ch *chain) Start() { 60 go ch.main() 61 } 62 63 func (ch *chain) Halt() { 64 select { 65 case <-ch.exitChan: 66 // Allow multiple halts without panic 67 default: 68 close(ch.exitChan) 69 } 70 } 71 72 // Enqueue accepts a message and returns true on acceptance, or false on shutdown 73 func (ch *chain) Enqueue(env *cb.Envelope) bool { 74 select { 75 case ch.sendChan <- env: 76 return true 77 case <-ch.exitChan: 78 return false 79 } 80 } 81 82 // Errored only closes on exit 83 func (ch *chain) Errored() <-chan struct{} { 84 return ch.exitChan 85 } 86 87 func (ch *chain) main() { 88 var timer <-chan time.Time 89 90 for { 91 select { 92 case msg := <-ch.sendChan: 93 batches, committers, ok := ch.support.BlockCutter().Ordered(msg) 94 if ok && len(batches) == 0 && timer == nil { 95 timer = time.After(ch.batchTimeout) 96 continue 97 } 98 for i, batch := range batches { 99 block := ch.support.CreateNextBlock(batch) 100 ch.support.WriteBlock(block, committers[i], nil) 101 } 102 if len(batches) > 0 { 103 timer = nil 104 } 105 case <-timer: 106 //clear the timer 107 timer = nil 108 109 batch, committers := ch.support.BlockCutter().Cut() 110 if len(batch) == 0 { 111 logger.Warningf("Batch timer expired with no pending requests, this might indicate a bug") 112 continue 113 } 114 logger.Debugf("Batch timer expired, creating block") 115 block := ch.support.CreateNextBlock(batch) 116 ch.support.WriteBlock(block, committers, nil) 117 case <-ch.exitChan: 118 logger.Debugf("Exiting") 119 return 120 } 121 } 122 }