pick returns the transport that will be used for the RPC. It may block in the following cases: - there's no picker - the current picker returns ErrNoSubConnAvailable - the current picker returns other errors and failfast is false. - the subConn returned by the current picker is not READY When one of
(ctx context.Context, failfast bool, info balancer.PickInfo)
| 103 | // - the subConn returned by the current picker is not READY |
| 104 | // When one of these situations happens, pick blocks until the picker gets updated. |
| 105 | func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer.PickInfo) (pick, error) { |
| 106 | var ch chan struct{} |
| 107 | |
| 108 | var lastPickErr error |
| 109 | pickBlocked := false |
| 110 | |
| 111 | for { |
| 112 | pg := pw.pickerGen.Load() |
| 113 | if pg == nil { |
| 114 | return pick{}, ErrClientConnClosing |
| 115 | } |
| 116 | if pg.picker == nil { |
| 117 | ch = pg.blockingCh |
| 118 | } |
| 119 | if ch == pg.blockingCh { |
| 120 | // This could happen when either: |
| 121 | // - pw.picker is nil (the previous if condition), or |
| 122 | // - we have already called pick on the current picker. |
| 123 | select { |
| 124 | case <-ctx.Done(): |
| 125 | var errStr string |
| 126 | if lastPickErr != nil { |
| 127 | errStr = "latest balancer error: " + lastPickErr.Error() |
| 128 | } else { |
| 129 | errStr = fmt.Sprintf("%v while waiting for connections to become ready", ctx.Err()) |
| 130 | } |
| 131 | switch ctx.Err() { |
| 132 | case context.DeadlineExceeded: |
| 133 | return pick{}, status.Error(codes.DeadlineExceeded, errStr) |
| 134 | case context.Canceled: |
| 135 | return pick{}, status.Error(codes.Canceled, errStr) |
| 136 | } |
| 137 | case <-ch: |
| 138 | } |
| 139 | continue |
| 140 | } |
| 141 | |
| 142 | // If the channel is set, it means that the pick call had to wait for a |
| 143 | // new picker at some point. Either it's the first iteration and this |
| 144 | // function received the first picker, or a picker errored with |
| 145 | // ErrNoSubConnAvailable or errored with failfast set to false, which |
| 146 | // will trigger a continue to the next iteration. In the first case this |
| 147 | // conditional will hit if this call had to block (the channel is set). |
| 148 | // In the second case, the only way it will get to this conditional is |
| 149 | // if there is a new picker. |
| 150 | if ch != nil { |
| 151 | pickBlocked = true |
| 152 | } |
| 153 | |
| 154 | ch = pg.blockingCh |
| 155 | p := pg.picker |
| 156 | |
| 157 | pickResult, err := p.Pick(info) |
| 158 | if err != nil { |
| 159 | if err == balancer.ErrNoSubConnAvailable { |
| 160 | continue |
| 161 | } |
| 162 | if st, ok := status.FromError(err); ok { |
nothing calls this directly
no test coverage detected