| 53 | } |
| 54 | |
| 55 | func (p *picker) Pick(info balancer.PickInfo) (balancer.PickResult, error) { |
| 56 | usingRandomHash := false |
| 57 | var requestHash uint64 |
| 58 | if p.requestHashHeader == "" { |
| 59 | var ok bool |
| 60 | if requestHash, ok = iringhash.XDSRequestHash(info.Ctx); !ok { |
| 61 | return balancer.PickResult{}, fmt.Errorf("ringhash: expected xDS config selector to set the request hash") |
| 62 | } |
| 63 | } else { |
| 64 | md, ok := metadata.FromOutgoingContext(info.Ctx) |
| 65 | if !ok || len(md.Get(p.requestHashHeader)) == 0 { |
| 66 | requestHash = p.randUint64() |
| 67 | usingRandomHash = true |
| 68 | } else { |
| 69 | values := strings.Join(md.Get(p.requestHashHeader), ",") |
| 70 | requestHash = xxhash.Sum64String(values) |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | e := p.ring.pick(requestHash) |
| 75 | ringSize := len(p.ring.items) |
| 76 | if !usingRandomHash { |
| 77 | // Per gRFC A61, because of sticky-TF with PickFirst's auto reconnect on TF, |
| 78 | // we ignore all TF subchannels and find the first ring entry in READY, |
| 79 | // CONNECTING or IDLE. If that entry is in IDLE, we need to initiate a |
| 80 | // connection. The idlePicker returned by the LazyLB or the new Pickfirst |
| 81 | // should do this automatically. |
| 82 | for i := 0; i < ringSize; i++ { |
| 83 | index := (e.idx + i) % ringSize |
| 84 | es := p.endpointState(p.ring.items[index]) |
| 85 | switch es.state.ConnectivityState { |
| 86 | case connectivity.Ready, connectivity.Connecting, connectivity.Idle: |
| 87 | return es.state.Picker.Pick(info) |
| 88 | case connectivity.TransientFailure: |
| 89 | default: |
| 90 | panic(fmt.Sprintf("Found child balancer in unknown state: %v", es.state.ConnectivityState)) |
| 91 | } |
| 92 | } |
| 93 | } else { |
| 94 | // If the picker has generated a random hash, it will walk the ring from |
| 95 | // this hash, and pick the first READY endpoint. If no endpoint is |
| 96 | // currently in CONNECTING state, it will trigger a connection attempt |
| 97 | // on at most one endpoint that is in IDLE state along the way. - A76 |
| 98 | requestedConnection := p.hasEndpointInConnectingState |
| 99 | for i := 0; i < ringSize; i++ { |
| 100 | index := (e.idx + i) % ringSize |
| 101 | es := p.endpointState(p.ring.items[index]) |
| 102 | if es.state.ConnectivityState == connectivity.Ready { |
| 103 | return es.state.Picker.Pick(info) |
| 104 | } |
| 105 | if !requestedConnection && es.state.ConnectivityState == connectivity.Idle { |
| 106 | requestedConnection = true |
| 107 | // If the SubChannel is in idle state, initiate a connection but |
| 108 | // continue to check other pickers to see if there is one in |
| 109 | // ready state. |
| 110 | es.balancer.ExitIdle() |
| 111 | } |
| 112 | } |