github.phpd.cn/hashicorp/consul@v1.4.5/agent/consul/rtt.go (about)

     1  package consul
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  
     7  	"github.com/hashicorp/consul/agent/structs"
     8  	"github.com/hashicorp/consul/lib"
     9  )
    10  
    11  // nodeSorter takes a list of nodes and a parallel vector of distances and
    12  // implements sort.Interface, keeping both structures coherent and sorting by
    13  // distance.
    14  type nodeSorter struct {
    15  	Nodes structs.Nodes
    16  	Vec   []float64
    17  }
    18  
    19  // newNodeSorter returns a new sorter for the given source coordinate and set of
    20  // nodes.
    21  func (s *Server) newNodeSorter(cs lib.CoordinateSet, nodes structs.Nodes) (sort.Interface, error) {
    22  	state := s.fsm.State()
    23  	vec := make([]float64, len(nodes))
    24  	for i, node := range nodes {
    25  		_, other, err := state.Coordinate(node.Node, nil)
    26  		if err != nil {
    27  			return nil, err
    28  		}
    29  		c1, c2 := cs.Intersect(other)
    30  		vec[i] = lib.ComputeDistance(c1, c2)
    31  	}
    32  	return &nodeSorter{nodes, vec}, nil
    33  }
    34  
    35  // See sort.Interface.
    36  func (n *nodeSorter) Len() int {
    37  	return len(n.Nodes)
    38  }
    39  
    40  // See sort.Interface.
    41  func (n *nodeSorter) Swap(i, j int) {
    42  	n.Nodes[i], n.Nodes[j] = n.Nodes[j], n.Nodes[i]
    43  	n.Vec[i], n.Vec[j] = n.Vec[j], n.Vec[i]
    44  }
    45  
    46  // See sort.Interface.
    47  func (n *nodeSorter) Less(i, j int) bool {
    48  	return n.Vec[i] < n.Vec[j]
    49  }
    50  
    51  // serviceNodeSorter takes a list of service nodes and a parallel vector of
    52  // distances and implements sort.Interface, keeping both structures coherent and
    53  // sorting by distance.
    54  type serviceNodeSorter struct {
    55  	Nodes structs.ServiceNodes
    56  	Vec   []float64
    57  }
    58  
    59  // newServiceNodeSorter returns a new sorter for the given source coordinate and
    60  // set of service nodes.
    61  func (s *Server) newServiceNodeSorter(cs lib.CoordinateSet, nodes structs.ServiceNodes) (sort.Interface, error) {
    62  	state := s.fsm.State()
    63  	vec := make([]float64, len(nodes))
    64  	for i, node := range nodes {
    65  		_, other, err := state.Coordinate(node.Node, nil)
    66  		if err != nil {
    67  			return nil, err
    68  		}
    69  		c1, c2 := cs.Intersect(other)
    70  		vec[i] = lib.ComputeDistance(c1, c2)
    71  	}
    72  	return &serviceNodeSorter{nodes, vec}, nil
    73  }
    74  
    75  // See sort.Interface.
    76  func (n *serviceNodeSorter) Len() int {
    77  	return len(n.Nodes)
    78  }
    79  
    80  // See sort.Interface.
    81  func (n *serviceNodeSorter) Swap(i, j int) {
    82  	n.Nodes[i], n.Nodes[j] = n.Nodes[j], n.Nodes[i]
    83  	n.Vec[i], n.Vec[j] = n.Vec[j], n.Vec[i]
    84  }
    85  
    86  // See sort.Interface.
    87  func (n *serviceNodeSorter) Less(i, j int) bool {
    88  	return n.Vec[i] < n.Vec[j]
    89  }
    90  
    91  // serviceNodeSorter takes a list of health checks and a parallel vector of
    92  // distances and implements sort.Interface, keeping both structures coherent and
    93  // sorting by distance.
    94  type healthCheckSorter struct {
    95  	Checks structs.HealthChecks
    96  	Vec    []float64
    97  }
    98  
    99  // newHealthCheckSorter returns a new sorter for the given source coordinate and
   100  // set of health checks with nodes.
   101  func (s *Server) newHealthCheckSorter(cs lib.CoordinateSet, checks structs.HealthChecks) (sort.Interface, error) {
   102  	state := s.fsm.State()
   103  	vec := make([]float64, len(checks))
   104  	for i, check := range checks {
   105  		_, other, err := state.Coordinate(check.Node, nil)
   106  		if err != nil {
   107  			return nil, err
   108  		}
   109  		c1, c2 := cs.Intersect(other)
   110  		vec[i] = lib.ComputeDistance(c1, c2)
   111  	}
   112  	return &healthCheckSorter{checks, vec}, nil
   113  }
   114  
   115  // See sort.Interface.
   116  func (n *healthCheckSorter) Len() int {
   117  	return len(n.Checks)
   118  }
   119  
   120  // See sort.Interface.
   121  func (n *healthCheckSorter) Swap(i, j int) {
   122  	n.Checks[i], n.Checks[j] = n.Checks[j], n.Checks[i]
   123  	n.Vec[i], n.Vec[j] = n.Vec[j], n.Vec[i]
   124  }
   125  
   126  // See sort.Interface.
   127  func (n *healthCheckSorter) Less(i, j int) bool {
   128  	return n.Vec[i] < n.Vec[j]
   129  }
   130  
   131  // checkServiceNodeSorter takes a list of service nodes and a parallel vector of
   132  // distances and implements sort.Interface, keeping both structures coherent and
   133  // sorting by distance.
   134  type checkServiceNodeSorter struct {
   135  	Nodes structs.CheckServiceNodes
   136  	Vec   []float64
   137  }
   138  
   139  // newCheckServiceNodeSorter returns a new sorter for the given source coordinate
   140  // and set of nodes with health checks.
   141  func (s *Server) newCheckServiceNodeSorter(cs lib.CoordinateSet, nodes structs.CheckServiceNodes) (sort.Interface, error) {
   142  	state := s.fsm.State()
   143  	vec := make([]float64, len(nodes))
   144  	for i, node := range nodes {
   145  		_, other, err := state.Coordinate(node.Node.Node, nil)
   146  		if err != nil {
   147  			return nil, err
   148  		}
   149  		c1, c2 := cs.Intersect(other)
   150  		vec[i] = lib.ComputeDistance(c1, c2)
   151  	}
   152  	return &checkServiceNodeSorter{nodes, vec}, nil
   153  }
   154  
   155  // See sort.Interface.
   156  func (n *checkServiceNodeSorter) Len() int {
   157  	return len(n.Nodes)
   158  }
   159  
   160  // See sort.Interface.
   161  func (n *checkServiceNodeSorter) Swap(i, j int) {
   162  	n.Nodes[i], n.Nodes[j] = n.Nodes[j], n.Nodes[i]
   163  	n.Vec[i], n.Vec[j] = n.Vec[j], n.Vec[i]
   164  }
   165  
   166  // See sort.Interface.
   167  func (n *checkServiceNodeSorter) Less(i, j int) bool {
   168  	return n.Vec[i] < n.Vec[j]
   169  }
   170  
   171  // newSorterByDistanceFrom returns a sorter for the given type.
   172  func (s *Server) newSorterByDistanceFrom(cs lib.CoordinateSet, subj interface{}) (sort.Interface, error) {
   173  	switch v := subj.(type) {
   174  	case structs.Nodes:
   175  		return s.newNodeSorter(cs, v)
   176  	case structs.ServiceNodes:
   177  		return s.newServiceNodeSorter(cs, v)
   178  	case structs.HealthChecks:
   179  		return s.newHealthCheckSorter(cs, v)
   180  	case structs.CheckServiceNodes:
   181  		return s.newCheckServiceNodeSorter(cs, v)
   182  	default:
   183  		panic(fmt.Errorf("Unhandled type passed to newSorterByDistanceFrom: %#v", subj))
   184  	}
   185  }
   186  
   187  // sortNodesByDistanceFrom is used to sort results from our service catalog based
   188  // on the round trip time from the given source node. Nodes with missing coordinates
   189  // will get stable sorted at the end of the list.
   190  //
   191  // If coordinates are disabled this will be a no-op.
   192  func (s *Server) sortNodesByDistanceFrom(source structs.QuerySource, subj interface{}) error {
   193  	// We can't sort if there's no source node.
   194  	if source.Node == "" {
   195  		return nil
   196  	}
   197  
   198  	// We can't compare coordinates across DCs.
   199  	if source.Datacenter != s.config.Datacenter {
   200  		return nil
   201  	}
   202  
   203  	// There won't always be coordinates for the source node. If there are
   204  	// none then we can bail out because there's no meaning for the sort.
   205  	state := s.fsm.State()
   206  	_, cs, err := state.Coordinate(source.Node, nil)
   207  	if err != nil {
   208  		return err
   209  	}
   210  	if len(cs) == 0 {
   211  		return nil
   212  	}
   213  
   214  	// Do the sort!
   215  	sorter, err := s.newSorterByDistanceFrom(cs, subj)
   216  	if err != nil {
   217  		return err
   218  	}
   219  	sort.Stable(sorter)
   220  	return nil
   221  }