github.imxd.top/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 }