github.com/kchristidis/fabric@v1.0.4-0.20171028114726-837acd08cde1/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  	sendChan chan *cb.Envelope
    34  	exitChan chan struct{}
    35  }
    36  
    37  // New creates a new consenter for the solo consensus scheme.
    38  // The solo consensus scheme is very simple, and allows only one consenter for a given chain (this process).
    39  // It accepts messages being delivered via Enqueue, orders them, and then uses the blockcutter to form the messages
    40  // into blocks before writing to the given ledger
    41  func New() multichain.Consenter {
    42  	return &consenter{}
    43  }
    44  
    45  func (solo *consenter) HandleChain(support multichain.ConsenterSupport, metadata *cb.Metadata) (multichain.Chain, error) {
    46  	return newChain(support), nil
    47  }
    48  
    49  func newChain(support multichain.ConsenterSupport) *chain {
    50  	return &chain{
    51  		support:  support,
    52  		sendChan: make(chan *cb.Envelope),
    53  		exitChan: make(chan struct{}),
    54  	}
    55  }
    56  
    57  func (ch *chain) Start() {
    58  	go ch.main()
    59  }
    60  
    61  func (ch *chain) Halt() {
    62  	select {
    63  	case <-ch.exitChan:
    64  		// Allow multiple halts without panic
    65  	default:
    66  		close(ch.exitChan)
    67  	}
    68  }
    69  
    70  // Enqueue accepts a message and returns true on acceptance, or false on shutdown
    71  func (ch *chain) Enqueue(env *cb.Envelope) bool {
    72  	select {
    73  	case ch.sendChan <- env:
    74  		return true
    75  	case <-ch.exitChan:
    76  		return false
    77  	}
    78  }
    79  
    80  // Errored only closes on exit
    81  func (ch *chain) Errored() <-chan struct{} {
    82  	return ch.exitChan
    83  }
    84  
    85  func (ch *chain) main() {
    86  	var timer <-chan time.Time
    87  
    88  	for {
    89  		select {
    90  		case msg := <-ch.sendChan:
    91  			batches, committers, ok, _ := ch.support.BlockCutter().Ordered(msg)
    92  			if ok && len(batches) == 0 && timer == nil {
    93  				timer = time.After(ch.support.SharedConfig().BatchTimeout())
    94  				continue
    95  			}
    96  			for i, batch := range batches {
    97  				block := ch.support.CreateNextBlock(batch)
    98  				ch.support.WriteBlock(block, committers[i], nil)
    99  			}
   100  			if len(batches) > 0 {
   101  				timer = nil
   102  			}
   103  		case <-timer:
   104  			//clear the timer
   105  			timer = nil
   106  
   107  			batch, committers := ch.support.BlockCutter().Cut()
   108  			if len(batch) == 0 {
   109  				logger.Warningf("Batch timer expired with no pending requests, this might indicate a bug")
   110  				continue
   111  			}
   112  			logger.Debugf("Batch timer expired, creating block")
   113  			block := ch.support.CreateNextBlock(batch)
   114  			ch.support.WriteBlock(block, committers, nil)
   115  		case <-ch.exitChan:
   116  			logger.Debugf("Exiting")
   117  			return
   118  		}
   119  	}
   120  }