github.com/hyperledger-labs/bdls@v2.1.1+incompatible/discovery/client/selection.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package discovery
     8  
     9  import (
    10  	"math/rand"
    11  	"sort"
    12  	"time"
    13  
    14  	"github.com/hyperledger/fabric/gossip/protoext"
    15  )
    16  
    17  // Filter filters and sorts the given endorsers
    18  type Filter interface {
    19  	Filter(endorsers Endorsers) Endorsers
    20  }
    21  
    22  // ExclusionFilter returns true if the given Peer
    23  // is not to be considered when selecting peers
    24  type ExclusionFilter interface {
    25  	// Exclude returns whether the given Peer is to be excluded or not
    26  	Exclude(Peer) bool
    27  }
    28  
    29  type selectionFunc func(Peer) bool
    30  
    31  func (sf selectionFunc) Exclude(p Peer) bool {
    32  	return sf(p)
    33  }
    34  
    35  // PrioritySelector guides the selection of peers via
    36  // giving peers a relative priority to their selection
    37  type PrioritySelector interface {
    38  	// Compare compares between 2 peers and returns
    39  	// their relative scores
    40  	Compare(Peer, Peer) Priority
    41  }
    42  
    43  // Priority defines how likely a peer is to be selected
    44  // over another peer.
    45  // Positive priority means the left peer is selected
    46  // Negative priority means the right peer is selected
    47  // Zero priority means their priorities are the same
    48  type Priority int
    49  
    50  var (
    51  	// PrioritiesByHeight selects peers by descending height
    52  	PrioritiesByHeight = &byHeight{}
    53  	// NoExclusion accepts all peers and rejects no peers
    54  	NoExclusion = selectionFunc(noExclusion)
    55  	// NoPriorities is indifferent to how it selects peers
    56  	NoPriorities = &noPriorities{}
    57  )
    58  
    59  type noPriorities struct{}
    60  
    61  func (nc noPriorities) Compare(_ Peer, _ Peer) Priority {
    62  	return 0
    63  }
    64  
    65  type byHeight struct{}
    66  
    67  func (*byHeight) Compare(left Peer, right Peer) Priority {
    68  	leftHeight := left.StateInfoMessage.GetStateInfo().Properties.LedgerHeight
    69  	rightHeight := right.StateInfoMessage.GetStateInfo().Properties.LedgerHeight
    70  
    71  	if leftHeight > rightHeight {
    72  		return 1
    73  	}
    74  	if rightHeight > leftHeight {
    75  		return -1
    76  	}
    77  	return 0
    78  }
    79  
    80  func noExclusion(_ Peer) bool {
    81  	return false
    82  }
    83  
    84  // ExcludeHosts returns a ExclusionFilter that excludes the given endpoints
    85  func ExcludeHosts(endpoints ...string) ExclusionFilter {
    86  	m := make(map[string]struct{})
    87  	for _, endpoint := range endpoints {
    88  		m[endpoint] = struct{}{}
    89  	}
    90  	return ExcludeByHost(func(host string) bool {
    91  		_, excluded := m[host]
    92  		return excluded
    93  	})
    94  }
    95  
    96  // ExcludeByHost creates a ExclusionFilter out of the given exclusion predicate
    97  func ExcludeByHost(reject func(host string) bool) ExclusionFilter {
    98  	return selectionFunc(func(p Peer) bool {
    99  		endpoint := p.AliveMessage.GetAliveMsg().Membership.Endpoint
   100  		var internalEndpoint string
   101  		se := p.AliveMessage.GetSecretEnvelope()
   102  		if se != nil {
   103  			internalEndpoint = protoext.InternalEndpoint(se)
   104  		}
   105  		return reject(endpoint) || reject(internalEndpoint)
   106  	})
   107  }
   108  
   109  // Filter filters the endorsers according to the given ExclusionFilter
   110  func (endorsers Endorsers) Filter(f ExclusionFilter) Endorsers {
   111  	var res Endorsers
   112  	for _, e := range endorsers {
   113  		if !f.Exclude(*e) {
   114  			res = append(res, e)
   115  		}
   116  	}
   117  	return res
   118  }
   119  
   120  // Shuffle sorts the endorsers in random order
   121  func (endorsers Endorsers) Shuffle() Endorsers {
   122  	res := make(Endorsers, len(endorsers))
   123  	rand.Seed(time.Now().UnixNano())
   124  	for i, index := range rand.Perm(len(endorsers)) {
   125  		res[i] = endorsers[index]
   126  	}
   127  	return res
   128  }
   129  
   130  type endorserSort struct {
   131  	Endorsers
   132  	PrioritySelector
   133  }
   134  
   135  // Sort sorts the endorsers according to the given PrioritySelector
   136  func (endorsers Endorsers) Sort(ps PrioritySelector) Endorsers {
   137  	sort.Sort(&endorserSort{
   138  		Endorsers:        endorsers,
   139  		PrioritySelector: ps,
   140  	})
   141  	return endorsers
   142  }
   143  
   144  func (es *endorserSort) Len() int {
   145  	return len(es.Endorsers)
   146  }
   147  
   148  func (es *endorserSort) Less(i, j int) bool {
   149  	e1 := es.Endorsers[i]
   150  	e2 := es.Endorsers[j]
   151  	less := es.Compare(*e1, *e2)
   152  	return less > Priority(0)
   153  }
   154  
   155  func (es *endorserSort) Swap(i, j int) {
   156  	es.Endorsers[i], es.Endorsers[j] = es.Endorsers[j], es.Endorsers[i]
   157  }