github.com/adnan-c/fabric_e2e_couchdb@v0.6.1-preview.0.20170228180935-21ce6b23cf91/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 func (ch *chain) main() { 83 var timer <-chan time.Time 84 85 for { 86 select { 87 case msg := <-ch.sendChan: 88 batches, committers, ok := ch.support.BlockCutter().Ordered(msg) 89 if ok && len(batches) == 0 && timer == nil { 90 timer = time.After(ch.batchTimeout) 91 continue 92 } 93 for i, batch := range batches { 94 block := ch.support.CreateNextBlock(batch) 95 ch.support.WriteBlock(block, committers[i], nil) 96 } 97 if len(batches) > 0 { 98 timer = nil 99 } 100 case <-timer: 101 //clear the timer 102 timer = nil 103 104 batch, committers := ch.support.BlockCutter().Cut() 105 if len(batch) == 0 { 106 logger.Warningf("Batch timer expired with no pending requests, this might indicate a bug") 107 continue 108 } 109 logger.Debugf("Batch timer expired, creating block") 110 block := ch.support.CreateNextBlock(batch) 111 ch.support.WriteBlock(block, committers, nil) 112 case <-ch.exitChan: 113 logger.Debugf("Exiting") 114 return 115 } 116 } 117 }