|
|
|
@ -128,6 +128,10 @@ class PickFirst : public LoadBalancingPolicy { |
|
|
|
|
|
|
|
|
|
void ShutdownLocked() override; |
|
|
|
|
|
|
|
|
|
void AttemptToConnectUsingLatestUpdateArgsLocked(); |
|
|
|
|
|
|
|
|
|
// Lateset update args.
|
|
|
|
|
UpdateArgs latest_update_args_; |
|
|
|
|
// All our subchannels.
|
|
|
|
|
OrphanablePtr<PickFirstSubchannelList> subchannel_list_; |
|
|
|
|
// Latest pending subchannel list.
|
|
|
|
@ -167,18 +171,7 @@ void PickFirst::ExitIdleLocked() { |
|
|
|
|
if (shutdown_) return; |
|
|
|
|
if (idle_) { |
|
|
|
|
idle_ = false; |
|
|
|
|
if (subchannel_list_ == nullptr || |
|
|
|
|
subchannel_list_->num_subchannels() == 0) { |
|
|
|
|
grpc_error* error = grpc_error_set_int( |
|
|
|
|
GRPC_ERROR_CREATE_FROM_STATIC_STRING("No addresses to connect to"), |
|
|
|
|
GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE); |
|
|
|
|
channel_control_helper()->UpdateState( |
|
|
|
|
GRPC_CHANNEL_TRANSIENT_FAILURE, |
|
|
|
|
UniquePtr<SubchannelPicker>(New<TransientFailurePicker>(error))); |
|
|
|
|
} else { |
|
|
|
|
subchannel_list_->subchannel(0) |
|
|
|
|
->CheckConnectivityStateAndStartWatchingLocked(); |
|
|
|
|
} |
|
|
|
|
AttemptToConnectUsingLatestUpdateArgsLocked(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -189,36 +182,26 @@ void PickFirst::ResetBackoffLocked() { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void PickFirst::UpdateLocked(UpdateArgs args) { |
|
|
|
|
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_pick_first_trace)) { |
|
|
|
|
gpr_log(GPR_INFO, |
|
|
|
|
"Pick First %p received update with %" PRIuPTR " addresses", this, |
|
|
|
|
args.addresses.size()); |
|
|
|
|
} |
|
|
|
|
grpc_arg new_arg = grpc_channel_arg_integer_create( |
|
|
|
|
const_cast<char*>(GRPC_ARG_INHIBIT_HEALTH_CHECKING), 1); |
|
|
|
|
grpc_channel_args* new_args = |
|
|
|
|
grpc_channel_args_copy_and_add(args.args, &new_arg, 1); |
|
|
|
|
void PickFirst::AttemptToConnectUsingLatestUpdateArgsLocked() { |
|
|
|
|
// Create a subchannel list from the latest_update_args_.
|
|
|
|
|
auto subchannel_list = MakeOrphanable<PickFirstSubchannelList>( |
|
|
|
|
this, &grpc_lb_pick_first_trace, args.addresses, combiner(), *new_args); |
|
|
|
|
grpc_channel_args_destroy(new_args); |
|
|
|
|
this, &grpc_lb_pick_first_trace, latest_update_args_.addresses, |
|
|
|
|
combiner(), *latest_update_args_.args); |
|
|
|
|
// Empty update or no valid subchannels.
|
|
|
|
|
if (subchannel_list->num_subchannels() == 0) { |
|
|
|
|
// Empty update or no valid subchannels. Unsubscribe from all current
|
|
|
|
|
// subchannels.
|
|
|
|
|
// Unsubscribe from all current subchannels.
|
|
|
|
|
subchannel_list_ = std::move(subchannel_list); // Empty list.
|
|
|
|
|
selected_ = nullptr; |
|
|
|
|
// If not idle, put the channel in TRANSIENT_FAILURE.
|
|
|
|
|
// (If we are idle, then this will happen in ExitIdleLocked() if we
|
|
|
|
|
// haven't gotten a non-empty update by the time the application tries
|
|
|
|
|
// to start a new call.)
|
|
|
|
|
if (!idle_) { |
|
|
|
|
grpc_error* error = grpc_error_set_int( |
|
|
|
|
GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty update"), |
|
|
|
|
GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE); |
|
|
|
|
channel_control_helper()->UpdateState( |
|
|
|
|
GRPC_CHANNEL_TRANSIENT_FAILURE, |
|
|
|
|
UniquePtr<SubchannelPicker>(New<TransientFailurePicker>(error))); |
|
|
|
|
} |
|
|
|
|
grpc_error* error = |
|
|
|
|
grpc_error_set_int(GRPC_ERROR_CREATE_FROM_STATIC_STRING("Empty update"), |
|
|
|
|
GRPC_ERROR_INT_GRPC_STATUS, GRPC_STATUS_UNAVAILABLE); |
|
|
|
|
channel_control_helper()->UpdateState( |
|
|
|
|
GRPC_CHANNEL_TRANSIENT_FAILURE, |
|
|
|
|
UniquePtr<SubchannelPicker>(New<TransientFailurePicker>(error))); |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
// If one of the subchannels in the new list is already in state
|
|
|
|
@ -226,8 +209,6 @@ void PickFirst::UpdateLocked(UpdateArgs args) { |
|
|
|
|
// currently selected subchannel is also present in the update. It
|
|
|
|
|
// can also happen if one of the subchannels in the update is already
|
|
|
|
|
// in the global subchannel pool because it's in use by another channel.
|
|
|
|
|
// TODO(roth): If we're in IDLE state, we should probably defer this
|
|
|
|
|
// check and instead do it in ExitIdleLocked().
|
|
|
|
|
for (size_t i = 0; i < subchannel_list->num_subchannels(); ++i) { |
|
|
|
|
PickFirstSubchannelData* sd = subchannel_list->subchannel(i); |
|
|
|
|
grpc_connectivity_state state = sd->CheckConnectivityStateLocked(); |
|
|
|
@ -239,10 +220,6 @@ void PickFirst::UpdateLocked(UpdateArgs args) { |
|
|
|
|
// not have contained the currently selected subchannel), drop
|
|
|
|
|
// it, so that it doesn't override what we've done here.
|
|
|
|
|
latest_pending_subchannel_list_.reset(); |
|
|
|
|
// Make sure that subsequent calls to ExitIdleLocked() don't cause
|
|
|
|
|
// us to start watching a subchannel other than the one we've
|
|
|
|
|
// selected.
|
|
|
|
|
idle_ = false; |
|
|
|
|
return; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -252,13 +229,11 @@ void PickFirst::UpdateLocked(UpdateArgs args) { |
|
|
|
|
subchannel_list_ = std::move(subchannel_list); |
|
|
|
|
// If we're not in IDLE state, start trying to connect to the first
|
|
|
|
|
// subchannel in the new list.
|
|
|
|
|
if (!idle_) { |
|
|
|
|
// Note: No need to use CheckConnectivityStateAndStartWatchingLocked()
|
|
|
|
|
// here, since we've already checked the initial connectivity
|
|
|
|
|
// state of all subchannels above.
|
|
|
|
|
subchannel_list_->subchannel(0)->StartConnectivityWatchLocked(); |
|
|
|
|
subchannel_list_->subchannel(0)->subchannel()->AttemptToConnect(); |
|
|
|
|
} |
|
|
|
|
// Note: No need to use CheckConnectivityStateAndStartWatchingLocked()
|
|
|
|
|
// here, since we've already checked the initial connectivity
|
|
|
|
|
// state of all subchannels above.
|
|
|
|
|
subchannel_list_->subchannel(0)->StartConnectivityWatchLocked(); |
|
|
|
|
subchannel_list_->subchannel(0)->subchannel()->AttemptToConnect(); |
|
|
|
|
} else { |
|
|
|
|
// We do have a selected subchannel (which means it's READY), so keep
|
|
|
|
|
// using it until one of the subchannels in the new list reports READY.
|
|
|
|
@ -274,16 +249,35 @@ void PickFirst::UpdateLocked(UpdateArgs args) { |
|
|
|
|
latest_pending_subchannel_list_ = std::move(subchannel_list); |
|
|
|
|
// If we're not in IDLE state, start trying to connect to the first
|
|
|
|
|
// subchannel in the new list.
|
|
|
|
|
if (!idle_) { |
|
|
|
|
// Note: No need to use CheckConnectivityStateAndStartWatchingLocked()
|
|
|
|
|
// here, since we've already checked the initial connectivity
|
|
|
|
|
// state of all subchannels above.
|
|
|
|
|
latest_pending_subchannel_list_->subchannel(0) |
|
|
|
|
->StartConnectivityWatchLocked(); |
|
|
|
|
latest_pending_subchannel_list_->subchannel(0) |
|
|
|
|
->subchannel() |
|
|
|
|
->AttemptToConnect(); |
|
|
|
|
} |
|
|
|
|
// Note: No need to use CheckConnectivityStateAndStartWatchingLocked()
|
|
|
|
|
// here, since we've already checked the initial connectivity
|
|
|
|
|
// state of all subchannels above.
|
|
|
|
|
latest_pending_subchannel_list_->subchannel(0) |
|
|
|
|
->StartConnectivityWatchLocked(); |
|
|
|
|
latest_pending_subchannel_list_->subchannel(0) |
|
|
|
|
->subchannel() |
|
|
|
|
->AttemptToConnect(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void PickFirst::UpdateLocked(UpdateArgs args) { |
|
|
|
|
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_pick_first_trace)) { |
|
|
|
|
gpr_log(GPR_INFO, |
|
|
|
|
"Pick First %p received update with %" PRIuPTR " addresses", this, |
|
|
|
|
args.addresses.size()); |
|
|
|
|
} |
|
|
|
|
// Update the latest_update_args_
|
|
|
|
|
grpc_arg new_arg = grpc_channel_arg_integer_create( |
|
|
|
|
const_cast<char*>(GRPC_ARG_INHIBIT_HEALTH_CHECKING), 1); |
|
|
|
|
const grpc_channel_args* new_args = |
|
|
|
|
grpc_channel_args_copy_and_add(args.args, &new_arg, 1); |
|
|
|
|
GPR_SWAP(const grpc_channel_args*, new_args, args.args); |
|
|
|
|
grpc_channel_args_destroy(new_args); |
|
|
|
|
latest_update_args_ = std::move(args); |
|
|
|
|
// If we are not in idle, start connection attempt immediately.
|
|
|
|
|
// Otherwise, we defer the attempt into ExitIdleLocked().
|
|
|
|
|
if (!idle_) { |
|
|
|
|
AttemptToConnectUsingLatestUpdateArgsLocked(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -338,10 +332,12 @@ void PickFirst::PickFirstSubchannelData::ProcessConnectivityChangeLocked( |
|
|
|
|
// also set the channel state to IDLE. The reason is that if the new
|
|
|
|
|
// state is TRANSIENT_FAILURE due to a GOAWAY reception we don't want
|
|
|
|
|
// to connect to the re-resolved backends until we leave IDLE state.
|
|
|
|
|
// TODO(qianchengz): We may want to request re-resolution in
|
|
|
|
|
// ExitIdleLocked().
|
|
|
|
|
p->idle_ = true; |
|
|
|
|
p->channel_control_helper()->RequestReresolution(); |
|
|
|
|
p->selected_ = nullptr; |
|
|
|
|
CancelConnectivityWatchLocked("selected subchannel failed; going IDLE"); |
|
|
|
|
p->subchannel_list_.reset(); |
|
|
|
|
p->channel_control_helper()->UpdateState( |
|
|
|
|
GRPC_CHANNEL_IDLE, UniquePtr<SubchannelPicker>(New<QueuePicker>( |
|
|
|
|
p->Ref(DEBUG_LOCATION, "QueuePicker")))); |
|
|
|
@ -454,6 +450,11 @@ void PickFirst::PickFirstSubchannelData::ProcessUnselectedReadyLocked() { |
|
|
|
|
if (GRPC_TRACE_FLAG_ENABLED(grpc_lb_pick_first_trace)) { |
|
|
|
|
gpr_log(GPR_INFO, "Pick First %p selected subchannel %p", p, subchannel()); |
|
|
|
|
} |
|
|
|
|
for (size_t i = 0; i < subchannel_list()->num_subchannels(); ++i) { |
|
|
|
|
if (i != Index()) { |
|
|
|
|
subchannel_list()->subchannel(i)->ShutdownLocked(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void PickFirst::PickFirstSubchannelData:: |
|
|
|
|