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  }