Move retry code into its own filter in the DynamicFilter stack (#25820)

* rename ChannelData to ClientChannel

* make ClientChannel class definition public

* move retry code to its own filter

* move LB call factory method to ClientChannel class

* move dynamic termination filter out of ClientChannel class

* update comments

* remove retry parsing from client channel service config parser

* fix clang-tidy

* fix service_config_test

* clang-format
pull/25848/head
Mark D. Roth 4 years ago committed by GitHub
parent efd2ed8ae6
commit 3f19333ced
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      BUILD
  2. 4
      BUILD.gn
  3. 4
      CMakeLists.txt
  4. 4
      Makefile
  5. 8
      build_autogenerated.yaml
  6. 2
      config.m4
  7. 2
      config.w32
  8. 4
      gRPC-C++.podspec
  9. 6
      gRPC-Core.podspec
  10. 4
      grpc.gemspec
  11. 4
      grpc.gyp
  12. 4
      package.xml
  13. 80
      src/core/ext/filters/client_channel/channel_connectivity.cc
  14. 3383
      src/core/ext/filters/client_channel/client_channel.cc
  15. 530
      src/core/ext/filters/client_channel/client_channel.h
  16. 5
      src/core/ext/filters/client_channel/client_channel_plugin.cc
  17. 16
      src/core/ext/filters/client_channel/lb_policy/grpclb/grpclb.cc
  18. 251
      src/core/ext/filters/client_channel/resolver_result_parsing.cc
  19. 65
      src/core/ext/filters/client_channel/resolver_result_parsing.h
  20. 2164
      src/core/ext/filters/client_channel/retry_filter.cc
  21. 30
      src/core/ext/filters/client_channel/retry_filter.h
  22. 285
      src/core/ext/filters/client_channel/retry_service_config.cc
  23. 90
      src/core/ext/filters/client_channel/retry_service_config.h
  24. 16
      src/core/ext/xds/xds_client.cc
  25. 2
      src/python/grpcio/grpc_core_dependencies.py
  26. 288
      test/core/client_channel/service_config_test.cc
  27. 3
      test/cpp/microbenchmarks/bm_call_create.cc
  28. 4
      tools/doxygen/Doxyfile.c++.internal
  29. 4
      tools/doxygen/Doxyfile.core.internal

@ -1135,6 +1135,8 @@ grpc_cc_library(
"src/core/ext/filters/client_channel/resolver.cc",
"src/core/ext/filters/client_channel/resolver_registry.cc",
"src/core/ext/filters/client_channel/resolver_result_parsing.cc",
"src/core/ext/filters/client_channel/retry_filter.cc",
"src/core/ext/filters/client_channel/retry_service_config.cc",
"src/core/ext/filters/client_channel/retry_throttle.cc",
"src/core/ext/filters/client_channel/server_address.cc",
"src/core/ext/filters/client_channel/service_config.cc",
@ -1167,6 +1169,8 @@ grpc_cc_library(
"src/core/ext/filters/client_channel/resolver_factory.h",
"src/core/ext/filters/client_channel/resolver_registry.h",
"src/core/ext/filters/client_channel/resolver_result_parsing.h",
"src/core/ext/filters/client_channel/retry_filter.h",
"src/core/ext/filters/client_channel/retry_service_config.h",
"src/core/ext/filters/client_channel/retry_throttle.h",
"src/core/ext/filters/client_channel/server_address.h",
"src/core/ext/filters/client_channel/service_config.h",

@ -300,6 +300,10 @@ config("grpc_config") {
"src/core/ext/filters/client_channel/resolver_registry.h",
"src/core/ext/filters/client_channel/resolver_result_parsing.cc",
"src/core/ext/filters/client_channel/resolver_result_parsing.h",
"src/core/ext/filters/client_channel/retry_filter.cc",
"src/core/ext/filters/client_channel/retry_filter.h",
"src/core/ext/filters/client_channel/retry_service_config.cc",
"src/core/ext/filters/client_channel/retry_service_config.h",
"src/core/ext/filters/client_channel/retry_throttle.cc",
"src/core/ext/filters/client_channel/retry_throttle.h",
"src/core/ext/filters/client_channel/server_address.cc",

@ -1518,6 +1518,8 @@ add_library(grpc
src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc
src/core/ext/filters/client_channel/resolver_registry.cc
src/core/ext/filters/client_channel/resolver_result_parsing.cc
src/core/ext/filters/client_channel/retry_filter.cc
src/core/ext/filters/client_channel/retry_service_config.cc
src/core/ext/filters/client_channel/retry_throttle.cc
src/core/ext/filters/client_channel/server_address.cc
src/core/ext/filters/client_channel/service_config.cc
@ -2331,6 +2333,8 @@ add_library(grpc_unsecure
src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc
src/core/ext/filters/client_channel/resolver_registry.cc
src/core/ext/filters/client_channel/resolver_result_parsing.cc
src/core/ext/filters/client_channel/retry_filter.cc
src/core/ext/filters/client_channel/retry_service_config.cc
src/core/ext/filters/client_channel/retry_throttle.cc
src/core/ext/filters/client_channel/server_address.cc
src/core/ext/filters/client_channel/service_config.cc

@ -1086,6 +1086,8 @@ LIBGRPC_SRC = \
src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc \
src/core/ext/filters/client_channel/resolver_registry.cc \
src/core/ext/filters/client_channel/resolver_result_parsing.cc \
src/core/ext/filters/client_channel/retry_filter.cc \
src/core/ext/filters/client_channel/retry_service_config.cc \
src/core/ext/filters/client_channel/retry_throttle.cc \
src/core/ext/filters/client_channel/server_address.cc \
src/core/ext/filters/client_channel/service_config.cc \
@ -1747,6 +1749,8 @@ LIBGRPC_UNSECURE_SRC = \
src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc \
src/core/ext/filters/client_channel/resolver_registry.cc \
src/core/ext/filters/client_channel/resolver_result_parsing.cc \
src/core/ext/filters/client_channel/retry_filter.cc \
src/core/ext/filters/client_channel/retry_service_config.cc \
src/core/ext/filters/client_channel/retry_throttle.cc \
src/core/ext/filters/client_channel/server_address.cc \
src/core/ext/filters/client_channel/service_config.cc \

@ -412,6 +412,8 @@ libs:
- src/core/ext/filters/client_channel/resolver_factory.h
- src/core/ext/filters/client_channel/resolver_registry.h
- src/core/ext/filters/client_channel/resolver_result_parsing.h
- src/core/ext/filters/client_channel/retry_filter.h
- src/core/ext/filters/client_channel/retry_service_config.h
- src/core/ext/filters/client_channel/retry_throttle.h
- src/core/ext/filters/client_channel/server_address.h
- src/core/ext/filters/client_channel/service_config.h
@ -928,6 +930,8 @@ libs:
- src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc
- src/core/ext/filters/client_channel/resolver_registry.cc
- src/core/ext/filters/client_channel/resolver_result_parsing.cc
- src/core/ext/filters/client_channel/retry_filter.cc
- src/core/ext/filters/client_channel/retry_service_config.cc
- src/core/ext/filters/client_channel/retry_throttle.cc
- src/core/ext/filters/client_channel/server_address.cc
- src/core/ext/filters/client_channel/service_config.cc
@ -1610,6 +1614,8 @@ libs:
- src/core/ext/filters/client_channel/resolver_factory.h
- src/core/ext/filters/client_channel/resolver_registry.h
- src/core/ext/filters/client_channel/resolver_result_parsing.h
- src/core/ext/filters/client_channel/retry_filter.h
- src/core/ext/filters/client_channel/retry_service_config.h
- src/core/ext/filters/client_channel/retry_throttle.h
- src/core/ext/filters/client_channel/server_address.h
- src/core/ext/filters/client_channel/service_config.h
@ -1863,6 +1869,8 @@ libs:
- src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc
- src/core/ext/filters/client_channel/resolver_registry.cc
- src/core/ext/filters/client_channel/resolver_result_parsing.cc
- src/core/ext/filters/client_channel/retry_filter.cc
- src/core/ext/filters/client_channel/retry_service_config.cc
- src/core/ext/filters/client_channel/retry_throttle.cc
- src/core/ext/filters/client_channel/server_address.cc
- src/core/ext/filters/client_channel/service_config.cc

@ -92,6 +92,8 @@ if test "$PHP_GRPC" != "no"; then
src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc \
src/core/ext/filters/client_channel/resolver_registry.cc \
src/core/ext/filters/client_channel/resolver_result_parsing.cc \
src/core/ext/filters/client_channel/retry_filter.cc \
src/core/ext/filters/client_channel/retry_service_config.cc \
src/core/ext/filters/client_channel/retry_throttle.cc \
src/core/ext/filters/client_channel/server_address.cc \
src/core/ext/filters/client_channel/service_config.cc \

@ -58,6 +58,8 @@ if (PHP_GRPC != "no") {
"src\\core\\ext\\filters\\client_channel\\resolver\\xds\\xds_resolver.cc " +
"src\\core\\ext\\filters\\client_channel\\resolver_registry.cc " +
"src\\core\\ext\\filters\\client_channel\\resolver_result_parsing.cc " +
"src\\core\\ext\\filters\\client_channel\\retry_filter.cc " +
"src\\core\\ext\\filters\\client_channel\\retry_service_config.cc " +
"src\\core\\ext\\filters\\client_channel\\retry_throttle.cc " +
"src\\core\\ext\\filters\\client_channel\\server_address.cc " +
"src\\core\\ext\\filters\\client_channel\\service_config.cc " +

@ -239,6 +239,8 @@ Pod::Spec.new do |s|
'src/core/ext/filters/client_channel/resolver_factory.h',
'src/core/ext/filters/client_channel/resolver_registry.h',
'src/core/ext/filters/client_channel/resolver_result_parsing.h',
'src/core/ext/filters/client_channel/retry_filter.h',
'src/core/ext/filters/client_channel/retry_service_config.h',
'src/core/ext/filters/client_channel/retry_throttle.h',
'src/core/ext/filters/client_channel/server_address.h',
'src/core/ext/filters/client_channel/service_config.h',
@ -878,6 +880,8 @@ Pod::Spec.new do |s|
'src/core/ext/filters/client_channel/resolver_factory.h',
'src/core/ext/filters/client_channel/resolver_registry.h',
'src/core/ext/filters/client_channel/resolver_result_parsing.h',
'src/core/ext/filters/client_channel/retry_filter.h',
'src/core/ext/filters/client_channel/retry_service_config.h',
'src/core/ext/filters/client_channel/retry_throttle.h',
'src/core/ext/filters/client_channel/server_address.h',
'src/core/ext/filters/client_channel/service_config.h',

@ -279,6 +279,10 @@ Pod::Spec.new do |s|
'src/core/ext/filters/client_channel/resolver_registry.h',
'src/core/ext/filters/client_channel/resolver_result_parsing.cc',
'src/core/ext/filters/client_channel/resolver_result_parsing.h',
'src/core/ext/filters/client_channel/retry_filter.cc',
'src/core/ext/filters/client_channel/retry_filter.h',
'src/core/ext/filters/client_channel/retry_service_config.cc',
'src/core/ext/filters/client_channel/retry_service_config.h',
'src/core/ext/filters/client_channel/retry_throttle.cc',
'src/core/ext/filters/client_channel/retry_throttle.h',
'src/core/ext/filters/client_channel/server_address.cc',
@ -1436,6 +1440,8 @@ Pod::Spec.new do |s|
'src/core/ext/filters/client_channel/resolver_factory.h',
'src/core/ext/filters/client_channel/resolver_registry.h',
'src/core/ext/filters/client_channel/resolver_result_parsing.h',
'src/core/ext/filters/client_channel/retry_filter.h',
'src/core/ext/filters/client_channel/retry_service_config.h',
'src/core/ext/filters/client_channel/retry_throttle.h',
'src/core/ext/filters/client_channel/server_address.h',
'src/core/ext/filters/client_channel/service_config.h',

@ -195,6 +195,10 @@ Gem::Specification.new do |s|
s.files += %w( src/core/ext/filters/client_channel/resolver_registry.h )
s.files += %w( src/core/ext/filters/client_channel/resolver_result_parsing.cc )
s.files += %w( src/core/ext/filters/client_channel/resolver_result_parsing.h )
s.files += %w( src/core/ext/filters/client_channel/retry_filter.cc )
s.files += %w( src/core/ext/filters/client_channel/retry_filter.h )
s.files += %w( src/core/ext/filters/client_channel/retry_service_config.cc )
s.files += %w( src/core/ext/filters/client_channel/retry_service_config.h )
s.files += %w( src/core/ext/filters/client_channel/retry_throttle.cc )
s.files += %w( src/core/ext/filters/client_channel/retry_throttle.h )
s.files += %w( src/core/ext/filters/client_channel/server_address.cc )

@ -496,6 +496,8 @@
'src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc',
'src/core/ext/filters/client_channel/resolver_registry.cc',
'src/core/ext/filters/client_channel/resolver_result_parsing.cc',
'src/core/ext/filters/client_channel/retry_filter.cc',
'src/core/ext/filters/client_channel/retry_service_config.cc',
'src/core/ext/filters/client_channel/retry_throttle.cc',
'src/core/ext/filters/client_channel/server_address.cc',
'src/core/ext/filters/client_channel/service_config.cc',
@ -1135,6 +1137,8 @@
'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc',
'src/core/ext/filters/client_channel/resolver_registry.cc',
'src/core/ext/filters/client_channel/resolver_result_parsing.cc',
'src/core/ext/filters/client_channel/retry_filter.cc',
'src/core/ext/filters/client_channel/retry_service_config.cc',
'src/core/ext/filters/client_channel/retry_throttle.cc',
'src/core/ext/filters/client_channel/server_address.cc',
'src/core/ext/filters/client_channel/service_config.cc',

@ -175,6 +175,10 @@
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver_registry.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver_result_parsing.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/resolver_result_parsing.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/retry_filter.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/retry_filter.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/retry_service_config.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/retry_service_config.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/retry_throttle.cc" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/retry_throttle.h" role="src" />
<file baseinstalldir="/" name="src/core/ext/filters/client_channel/server_address.cc" role="src" />

@ -32,27 +32,21 @@
grpc_connectivity_state grpc_channel_check_connectivity_state(
grpc_channel* channel, int try_to_connect) {
/* forward through to the underlying client channel */
grpc_channel_element* client_channel_elem =
grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel));
grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
grpc_core::ExecCtx exec_ctx;
grpc_connectivity_state state;
GRPC_API_TRACE(
"grpc_channel_check_connectivity_state(channel=%p, try_to_connect=%d)", 2,
(channel, try_to_connect));
if (GPR_LIKELY(client_channel_elem->filter == &grpc_client_channel_filter)) {
state = grpc_client_channel_check_connectivity_state(client_channel_elem,
try_to_connect);
return state;
// Forward through to the underlying client channel.
grpc_core::ClientChannel* client_channel =
grpc_core::ClientChannel::GetFromChannel(channel);
if (GPR_UNLIKELY(client_channel == nullptr)) {
gpr_log(GPR_ERROR,
"grpc_channel_check_connectivity_state called on something that is "
"not a client channel");
return GRPC_CHANNEL_SHUTDOWN;
}
gpr_log(GPR_ERROR,
"grpc_channel_check_connectivity_state called on something that is "
"not a client channel, but '%s'",
client_channel_elem->filter->name);
return GRPC_CHANNEL_SHUTDOWN;
return client_channel->CheckConnectivityState(try_to_connect);
}
typedef enum {
@ -79,13 +73,7 @@ struct state_watcher {
} // namespace
static void delete_state_watcher(state_watcher* w) {
grpc_channel_element* client_channel_elem = grpc_channel_stack_last_element(
grpc_channel_get_channel_stack(w->channel));
if (client_channel_elem->filter == &grpc_client_channel_filter) {
GRPC_CHANNEL_INTERNAL_UNREF(w->channel, "watch_channel_connectivity");
} else {
abort();
}
GRPC_CHANNEL_INTERNAL_UNREF(w->channel, "watch_channel_connectivity");
gpr_mu_destroy(&w->mu);
gpr_free(w);
}
@ -120,12 +108,10 @@ static void partly_done(state_watcher* w, bool due_to_completion,
if (due_to_completion) {
grpc_timer_cancel(&w->alarm);
} else {
grpc_channel_element* client_channel_elem = grpc_channel_stack_last_element(
grpc_channel_get_channel_stack(w->channel));
grpc_client_channel_watch_connectivity_state(
client_channel_elem,
grpc_polling_entity_create_from_pollset(grpc_cq_pollset(w->cq)),
nullptr, &w->on_complete, nullptr);
grpc_core::ClientChannel* client_channel =
grpc_core::ClientChannel::GetFromChannel(w->channel);
GPR_ASSERT(client_channel != nullptr);
client_channel->CancelExternalConnectivityWatcher(&w->on_complete);
}
gpr_mu_lock(&w->mu);
@ -187,10 +173,15 @@ static void timeout_complete(void* pw, grpc_error* error) {
}
int grpc_channel_num_external_connectivity_watchers(grpc_channel* channel) {
grpc_channel_element* client_channel_elem =
grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel));
return grpc_client_channel_num_external_connectivity_watchers(
client_channel_elem);
grpc_core::ClientChannel* client_channel =
grpc_core::ClientChannel::GetFromChannel(channel);
if (client_channel == nullptr) {
gpr_log(GPR_ERROR,
"grpc_channel_num_external_connectivity_watchers called on "
"something that is not a client channel");
return 0;
}
return client_channel->NumExternalConnectivityWatchers();
}
typedef struct watcher_timer_init_arg {
@ -207,20 +198,14 @@ static void watcher_timer_init(void* arg, grpc_error* /*error_ignored*/) {
}
int grpc_channel_support_connectivity_watcher(grpc_channel* channel) {
grpc_channel_element* client_channel_elem =
grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel));
return client_channel_elem->filter != &grpc_client_channel_filter ? 0 : 1;
return grpc_core::ClientChannel::GetFromChannel(channel) != nullptr;
}
void grpc_channel_watch_connectivity_state(
grpc_channel* channel, grpc_connectivity_state last_observed_state,
gpr_timespec deadline, grpc_completion_queue* cq, void* tag) {
grpc_channel_element* client_channel_elem =
grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel));
grpc_core::ApplicationCallbackExecCtx callback_exec_ctx;
grpc_core::ExecCtx exec_ctx;
state_watcher* w = static_cast<state_watcher*>(gpr_malloc(sizeof(*w)));
GRPC_API_TRACE(
"grpc_channel_watch_connectivity_state("
"channel=%p, last_observed_state=%d, "
@ -233,6 +218,7 @@ void grpc_channel_watch_connectivity_state(
GPR_ASSERT(grpc_cq_begin_op(cq, tag));
state_watcher* w = static_cast<state_watcher*>(gpr_malloc(sizeof(*w)));
gpr_mu_init(&w->mu);
GRPC_CLOSURE_INIT(&w->on_complete, watch_complete, w,
grpc_schedule_on_exec_ctx);
@ -252,13 +238,11 @@ void grpc_channel_watch_connectivity_state(
GRPC_CLOSURE_INIT(&w->watcher_timer_init, watcher_timer_init, wa,
grpc_schedule_on_exec_ctx);
if (client_channel_elem->filter == &grpc_client_channel_filter) {
GRPC_CHANNEL_INTERNAL_REF(channel, "watch_channel_connectivity");
grpc_client_channel_watch_connectivity_state(
client_channel_elem,
grpc_polling_entity_create_from_pollset(grpc_cq_pollset(cq)), &w->state,
&w->on_complete, &w->watcher_timer_init);
} else {
abort();
}
GRPC_CHANNEL_INTERNAL_REF(channel, "watch_channel_connectivity");
grpc_core::ClientChannel* client_channel =
grpc_core::ClientChannel::GetFromChannel(channel);
GPR_ASSERT(client_channel != nullptr);
client_channel->AddExternalConnectivityWatcher(
grpc_polling_entity_create_from_pollset(grpc_cq_pollset(cq)), &w->state,
&w->on_complete, &w->watcher_timer_init);
}

File diff suppressed because it is too large Load Diff

@ -1,76 +1,496 @@
/*
*
* Copyright 2015 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
//
// Copyright 2015 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_H
#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_H
#include <grpc/support/port_platform.h>
#include "src/core/ext/filters/client_channel/client_channel_channelz.h"
#include <map>
#include <memory>
#include <set>
#include <string>
#include "absl/status/status.h"
#include "absl/types/optional.h"
#include <grpc/support/log.h>
#include "src/core/ext/filters/client_channel/client_channel_factory.h"
#include "src/core/ext/filters/client_channel/config_selector.h"
#include "src/core/ext/filters/client_channel/dynamic_filters.h"
#include "src/core/ext/filters/client_channel/lb_policy.h"
#include "src/core/ext/filters/client_channel/resolver.h"
#include "src/core/lib/channel/channel_stack.h"
#include "src/core/ext/filters/client_channel/resolver_result_parsing.h"
#include "src/core/ext/filters/client_channel/retry_throttle.h"
#include "src/core/ext/filters/client_channel/service_config.h"
#include "src/core/ext/filters/client_channel/subchannel.h"
#include "src/core/ext/filters/client_channel/subchannel_pool_interface.h"
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/iomgr/error.h"
#include "src/core/lib/iomgr/polling_entity.h"
#include "src/core/lib/iomgr/work_serializer.h"
#include "src/core/lib/surface/channel.h"
#include "src/core/lib/transport/connectivity_state.h"
//
// Client channel filter
//
// A client channel is a channel that begins disconnected, and can connect
// to some endpoint on demand. If that endpoint disconnects, it will be
// connected to again later.
//
// Calls on a disconnected client channel are queued until a connection is
// established.
// Channel arg key for server URI string.
#define GRPC_ARG_SERVER_URI "grpc.server_uri"
/* A client channel is a channel that begins disconnected, and can connect
to some endpoint on demand. If that endpoint disconnects, it will be
connected to again later.
// Channel arg containing a pointer to the ClientChannel object.
#define GRPC_ARG_CLIENT_CHANNEL "grpc.internal.client_channel"
// Channel arg containing a pointer to the ServiceConfig object.
#define GRPC_ARG_SERVICE_CONFIG_OBJ "grpc.internal.service_config_obj"
// Max number of batches that can be pending on a call at any given
// time. This includes one batch for each of the following ops:
// recv_initial_metadata
// send_initial_metadata
// recv_message
// send_message
// recv_trailing_metadata
// send_trailing_metadata
#define MAX_PENDING_BATCHES 6
namespace grpc_core {
class ClientChannel {
public:
static const grpc_channel_filter kFilterVtable;
class LoadBalancedCall;
// Returns the ClientChannel object from channel, or null if channel
// is not a client channel.
static ClientChannel* GetFromChannel(grpc_channel* channel);
grpc_connectivity_state CheckConnectivityState(bool try_to_connect);
// Starts a one-time connectivity state watch. When the channel's state
// becomes different from *state, sets *state to the new state and
// schedules on_complete. The watcher_timer_init callback is invoked as
// soon as the watch is actually started (i.e., after hopping into the
// client channel combiner). I/O will be serviced via pollent.
//
// This is intended to be used when starting a watch from outside of C-core
// via grpc_channel_watch_connectivity_state(). It should not be used
// by other callers.
void AddExternalConnectivityWatcher(grpc_polling_entity pollent,
grpc_connectivity_state* state,
grpc_closure* on_complete,
grpc_closure* watcher_timer_init) {
new ExternalConnectivityWatcher(this, pollent, state, on_complete,
watcher_timer_init);
}
// Cancels a pending external watcher previously added by
// AddExternalConnectivityWatcher().
void CancelExternalConnectivityWatcher(grpc_closure* on_complete) {
ExternalConnectivityWatcher::RemoveWatcherFromExternalWatchersMap(
this, on_complete, /*cancel=*/true);
}
int NumExternalConnectivityWatchers() const {
MutexLock lock(&external_watchers_mu_);
return static_cast<int>(external_watchers_.size());
}
// Starts and stops a connectivity watch. The watcher will be initially
// notified as soon as the state changes from initial_state and then on
// every subsequent state change until either the watch is stopped or
// it is notified that the state has changed to SHUTDOWN.
//
// This is intended to be used when starting watches from code inside of
// C-core (e.g., for a nested control plane channel for things like xds).
void AddConnectivityWatcher(
grpc_connectivity_state initial_state,
OrphanablePtr<AsyncConnectivityStateWatcherInterface> watcher);
void RemoveConnectivityWatcher(
AsyncConnectivityStateWatcherInterface* watcher);
RefCountedPtr<LoadBalancedCall> CreateLoadBalancedCall(
const grpc_call_element_args& args, grpc_polling_entity* pollent,
size_t parent_data_size);
private:
class CallData;
class ResolverResultHandler;
class SubchannelWrapper;
class ClientChannelControlHelper;
class ConnectivityWatcherAdder;
class ConnectivityWatcherRemover;
// Represents a pending connectivity callback from an external caller
// via grpc_client_channel_watch_connectivity_state().
class ExternalConnectivityWatcher : public ConnectivityStateWatcherInterface {
public:
ExternalConnectivityWatcher(ClientChannel* chand,
grpc_polling_entity pollent,
grpc_connectivity_state* state,
grpc_closure* on_complete,
grpc_closure* watcher_timer_init);
~ExternalConnectivityWatcher() override;
// Removes the watcher from the external_watchers_ map.
static void RemoveWatcherFromExternalWatchersMap(ClientChannel* chand,
grpc_closure* on_complete,
bool cancel);
void Notify(grpc_connectivity_state state,
const absl::Status& /* status */) override;
void Cancel();
private:
// Adds the watcher to state_tracker_. Consumes the ref that is passed to it
// from Start().
void AddWatcherLocked();
void RemoveWatcherLocked();
ClientChannel* chand_;
grpc_polling_entity pollent_;
grpc_connectivity_state initial_state_;
grpc_connectivity_state* state_;
grpc_closure* on_complete_;
grpc_closure* watcher_timer_init_;
Atomic<bool> done_{false};
};
struct ResolverQueuedCall {
grpc_call_element* elem;
ResolverQueuedCall* next = nullptr;
};
struct LbQueuedCall {
LoadBalancedCall* lb_call;
LbQueuedCall* next = nullptr;
};
ClientChannel(grpc_channel_element_args* args, grpc_error** error);
~ClientChannel();
// Filter vtable functions.
static grpc_error* Init(grpc_channel_element* elem,
grpc_channel_element_args* args);
static void Destroy(grpc_channel_element* elem);
static void StartTransportOp(grpc_channel_element* elem,
grpc_transport_op* op);
static void GetChannelInfo(grpc_channel_element* elem,
const grpc_channel_info* info);
// Note: Does NOT return a new ref.
grpc_error* disconnect_error() const {
return disconnect_error_.Load(MemoryOrder::ACQUIRE);
}
// Note: All methods with "Locked" suffix must be invoked from within
// work_serializer_.
Calls on a disconnected client channel are queued until a connection is
established. */
void OnResolverResultChangedLocked(Resolver::Result result);
void OnResolverErrorLocked(grpc_error* error);
extern const grpc_channel_filter grpc_client_channel_filter;
void CreateOrUpdateLbPolicyLocked(
RefCountedPtr<LoadBalancingPolicy::Config> lb_policy_config,
Resolver::Result result);
OrphanablePtr<LoadBalancingPolicy> CreateLbPolicyLocked(
const grpc_channel_args& args);
grpc_connectivity_state grpc_client_channel_check_connectivity_state(
grpc_channel_element* elem, int try_to_connect);
void UpdateStateAndPickerLocked(
grpc_connectivity_state state, const absl::Status& status,
const char* reason,
std::unique_ptr<LoadBalancingPolicy::SubchannelPicker> picker);
int grpc_client_channel_num_external_connectivity_watchers(
grpc_channel_element* elem);
void UpdateServiceConfigInControlPlaneLocked(
RefCountedPtr<ServiceConfig> service_config,
RefCountedPtr<ConfigSelector> config_selector,
const internal::ClientChannelGlobalParsedConfig* parsed_service_config,
const char* lb_policy_name);
void UpdateServiceConfigInDataPlaneLocked();
void CreateResolverLocked();
void DestroyResolverAndLbPolicyLocked();
grpc_error* DoPingLocked(grpc_transport_op* op);
void StartTransportOpLocked(grpc_transport_op* op);
void TryToConnectLocked();
// These methods all require holding resolution_mu_.
void AddResolverQueuedCall(ResolverQueuedCall* call,
grpc_polling_entity* pollent)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(resolution_mu_);
void RemoveResolverQueuedCall(ResolverQueuedCall* to_remove,
grpc_polling_entity* pollent)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(resolution_mu_);
// These methods all require holding data_plane_mu_.
void AddLbQueuedCall(LbQueuedCall* call, grpc_polling_entity* pollent)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(data_plane_mu_);
void RemoveLbQueuedCall(LbQueuedCall* to_remove, grpc_polling_entity* pollent)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(data_plane_mu_);
RefCountedPtr<ConnectedSubchannel> GetConnectedSubchannelInDataPlane(
SubchannelInterface* subchannel) const
ABSL_EXCLUSIVE_LOCKS_REQUIRED(data_plane_mu_);
//
// Fields set at construction and never modified.
//
const bool deadline_checking_enabled_;
const bool enable_retries_;
const size_t per_rpc_retry_buffer_size_;
grpc_channel_stack* owning_stack_;
ClientChannelFactory* client_channel_factory_;
const grpc_channel_args* channel_args_;
RefCountedPtr<ServiceConfig> default_service_config_;
std::string server_name_;
UniquePtr<char> target_uri_;
channelz::ChannelNode* channelz_node_;
//
// Fields related to name resolution. Guarded by resolution_mu_.
//
mutable Mutex resolution_mu_;
// Linked list of calls queued waiting for resolver result.
ResolverQueuedCall* resolver_queued_calls_ ABSL_GUARDED_BY(resolution_mu_) =
nullptr;
// Data from service config.
grpc_error* resolver_transient_failure_error_
ABSL_GUARDED_BY(resolution_mu_) = GRPC_ERROR_NONE;
bool received_service_config_data_ ABSL_GUARDED_BY(resolution_mu_) = false;
RefCountedPtr<ServiceConfig> service_config_ ABSL_GUARDED_BY(resolution_mu_);
RefCountedPtr<ConfigSelector> config_selector_
ABSL_GUARDED_BY(resolution_mu_);
RefCountedPtr<DynamicFilters> dynamic_filters_
ABSL_GUARDED_BY(resolution_mu_);
//
// Fields used in the data plane. Guarded by data_plane_mu_.
//
mutable Mutex data_plane_mu_;
std::unique_ptr<LoadBalancingPolicy::SubchannelPicker> picker_
ABSL_GUARDED_BY(data_plane_mu_);
// Linked list of calls queued waiting for LB pick.
LbQueuedCall* lb_queued_calls_ ABSL_GUARDED_BY(data_plane_mu_) = nullptr;
//
// Fields used in the control plane. Guarded by work_serializer.
//
std::shared_ptr<WorkSerializer> work_serializer_;
grpc_pollset_set* interested_parties_;
ConnectivityStateTracker state_tracker_;
OrphanablePtr<Resolver> resolver_;
bool previous_resolution_contained_addresses_ = false;
RefCountedPtr<ServiceConfig> saved_service_config_;
RefCountedPtr<ConfigSelector> saved_config_selector_;
absl::optional<std::string> health_check_service_name_;
OrphanablePtr<LoadBalancingPolicy> lb_policy_;
RefCountedPtr<SubchannelPoolInterface> subchannel_pool_;
// The number of SubchannelWrapper instances referencing a given Subchannel.
std::map<Subchannel*, int> subchannel_refcount_map_;
// The set of SubchannelWrappers that currently exist.
// No need to hold a ref, since the map is updated in the control-plane
// work_serializer when the SubchannelWrappers are created and destroyed.
std::set<SubchannelWrapper*> subchannel_wrappers_;
// Pending ConnectedSubchannel updates for each SubchannelWrapper.
// Updates are queued here in the control plane work_serializer and then
// applied in the data plane mutex when the picker is updated.
std::map<RefCountedPtr<SubchannelWrapper>, RefCountedPtr<ConnectedSubchannel>>
pending_subchannel_updates_;
int keepalive_time_ = -1;
//
// Fields accessed from both data plane mutex and control plane
// work_serializer.
//
Atomic<grpc_error*> disconnect_error_;
//
// Fields guarded by a mutex, since they need to be accessed
// synchronously via get_channel_info().
//
Mutex info_mu_;
UniquePtr<char> info_lb_policy_name_ ABSL_GUARDED_BY(info_mu_);
UniquePtr<char> info_service_config_json_ ABSL_GUARDED_BY(info_mu_);
//
// Fields guarded by a mutex, since they need to be accessed
// synchronously via grpc_channel_num_external_connectivity_watchers().
//
mutable Mutex external_watchers_mu_;
std::map<grpc_closure*, RefCountedPtr<ExternalConnectivityWatcher>>
external_watchers_ ABSL_GUARDED_BY(external_watchers_mu_);
};
// Starts a one-time connectivity state watch. When the channel's state
// becomes different from *state, sets *state to the new state and
// schedules on_complete. The watcher_timer_init callback is invoked as
// soon as the watch is actually started (i.e., after hopping into the
// client channel combiner). I/O will be serviced via pollent.
//
// This is intended to be used when starting a watch from outside of C-core
// via grpc_channel_watch_connectivity_state(). It should not be used
// by other callers.
void grpc_client_channel_watch_connectivity_state(
grpc_channel_element* elem, grpc_polling_entity pollent,
grpc_connectivity_state* state, grpc_closure* on_complete,
grpc_closure* watcher_timer_init);
// Starts and stops a connectivity watch. The watcher will be initially
// notified as soon as the state changes from initial_state and then on
// every subsequent state change until either the watch is stopped or
// it is notified that the state has changed to SHUTDOWN.
// ClientChannel::LoadBalancedCall
//
// This is intended to be used when starting watches from code inside of
// C-core (e.g., for a nested control plane channel for things like xds).
void grpc_client_channel_start_connectivity_watch(
grpc_channel_element* elem, grpc_connectivity_state initial_state,
grpc_core::OrphanablePtr<grpc_core::AsyncConnectivityStateWatcherInterface>
watcher);
void grpc_client_channel_stop_connectivity_watch(
grpc_channel_element* elem,
grpc_core::AsyncConnectivityStateWatcherInterface* watcher);
// This object is ref-counted, but it cannot inherit from RefCounted<>,
// because it is allocated on the arena and can't free its memory when
// its refcount goes to zero. So instead, it manually implements the
// same API as RefCounted<>, so that it can be used with RefCountedPtr<>.
class ClientChannel::LoadBalancedCall {
public:
LoadBalancedCall(ClientChannel* chand, const grpc_call_element_args& args,
grpc_polling_entity* pollent);
~LoadBalancedCall();
// Interface of RefCounted<>.
RefCountedPtr<LoadBalancedCall> Ref() GRPC_MUST_USE_RESULT;
RefCountedPtr<LoadBalancedCall> Ref(const DebugLocation& location,
const char* reason) GRPC_MUST_USE_RESULT;
// When refcount drops to 0, destroys itself and the associated call stack,
// but does NOT free the memory because it's in the call arena.
void Unref();
void Unref(const DebugLocation& location, const char* reason);
void* GetParentData();
void StartTransportStreamOpBatch(grpc_transport_stream_op_batch* batch);
// Invoked by channel for queued LB picks when the picker is updated.
static void PickSubchannel(void* arg, grpc_error* error);
// Helper function for performing an LB pick while holding the data plane
// mutex. Returns true if the pick is complete, in which case the caller
// must invoke PickDone() or AsyncPickDone() with the returned error.
bool PickSubchannelLocked(grpc_error** error)
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&ClientChannel::data_plane_mu_);
// Schedules a callback to process the completed pick. The callback
// will not run until after this method returns.
void AsyncPickDone(grpc_error* error);
RefCountedPtr<SubchannelCall> subchannel_call() const {
return subchannel_call_;
}
private:
// Allow RefCountedPtr<> to access IncrementRefCount().
template <typename T>
friend class ::grpc_core::RefCountedPtr;
class LbQueuedCallCanceller;
class Metadata;
class LbCallState;
// Interface of RefCounted<>.
void IncrementRefCount();
void IncrementRefCount(const DebugLocation& location, const char* reason);
// Returns the index into pending_batches_ to be used for batch.
static size_t GetBatchIndex(grpc_transport_stream_op_batch* batch);
void PendingBatchesAdd(grpc_transport_stream_op_batch* batch);
static void FailPendingBatchInCallCombiner(void* arg, grpc_error* error);
// A predicate type and some useful implementations for PendingBatchesFail().
typedef bool (*YieldCallCombinerPredicate)(
const CallCombinerClosureList& closures);
static bool YieldCallCombiner(const CallCombinerClosureList& /*closures*/) {
return true;
}
static bool NoYieldCallCombiner(const CallCombinerClosureList& /*closures*/) {
return false;
}
static bool YieldCallCombinerIfPendingBatchesFound(
const CallCombinerClosureList& closures) {
return closures.size() > 0;
}
// Fails all pending batches.
// If yield_call_combiner_predicate returns true, assumes responsibility for
// yielding the call combiner.
void PendingBatchesFail(
grpc_error* error,
YieldCallCombinerPredicate yield_call_combiner_predicate);
static void ResumePendingBatchInCallCombiner(void* arg, grpc_error* ignored);
// Resumes all pending batches on subchannel_call_.
void PendingBatchesResume();
static void RecvTrailingMetadataReadyForLoadBalancingPolicy(
void* arg, grpc_error* error);
void InjectRecvTrailingMetadataReadyForLoadBalancingPolicy(
grpc_transport_stream_op_batch* batch);
void CreateSubchannelCall();
// Invoked when a pick is completed, on both success or failure.
static void PickDone(void* arg, grpc_error* error);
// Removes the call from the channel's list of queued picks if present.
void MaybeRemoveCallFromLbQueuedCallsLocked()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&ClientChannel::data_plane_mu_);
// Adds the call to the channel's list of queued picks if not already present.
void MaybeAddCallToLbQueuedCallsLocked()
ABSL_EXCLUSIVE_LOCKS_REQUIRED(&ClientChannel::data_plane_mu_);
RefCount refs_;
ClientChannel* chand_;
// TODO(roth): Instead of duplicating these fields in every filter
// that uses any one of them, we should store them in the call
// context. This will save per-call memory overhead.
grpc_slice path_; // Request path.
gpr_cycle_counter call_start_time_;
grpc_millis deadline_;
Arena* arena_;
grpc_call_stack* owning_call_;
CallCombiner* call_combiner_;
grpc_call_context_element* call_context_;
// Set when we get a cancel_stream op.
grpc_error* cancel_error_ = GRPC_ERROR_NONE;
grpc_polling_entity* pollent_ = nullptr;
grpc_closure pick_closure_;
// Accessed while holding ClientChannel::data_plane_mu_.
ClientChannel::LbQueuedCall queued_call_;
bool queued_pending_lb_pick_ = false;
const LoadBalancingPolicy::BackendMetricData* backend_metric_data_ = nullptr;
RefCountedPtr<ConnectedSubchannel> connected_subchannel_;
std::function<void(grpc_error*, LoadBalancingPolicy::MetadataInterface*,
LoadBalancingPolicy::CallState*)>
lb_recv_trailing_metadata_ready_;
LbQueuedCallCanceller* lb_call_canceller_ = nullptr;
RefCountedPtr<SubchannelCall> subchannel_call_;
// For intercepting recv_trailing_metadata_ready for the LB policy.
grpc_metadata_batch* recv_trailing_metadata_ = nullptr;
grpc_closure recv_trailing_metadata_ready_;
grpc_closure* original_recv_trailing_metadata_ready_ = nullptr;
// Batches are added to this list when received from above.
// They are removed when we are done handling the batch (i.e., when
// either we have invoked all of the batch's callbacks or we have
// passed the batch down to the subchannel call and are not
// intercepting any of its callbacks).
grpc_transport_stream_op_batch* pending_batches_[MAX_PENDING_BATCHES] = {};
};
} // namespace grpc_core
#endif // GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_CLIENT_CHANNEL_H

@ -34,6 +34,7 @@
#include "src/core/ext/filters/client_channel/proxy_mapper_registry.h"
#include "src/core/ext/filters/client_channel/resolver_registry.h"
#include "src/core/ext/filters/client_channel/resolver_result_parsing.h"
#include "src/core/ext/filters/client_channel/retry_service_config.h"
#include "src/core/ext/filters/client_channel/retry_throttle.h"
#include "src/core/ext/filters/client_channel/service_config_parser.h"
#include "src/core/lib/surface/channel_init.h"
@ -46,6 +47,7 @@ static bool append_filter(grpc_channel_stack_builder* builder, void* arg) {
void grpc_client_channel_init(void) {
grpc_core::ServiceConfigParser::Init();
grpc_core::internal::ClientChannelServiceConfigParser::Register();
grpc_core::internal::RetryServiceConfigParser::Register();
grpc_core::LoadBalancingPolicyRegistry::Builder::InitRegistry();
grpc_core::ResolverRegistry::Builder::InitRegistry();
grpc_core::internal::ServerRetryThrottleMap::Init();
@ -54,7 +56,8 @@ void grpc_client_channel_init(void) {
grpc_core::GlobalSubchannelPool::Init();
grpc_channel_init_register_stage(
GRPC_CLIENT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, append_filter,
const_cast<grpc_channel_filter*>(&grpc_client_channel_filter));
const_cast<grpc_channel_filter*>(
&grpc_core::ClientChannel::kFilterVtable));
grpc_http_connect_register_handshaker_factory();
grpc_client_channel_global_init_backup_polling();
}

@ -1420,13 +1420,12 @@ void GrpcLb::UpdateLocked(UpdateArgs args) {
// Start watching the channel's connectivity state. If the channel
// goes into state TRANSIENT_FAILURE before the timer fires, we go into
// fallback mode even if the fallback timeout has not elapsed.
grpc_channel_element* client_channel_elem = grpc_channel_stack_last_element(
grpc_channel_get_channel_stack(lb_channel_));
GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter);
ClientChannel* client_channel = ClientChannel::GetFromChannel(lb_channel_);
GPR_ASSERT(client_channel != nullptr);
// Ref held by callback.
watcher_ = new StateWatcher(Ref(DEBUG_LOCATION, "StateWatcher"));
grpc_client_channel_start_connectivity_watch(
client_channel_elem, GRPC_CHANNEL_IDLE,
client_channel->AddConnectivityWatcher(
GRPC_CHANNEL_IDLE,
OrphanablePtr<AsyncConnectivityStateWatcherInterface>(watcher_));
// Start balancer call.
StartBalancerCallLocked();
@ -1490,10 +1489,9 @@ void GrpcLb::ProcessAddressesAndChannelArgsLocked(
}
void GrpcLb::CancelBalancerChannelConnectivityWatchLocked() {
grpc_channel_element* client_channel_elem = grpc_channel_stack_last_element(
grpc_channel_get_channel_stack(lb_channel_));
GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter);
grpc_client_channel_stop_connectivity_watch(client_channel_elem, watcher_);
ClientChannel* client_channel = ClientChannel::GetFromChannel(lb_channel_);
GPR_ASSERT(client_channel != nullptr);
client_channel->RemoveConnectivityWatcher(watcher_);
}
//

@ -1,20 +1,18 @@
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
//
// Copyright 2018 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include <grpc/support/port_platform.h>
@ -63,191 +61,6 @@ void ClientChannelServiceConfigParser::Register() {
namespace {
std::unique_ptr<ClientChannelMethodParsedConfig::RetryPolicy> ParseRetryPolicy(
const Json& json, grpc_error** error) {
GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
auto retry_policy =
absl::make_unique<ClientChannelMethodParsedConfig::RetryPolicy>();
if (json.type() != Json::Type::OBJECT) {
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryPolicy error:should be of type object");
return nullptr;
}
std::vector<grpc_error*> error_list;
// Parse maxAttempts.
auto it = json.object_value().find("maxAttempts");
if (it != json.object_value().end()) {
if (it->second.type() != Json::Type::NUMBER) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:maxAttempts error:should be of type number"));
} else {
retry_policy->max_attempts =
gpr_parse_nonnegative_int(it->second.string_value().c_str());
if (retry_policy->max_attempts <= 1) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:maxAttempts error:should be at least 2"));
} else if (retry_policy->max_attempts > MAX_MAX_RETRY_ATTEMPTS) {
gpr_log(GPR_ERROR,
"service config: clamped retryPolicy.maxAttempts at %d",
MAX_MAX_RETRY_ATTEMPTS);
retry_policy->max_attempts = MAX_MAX_RETRY_ATTEMPTS;
}
}
}
// Parse initialBackoff.
if (ParseJsonObjectFieldAsDuration(json.object_value(), "initialBackoff",
&retry_policy->initial_backoff,
&error_list) &&
retry_policy->initial_backoff == 0) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:initialBackoff error:must be greater than 0"));
}
// Parse maxBackoff.
if (ParseJsonObjectFieldAsDuration(json.object_value(), "maxBackoff",
&retry_policy->max_backoff, &error_list) &&
retry_policy->max_backoff == 0) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:maxBackoff error:should be greater than 0"));
}
// Parse backoffMultiplier.
it = json.object_value().find("backoffMultiplier");
if (it != json.object_value().end()) {
if (it->second.type() != Json::Type::NUMBER) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:backoffMultiplier error:should be of type number"));
} else {
if (sscanf(it->second.string_value().c_str(), "%f",
&retry_policy->backoff_multiplier) != 1) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:backoffMultiplier error:failed to parse"));
} else if (retry_policy->backoff_multiplier <= 0) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:backoffMultiplier error:should be greater than 0"));
}
}
}
// Parse retryableStatusCodes.
it = json.object_value().find("retryableStatusCodes");
if (it != json.object_value().end()) {
if (it->second.type() != Json::Type::ARRAY) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryableStatusCodes error:should be of type array"));
} else {
for (const Json& element : it->second.array_value()) {
if (element.type() != Json::Type::STRING) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryableStatusCodes error:status codes should be of type "
"string"));
continue;
}
grpc_status_code status;
if (!grpc_status_code_from_string(element.string_value().c_str(),
&status)) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryableStatusCodes error:failed to parse status code"));
continue;
}
retry_policy->retryable_status_codes.Add(status);
}
if (retry_policy->retryable_status_codes.Empty()) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryableStatusCodes error:should be non-empty"));
};
}
}
// Make sure required fields are set.
if (error_list.empty()) {
if (retry_policy->max_attempts == 0 || retry_policy->initial_backoff == 0 ||
retry_policy->max_backoff == 0 ||
retry_policy->backoff_multiplier == 0 ||
retry_policy->retryable_status_codes.Empty()) {
*error = GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryPolicy error:Missing required field(s)");
return nullptr;
}
}
*error = GRPC_ERROR_CREATE_FROM_VECTOR("retryPolicy", &error_list);
return *error == GRPC_ERROR_NONE ? std::move(retry_policy) : nullptr;
}
grpc_error* ParseRetryThrottling(
const Json& json,
ClientChannelGlobalParsedConfig::RetryThrottling* retry_throttling) {
if (json.type() != Json::Type::OBJECT) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryThrottling error:Type should be object");
}
std::vector<grpc_error*> error_list;
// Parse maxTokens.
auto it = json.object_value().find("maxTokens");
if (it == json.object_value().end()) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryThrottling field:maxTokens error:Not found"));
} else if (it->second.type() != Json::Type::NUMBER) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryThrottling field:maxTokens error:Type should be "
"number"));
} else {
retry_throttling->max_milli_tokens =
gpr_parse_nonnegative_int(it->second.string_value().c_str()) * 1000;
if (retry_throttling->max_milli_tokens <= 0) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryThrottling field:maxTokens error:should be "
"greater than zero"));
}
}
// Parse tokenRatio.
it = json.object_value().find("tokenRatio");
if (it == json.object_value().end()) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryThrottling field:tokenRatio error:Not found"));
} else if (it->second.type() != Json::Type::NUMBER) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryThrottling field:tokenRatio error:type should be "
"number"));
} else {
// We support up to 3 decimal digits.
size_t whole_len = it->second.string_value().size();
const char* value = it->second.string_value().c_str();
uint32_t multiplier = 1;
uint32_t decimal_value = 0;
const char* decimal_point = strchr(value, '.');
if (decimal_point != nullptr) {
whole_len = static_cast<size_t>(decimal_point - value);
multiplier = 1000;
size_t decimal_len = strlen(decimal_point + 1);
if (decimal_len > 3) decimal_len = 3;
if (!gpr_parse_bytes_to_uint32(decimal_point + 1, decimal_len,
&decimal_value)) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryThrottling field:tokenRatio error:Failed "
"parsing"));
return GRPC_ERROR_CREATE_FROM_VECTOR("retryPolicy", &error_list);
}
uint32_t decimal_multiplier = 1;
for (size_t i = 0; i < (3 - decimal_len); ++i) {
decimal_multiplier *= 10;
}
decimal_value *= decimal_multiplier;
}
uint32_t whole_value;
if (!gpr_parse_bytes_to_uint32(value, whole_len, &whole_value)) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryThrottling field:tokenRatio error:Failed "
"parsing"));
return GRPC_ERROR_CREATE_FROM_VECTOR("retryPolicy", &error_list);
}
retry_throttling->milli_token_ratio =
static_cast<int>((whole_value * multiplier) + decimal_value);
if (retry_throttling->milli_token_ratio <= 0) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryThrottling field:tokenRatio error:value should "
"be greater than 0"));
}
}
return GRPC_ERROR_CREATE_FROM_VECTOR("retryPolicy", &error_list);
}
absl::optional<std::string> ParseHealthCheckConfig(const Json& field,
grpc_error** error) {
GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
@ -319,19 +132,6 @@ ClientChannelServiceConfigParser::ParseGlobalParams(
}
}
}
// Parse retry throttling.
absl::optional<ClientChannelGlobalParsedConfig::RetryThrottling>
retry_throttling;
it = json.object_value().find("retryThrottling");
if (it != json.object_value().end()) {
ClientChannelGlobalParsedConfig::RetryThrottling data;
grpc_error* parsing_error = ParseRetryThrottling(it->second, &data);
if (parsing_error != GRPC_ERROR_NONE) {
error_list.push_back(parsing_error);
} else {
retry_throttling.emplace(data);
}
}
// Parse health check config.
absl::optional<std::string> health_check_service_name;
it = json.object_value().find("healthCheckConfig");
@ -348,7 +148,7 @@ ClientChannelServiceConfigParser::ParseGlobalParams(
if (*error == GRPC_ERROR_NONE) {
return absl::make_unique<ClientChannelGlobalParsedConfig>(
std::move(parsed_lb_config), std::move(lb_policy_name),
retry_throttling, std::move(health_check_service_name));
std::move(health_check_service_name));
}
return nullptr;
}
@ -358,10 +158,8 @@ ClientChannelServiceConfigParser::ParsePerMethodParams(
const grpc_channel_args* /*args*/, const Json& json, grpc_error** error) {
GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
std::vector<grpc_error*> error_list;
absl::optional<bool> wait_for_ready;
grpc_millis timeout = 0;
std::unique_ptr<ClientChannelMethodParsedConfig::RetryPolicy> retry_policy;
// Parse waitForReady.
absl::optional<bool> wait_for_ready;
auto it = json.object_value().find("waitForReady");
if (it != json.object_value().end()) {
if (it->second.type() == Json::Type::JSON_TRUE) {
@ -374,21 +172,14 @@ ClientChannelServiceConfigParser::ParsePerMethodParams(
}
}
// Parse timeout.
grpc_millis timeout = 0;
ParseJsonObjectFieldAsDuration(json.object_value(), "timeout", &timeout,
&error_list, false);
// Parse retry policy.
it = json.object_value().find("retryPolicy");
if (it != json.object_value().end()) {
grpc_error* error = GRPC_ERROR_NONE;
retry_policy = ParseRetryPolicy(it->second, &error);
if (retry_policy == nullptr) {
error_list.push_back(error);
}
}
// Return result.
*error = GRPC_ERROR_CREATE_FROM_VECTOR("Client channel parser", &error_list);
if (*error == GRPC_ERROR_NONE) {
return absl::make_unique<ClientChannelMethodParsedConfig>(
timeout, wait_for_ready, std::move(retry_policy));
return absl::make_unique<ClientChannelMethodParsedConfig>(timeout,
wait_for_ready);
}
return nullptr;
}

@ -1,20 +1,18 @@
/*
*
* Copyright 2018 gRPC authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
//
// Copyright 2018 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_RESULT_PARSING_H
#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_RESULT_PARSING_H
@ -26,7 +24,6 @@
#include "src/core/ext/filters/client_channel/lb_policy.h"
#include "src/core/ext/filters/client_channel/lb_policy_factory.h"
#include "src/core/ext/filters/client_channel/resolver.h"
#include "src/core/ext/filters/client_channel/retry_throttle.h"
#include "src/core/ext/filters/client_channel/service_config.h"
#include "src/core/lib/channel/status_util.h"
#include "src/core/lib/gprpp/ref_counted.h"
@ -40,19 +37,12 @@ namespace internal {
class ClientChannelGlobalParsedConfig
: public ServiceConfigParser::ParsedConfig {
public:
struct RetryThrottling {
intptr_t max_milli_tokens = 0;
intptr_t milli_token_ratio = 0;
};
ClientChannelGlobalParsedConfig(
RefCountedPtr<LoadBalancingPolicy::Config> parsed_lb_config,
std::string parsed_deprecated_lb_policy,
const absl::optional<RetryThrottling>& retry_throttling,
absl::optional<std::string> health_check_service_name)
: parsed_lb_config_(std::move(parsed_lb_config)),
parsed_deprecated_lb_policy_(std::move(parsed_deprecated_lb_policy)),
retry_throttling_(retry_throttling),
health_check_service_name_(std::move(health_check_service_name)) {}
RefCountedPtr<LoadBalancingPolicy::Config> parsed_lb_config() const {
@ -63,10 +53,6 @@ class ClientChannelGlobalParsedConfig
return parsed_deprecated_lb_policy_;
}
absl::optional<RetryThrottling> retry_throttling() const {
return retry_throttling_;
}
const absl::optional<std::string>& health_check_service_name() const {
return health_check_service_name_;
}
@ -74,38 +60,23 @@ class ClientChannelGlobalParsedConfig
private:
RefCountedPtr<LoadBalancingPolicy::Config> parsed_lb_config_;
std::string parsed_deprecated_lb_policy_;
absl::optional<RetryThrottling> retry_throttling_;
absl::optional<std::string> health_check_service_name_;
};
class ClientChannelMethodParsedConfig
: public ServiceConfigParser::ParsedConfig {
public:
struct RetryPolicy {
int max_attempts = 0;
grpc_millis initial_backoff = 0;
grpc_millis max_backoff = 0;
float backoff_multiplier = 0;
StatusCodeSet retryable_status_codes;
};
ClientChannelMethodParsedConfig(grpc_millis timeout,
const absl::optional<bool>& wait_for_ready,
std::unique_ptr<RetryPolicy> retry_policy)
: timeout_(timeout),
wait_for_ready_(wait_for_ready),
retry_policy_(std::move(retry_policy)) {}
const absl::optional<bool>& wait_for_ready)
: timeout_(timeout), wait_for_ready_(wait_for_ready) {}
grpc_millis timeout() const { return timeout_; }
absl::optional<bool> wait_for_ready() const { return wait_for_ready_; }
const RetryPolicy* retry_policy() const { return retry_policy_.get(); }
private:
grpc_millis timeout_ = 0;
absl::optional<bool> wait_for_ready_;
std::unique_ptr<RetryPolicy> retry_policy_;
};
class ClientChannelServiceConfigParser : public ServiceConfigParser::Parser {
@ -125,4 +96,4 @@ class ClientChannelServiceConfigParser : public ServiceConfigParser::Parser {
} // namespace internal
} // namespace grpc_core
#endif /* GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_RESULT_PARSING_H */
#endif // GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RESOLVER_RESULT_PARSING_H

File diff suppressed because it is too large Load Diff

@ -0,0 +1,30 @@
//
// Copyright 2021 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RETRY_FILTER_H
#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RETRY_FILTER_H
#include <grpc/support/port_platform.h>
#include "src/core/lib/channel/channel_stack.h"
namespace grpc_core {
extern const grpc_channel_filter kRetryFilterVtable;
} // namespace grpc_core
#endif // GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RETRY_FILTER_H

@ -0,0 +1,285 @@
//
// Copyright 2018 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#include <grpc/support/port_platform.h>
#include "src/core/ext/filters/client_channel/retry_service_config.h"
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "absl/strings/str_cat.h"
#include "absl/types/optional.h"
#include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include "src/core/ext/filters/client_channel/client_channel.h"
#include "src/core/ext/filters/client_channel/lb_policy_registry.h"
#include "src/core/ext/filters/client_channel/server_address.h"
#include "src/core/lib/channel/channel_args.h"
#include "src/core/lib/channel/status_util.h"
#include "src/core/lib/gpr/string.h"
#include "src/core/lib/gprpp/memory.h"
#include "src/core/lib/json/json_util.h"
#include "src/core/lib/uri/uri_parser.h"
// As per the retry design, we do not allow more than 5 retry attempts.
#define MAX_MAX_RETRY_ATTEMPTS 5
namespace grpc_core {
namespace internal {
namespace {
size_t g_retry_service_config_parser_index;
}
size_t RetryServiceConfigParser::ParserIndex() {
return g_retry_service_config_parser_index;
}
void RetryServiceConfigParser::Register() {
g_retry_service_config_parser_index = ServiceConfigParser::RegisterParser(
absl::make_unique<RetryServiceConfigParser>());
}
namespace {
grpc_error* ParseRetryThrottling(const Json& json, intptr_t* max_milli_tokens,
intptr_t* milli_token_ratio) {
if (json.type() != Json::Type::OBJECT) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryThrottling error:Type should be object");
}
std::vector<grpc_error*> error_list;
// Parse maxTokens.
auto it = json.object_value().find("maxTokens");
if (it == json.object_value().end()) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryThrottling field:maxTokens error:Not found"));
} else if (it->second.type() != Json::Type::NUMBER) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryThrottling field:maxTokens error:Type should be "
"number"));
} else {
*max_milli_tokens =
gpr_parse_nonnegative_int(it->second.string_value().c_str()) * 1000;
if (*max_milli_tokens <= 0) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryThrottling field:maxTokens error:should be "
"greater than zero"));
}
}
// Parse tokenRatio.
it = json.object_value().find("tokenRatio");
if (it == json.object_value().end()) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryThrottling field:tokenRatio error:Not found"));
} else if (it->second.type() != Json::Type::NUMBER) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryThrottling field:tokenRatio error:type should be "
"number"));
} else {
// We support up to 3 decimal digits.
size_t whole_len = it->second.string_value().size();
const char* value = it->second.string_value().c_str();
uint32_t multiplier = 1;
uint32_t decimal_value = 0;
const char* decimal_point = strchr(value, '.');
if (decimal_point != nullptr) {
whole_len = static_cast<size_t>(decimal_point - value);
multiplier = 1000;
size_t decimal_len = strlen(decimal_point + 1);
if (decimal_len > 3) decimal_len = 3;
if (!gpr_parse_bytes_to_uint32(decimal_point + 1, decimal_len,
&decimal_value)) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryThrottling field:tokenRatio error:Failed "
"parsing"));
return GRPC_ERROR_CREATE_FROM_VECTOR("retryThrottling", &error_list);
}
uint32_t decimal_multiplier = 1;
for (size_t i = 0; i < (3 - decimal_len); ++i) {
decimal_multiplier *= 10;
}
decimal_value *= decimal_multiplier;
}
uint32_t whole_value;
if (!gpr_parse_bytes_to_uint32(value, whole_len, &whole_value)) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryThrottling field:tokenRatio error:Failed "
"parsing"));
return GRPC_ERROR_CREATE_FROM_VECTOR("retryThrottling", &error_list);
}
*milli_token_ratio =
static_cast<int>((whole_value * multiplier) + decimal_value);
if (*milli_token_ratio <= 0) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryThrottling field:tokenRatio error:value should "
"be greater than 0"));
}
}
return GRPC_ERROR_CREATE_FROM_VECTOR("retryThrottling", &error_list);
}
} // namespace
std::unique_ptr<ServiceConfigParser::ParsedConfig>
RetryServiceConfigParser::ParseGlobalParams(const grpc_channel_args* /*args*/,
const Json& json,
grpc_error** error) {
GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
auto it = json.object_value().find("retryThrottling");
if (it == json.object_value().end()) return nullptr;
intptr_t max_milli_tokens = 0;
intptr_t milli_token_ratio = 0;
*error =
ParseRetryThrottling(it->second, &max_milli_tokens, &milli_token_ratio);
if (*error != GRPC_ERROR_NONE) return nullptr;
return absl::make_unique<RetryGlobalConfig>(max_milli_tokens,
milli_token_ratio);
}
namespace {
grpc_error* ParseRetryPolicy(const Json& json, int* max_attempts,
grpc_millis* initial_backoff,
grpc_millis* max_backoff,
float* backoff_multiplier,
StatusCodeSet* retryable_status_codes) {
if (json.type() != Json::Type::OBJECT) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryPolicy error:should be of type object");
}
std::vector<grpc_error*> error_list;
// Parse maxAttempts.
auto it = json.object_value().find("maxAttempts");
if (it != json.object_value().end()) {
if (it->second.type() != Json::Type::NUMBER) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:maxAttempts error:should be of type number"));
} else {
*max_attempts =
gpr_parse_nonnegative_int(it->second.string_value().c_str());
if (*max_attempts <= 1) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:maxAttempts error:should be at least 2"));
} else if (*max_attempts > MAX_MAX_RETRY_ATTEMPTS) {
gpr_log(GPR_ERROR,
"service config: clamped retryPolicy.maxAttempts at %d",
MAX_MAX_RETRY_ATTEMPTS);
*max_attempts = MAX_MAX_RETRY_ATTEMPTS;
}
}
}
// Parse initialBackoff.
if (ParseJsonObjectFieldAsDuration(json.object_value(), "initialBackoff",
initial_backoff, &error_list) &&
*initial_backoff == 0) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:initialBackoff error:must be greater than 0"));
}
// Parse maxBackoff.
if (ParseJsonObjectFieldAsDuration(json.object_value(), "maxBackoff",
max_backoff, &error_list) &&
*max_backoff == 0) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:maxBackoff error:should be greater than 0"));
}
// Parse backoffMultiplier.
it = json.object_value().find("backoffMultiplier");
if (it != json.object_value().end()) {
if (it->second.type() != Json::Type::NUMBER) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:backoffMultiplier error:should be of type number"));
} else {
if (sscanf(it->second.string_value().c_str(), "%f", backoff_multiplier) !=
1) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:backoffMultiplier error:failed to parse"));
} else if (*backoff_multiplier <= 0) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:backoffMultiplier error:should be greater than 0"));
}
}
}
// Parse retryableStatusCodes.
it = json.object_value().find("retryableStatusCodes");
if (it != json.object_value().end()) {
if (it->second.type() != Json::Type::ARRAY) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryableStatusCodes error:should be of type array"));
} else {
for (const Json& element : it->second.array_value()) {
if (element.type() != Json::Type::STRING) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryableStatusCodes error:status codes should be of type "
"string"));
continue;
}
grpc_status_code status;
if (!grpc_status_code_from_string(element.string_value().c_str(),
&status)) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryableStatusCodes error:failed to parse status code"));
continue;
}
retryable_status_codes->Add(status);
}
if (retryable_status_codes->Empty()) {
error_list.push_back(GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryableStatusCodes error:should be non-empty"));
};
}
}
// Make sure required fields are set.
if (error_list.empty()) {
if (*max_attempts == 0 || *initial_backoff == 0 || *max_backoff == 0 ||
*backoff_multiplier == 0 || retryable_status_codes->Empty()) {
return GRPC_ERROR_CREATE_FROM_STATIC_STRING(
"field:retryPolicy error:Missing required field(s)");
}
}
return GRPC_ERROR_CREATE_FROM_VECTOR("retryPolicy", &error_list);
}
} // namespace
std::unique_ptr<ServiceConfigParser::ParsedConfig>
RetryServiceConfigParser::ParsePerMethodParams(
const grpc_channel_args* /*args*/, const Json& json, grpc_error** error) {
GPR_DEBUG_ASSERT(error != nullptr && *error == GRPC_ERROR_NONE);
// Parse retry policy.
auto it = json.object_value().find("retryPolicy");
if (it == json.object_value().end()) return nullptr;
int max_attempts = 0;
grpc_millis initial_backoff = 0;
grpc_millis max_backoff = 0;
float backoff_multiplier = 0;
StatusCodeSet retryable_status_codes;
*error = ParseRetryPolicy(it->second, &max_attempts, &initial_backoff,
&max_backoff, &backoff_multiplier,
&retryable_status_codes);
if (*error != GRPC_ERROR_NONE) return nullptr;
return absl::make_unique<RetryMethodConfig>(max_attempts, initial_backoff,
max_backoff, backoff_multiplier,
retryable_status_codes);
}
} // namespace internal
} // namespace grpc_core

@ -0,0 +1,90 @@
//
// Copyright 2018 gRPC authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#ifndef GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RETRY_SERVICE_CONFIG_H
#define GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RETRY_SERVICE_CONFIG_H
#include <grpc/support/port_platform.h>
#include <memory>
#include "src/core/ext/filters/client_channel/retry_throttle.h"
#include "src/core/ext/filters/client_channel/service_config_parser.h"
#include "src/core/lib/channel/status_util.h"
#include "src/core/lib/iomgr/exec_ctx.h" // for grpc_millis
namespace grpc_core {
namespace internal {
class RetryGlobalConfig : public ServiceConfigParser::ParsedConfig {
public:
RetryGlobalConfig(intptr_t max_milli_tokens, intptr_t milli_token_ratio)
: max_milli_tokens_(max_milli_tokens),
milli_token_ratio_(milli_token_ratio) {}
intptr_t max_milli_tokens() const { return max_milli_tokens_; }
intptr_t milli_token_ratio() const { return milli_token_ratio_; }
private:
intptr_t max_milli_tokens_ = 0;
intptr_t milli_token_ratio_ = 0;
};
class RetryMethodConfig : public ServiceConfigParser::ParsedConfig {
public:
RetryMethodConfig(int max_attempts, grpc_millis initial_backoff,
grpc_millis max_backoff, float backoff_multiplier,
StatusCodeSet retryable_status_codes)
: max_attempts_(max_attempts),
initial_backoff_(initial_backoff),
max_backoff_(max_backoff),
backoff_multiplier_(backoff_multiplier),
retryable_status_codes_(retryable_status_codes) {}
int max_attempts() const { return max_attempts_; }
grpc_millis initial_backoff() const { return initial_backoff_; }
grpc_millis max_backoff() const { return max_backoff_; }
float backoff_multiplier() const { return backoff_multiplier_; }
StatusCodeSet retryable_status_codes() const {
return retryable_status_codes_;
}
private:
int max_attempts_ = 0;
grpc_millis initial_backoff_ = 0;
grpc_millis max_backoff_ = 0;
float backoff_multiplier_ = 0;
StatusCodeSet retryable_status_codes_;
};
class RetryServiceConfigParser : public ServiceConfigParser::Parser {
public:
std::unique_ptr<ServiceConfigParser::ParsedConfig> ParseGlobalParams(
const grpc_channel_args* /*args*/, const Json& json,
grpc_error** error) override;
std::unique_ptr<ServiceConfigParser::ParsedConfig> ParsePerMethodParams(
const grpc_channel_args* /*args*/, const Json& json,
grpc_error** error) override;
static size_t ParserIndex();
static void Register();
};
} // namespace internal
} // namespace grpc_core
#endif // GRPC_CORE_EXT_FILTERS_CLIENT_CHANNEL_RETRY_SERVICE_CONFIG_H

@ -529,20 +529,18 @@ void XdsClient::ChannelState::MaybeStartLrsCall() {
void XdsClient::ChannelState::StopLrsCall() { lrs_calld_.reset(); }
void XdsClient::ChannelState::StartConnectivityWatchLocked() {
grpc_channel_element* client_channel_elem =
grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel_));
GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter);
ClientChannel* client_channel = ClientChannel::GetFromChannel(channel_);
GPR_ASSERT(client_channel != nullptr);
watcher_ = new StateWatcher(Ref(DEBUG_LOCATION, "ChannelState+watch"));
grpc_client_channel_start_connectivity_watch(
client_channel_elem, GRPC_CHANNEL_IDLE,
client_channel->AddConnectivityWatcher(
GRPC_CHANNEL_IDLE,
OrphanablePtr<AsyncConnectivityStateWatcherInterface>(watcher_));
}
void XdsClient::ChannelState::CancelConnectivityWatchLocked() {
grpc_channel_element* client_channel_elem =
grpc_channel_stack_last_element(grpc_channel_get_channel_stack(channel_));
GPR_ASSERT(client_channel_elem->filter == &grpc_client_channel_filter);
grpc_client_channel_stop_connectivity_watch(client_channel_elem, watcher_);
ClientChannel* client_channel = ClientChannel::GetFromChannel(channel_);
GPR_ASSERT(client_channel != nullptr);
client_channel->RemoveConnectivityWatcher(watcher_);
}
void XdsClient::ChannelState::SubscribeLocked(const std::string& type_url,

@ -67,6 +67,8 @@ CORE_SOURCE_FILES = [
'src/core/ext/filters/client_channel/resolver/xds/xds_resolver.cc',
'src/core/ext/filters/client_channel/resolver_registry.cc',
'src/core/ext/filters/client_channel/resolver_result_parsing.cc',
'src/core/ext/filters/client_channel/retry_filter.cc',
'src/core/ext/filters/client_channel/retry_service_config.cc',
'src/core/ext/filters/client_channel/retry_throttle.cc',
'src/core/ext/filters/client_channel/server_address.cc',
'src/core/ext/filters/client_channel/service_config.cc',

@ -23,6 +23,7 @@
#include <grpc/grpc.h>
#include "src/core/ext/filters/client_channel/resolver_result_parsing.h"
#include "src/core/ext/filters/client_channel/retry_service_config.h"
#include "src/core/ext/filters/client_channel/service_config.h"
#include "src/core/ext/filters/client_channel/service_config_parser.h"
#include "src/core/ext/filters/message_size/message_size_filter.h"
@ -33,6 +34,10 @@
namespace grpc_core {
namespace testing {
//
// ServiceConfig tests
//
// Set this channel arg to true to disable parsing.
#define GRPC_ARG_DISABLE_PARSING "disable_parsing"
@ -462,6 +467,10 @@ TEST_F(ErroredParsersScopingTest, MethodParams) {
GRPC_ERROR_UNREF(error);
}
//
// client_channel parser tests
//
class ClientChannelParserTest : public ::testing::Test {
protected:
void SetUp() override {
@ -621,84 +630,6 @@ TEST_F(ClientChannelParserTest, LoadBalancingPolicyXdsNotAllowed) {
GRPC_ERROR_UNREF(error);
}
TEST_F(ClientChannelParserTest, ValidRetryThrottling) {
const char* test_json =
"{\n"
" \"retryThrottling\": {\n"
" \"maxTokens\": 2,\n"
" \"tokenRatio\": 1.0\n"
" }\n"
"}";
grpc_error* error = GRPC_ERROR_NONE;
auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error);
const auto* parsed_config =
static_cast<grpc_core::internal::ClientChannelGlobalParsedConfig*>(
svc_cfg->GetGlobalParsedConfig(0));
const auto retryThrottling = parsed_config->retry_throttling();
ASSERT_TRUE(retryThrottling.has_value());
EXPECT_EQ(retryThrottling.value().max_milli_tokens, 2000);
EXPECT_EQ(retryThrottling.value().milli_token_ratio, 1000);
}
TEST_F(ClientChannelParserTest, RetryThrottlingMissingFields) {
const char* test_json =
"{\n"
" \"retryThrottling\": {\n"
" }\n"
"}";
grpc_error* error = GRPC_ERROR_NONE;
auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
EXPECT_THAT(grpc_error_string(error),
::testing::ContainsRegex(
"Service config parsing error.*referenced_errors.*"
"Global Params.*referenced_errors.*"
"Client channel global parser.*referenced_errors.*"
"field:retryThrottling field:maxTokens error:Not found.*"
"field:retryThrottling field:tokenRatio error:Not found"));
GRPC_ERROR_UNREF(error);
}
TEST_F(ClientChannelParserTest, InvalidRetryThrottlingNegativeMaxTokens) {
const char* test_json =
"{\n"
" \"retryThrottling\": {\n"
" \"maxTokens\": -2,\n"
" \"tokenRatio\": 1.0\n"
" }\n"
"}";
grpc_error* error = GRPC_ERROR_NONE;
auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
EXPECT_THAT(grpc_error_string(error),
::testing::ContainsRegex(
"Service config parsing error.*referenced_errors.*"
"Global Params.*referenced_errors.*"
"Client channel global parser.*referenced_errors.*"
"field:retryThrottling field:maxTokens error:should "
"be greater than zero"));
GRPC_ERROR_UNREF(error);
}
TEST_F(ClientChannelParserTest, InvalidRetryThrottlingInvalidTokenRatio) {
const char* test_json =
"{\n"
" \"retryThrottling\": {\n"
" \"maxTokens\": 2,\n"
" \"tokenRatio\": -1\n"
" }\n"
"}";
grpc_error* error = GRPC_ERROR_NONE;
auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
EXPECT_THAT(grpc_error_string(error),
::testing::ContainsRegex(
"Service config parsing error.*referenced_errors.*"
"Global Params.*referenced_errors.*"
"Client channel global parser.*referenced_errors.*"
"field:retryThrottling field:tokenRatio "
"error:Failed parsing"));
GRPC_ERROR_UNREF(error);
}
TEST_F(ClientChannelParserTest, ValidTimeout) {
const char* test_json =
"{\n"
@ -796,7 +727,136 @@ TEST_F(ClientChannelParserTest, InvalidWaitForReady) {
GRPC_ERROR_UNREF(error);
}
TEST_F(ClientChannelParserTest, ValidRetryPolicy) {
TEST_F(ClientChannelParserTest, ValidHealthCheck) {
const char* test_json =
"{\n"
" \"healthCheckConfig\": {\n"
" \"serviceName\": \"health_check_service_name\"\n"
" }\n"
"}";
grpc_error* error = GRPC_ERROR_NONE;
auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error);
const auto* parsed_config =
static_cast<grpc_core::internal::ClientChannelGlobalParsedConfig*>(
svc_cfg->GetGlobalParsedConfig(0));
ASSERT_NE(parsed_config, nullptr);
EXPECT_EQ(parsed_config->health_check_service_name(),
"health_check_service_name");
}
TEST_F(ClientChannelParserTest, InvalidHealthCheckMultipleEntries) {
const char* test_json =
"{\n"
" \"healthCheckConfig\": {\n"
" \"serviceName\": \"health_check_service_name\"\n"
" },\n"
" \"healthCheckConfig\": {\n"
" \"serviceName\": \"health_check_service_name1\"\n"
" }\n"
"}";
grpc_error* error = GRPC_ERROR_NONE;
auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
EXPECT_THAT(grpc_error_string(error),
::testing::ContainsRegex(
"JSON parsing failed.*referenced_errors.*"
"duplicate key \"healthCheckConfig\" at index 104"));
GRPC_ERROR_UNREF(error);
}
//
// retry parser tests
//
class RetryParserTest : public ::testing::Test {
protected:
void SetUp() override {
ServiceConfigParser::Shutdown();
ServiceConfigParser::Init();
EXPECT_EQ(ServiceConfigParser::RegisterParser(
absl::make_unique<internal::RetryServiceConfigParser>()),
0);
}
};
TEST_F(RetryParserTest, ValidRetryThrottling) {
const char* test_json =
"{\n"
" \"retryThrottling\": {\n"
" \"maxTokens\": 2,\n"
" \"tokenRatio\": 1.0\n"
" }\n"
"}";
grpc_error* error = GRPC_ERROR_NONE;
auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error);
const auto* parsed_config =
static_cast<grpc_core::internal::RetryGlobalConfig*>(
svc_cfg->GetGlobalParsedConfig(0));
ASSERT_NE(parsed_config, nullptr);
EXPECT_EQ(parsed_config->max_milli_tokens(), 2000);
EXPECT_EQ(parsed_config->milli_token_ratio(), 1000);
}
TEST_F(RetryParserTest, RetryThrottlingMissingFields) {
const char* test_json =
"{\n"
" \"retryThrottling\": {\n"
" }\n"
"}";
grpc_error* error = GRPC_ERROR_NONE;
auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
EXPECT_THAT(grpc_error_string(error),
::testing::ContainsRegex(
"Service config parsing error.*referenced_errors.*"
"Global Params.*referenced_errors.*"
"retryThrottling.*referenced_errors.*"
"field:retryThrottling field:maxTokens error:Not found.*"
"field:retryThrottling field:tokenRatio error:Not found"));
GRPC_ERROR_UNREF(error);
}
TEST_F(RetryParserTest, InvalidRetryThrottlingNegativeMaxTokens) {
const char* test_json =
"{\n"
" \"retryThrottling\": {\n"
" \"maxTokens\": -2,\n"
" \"tokenRatio\": 1.0\n"
" }\n"
"}";
grpc_error* error = GRPC_ERROR_NONE;
auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
EXPECT_THAT(grpc_error_string(error),
::testing::ContainsRegex(
"Service config parsing error.*referenced_errors.*"
"Global Params.*referenced_errors.*"
"retryThrottling.*referenced_errors.*"
"field:retryThrottling field:maxTokens error:should "
"be greater than zero"));
GRPC_ERROR_UNREF(error);
}
TEST_F(RetryParserTest, InvalidRetryThrottlingInvalidTokenRatio) {
const char* test_json =
"{\n"
" \"retryThrottling\": {\n"
" \"maxTokens\": 2,\n"
" \"tokenRatio\": -1\n"
" }\n"
"}";
grpc_error* error = GRPC_ERROR_NONE;
auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
EXPECT_THAT(grpc_error_string(error),
::testing::ContainsRegex(
"Service config parsing error.*referenced_errors.*"
"Global Params.*referenced_errors.*"
"retryThrottling.*referenced_errors.*"
"field:retryThrottling field:tokenRatio "
"error:Failed parsing"));
GRPC_ERROR_UNREF(error);
}
TEST_F(RetryParserTest, ValidRetryPolicy) {
const char* test_json =
"{\n"
" \"methodConfig\": [ {\n"
@ -819,18 +879,18 @@ TEST_F(ClientChannelParserTest, ValidRetryPolicy) {
grpc_slice_from_static_string("/TestServ/TestMethod"));
ASSERT_NE(vector_ptr, nullptr);
const auto* parsed_config =
static_cast<grpc_core::internal::ClientChannelMethodParsedConfig*>(
static_cast<grpc_core::internal::RetryMethodConfig*>(
((*vector_ptr)[0]).get());
ASSERT_NE(parsed_config->retry_policy(), nullptr);
EXPECT_EQ(parsed_config->retry_policy()->max_attempts, 3);
EXPECT_EQ(parsed_config->retry_policy()->initial_backoff, 1000);
EXPECT_EQ(parsed_config->retry_policy()->max_backoff, 120000);
EXPECT_EQ(parsed_config->retry_policy()->backoff_multiplier, 1.6f);
EXPECT_TRUE(parsed_config->retry_policy()->retryable_status_codes.Contains(
GRPC_STATUS_ABORTED));
ASSERT_NE(parsed_config, nullptr);
EXPECT_EQ(parsed_config->max_attempts(), 3);
EXPECT_EQ(parsed_config->initial_backoff(), 1000);
EXPECT_EQ(parsed_config->max_backoff(), 120000);
EXPECT_EQ(parsed_config->backoff_multiplier(), 1.6f);
EXPECT_TRUE(
parsed_config->retryable_status_codes().Contains(GRPC_STATUS_ABORTED));
}
TEST_F(ClientChannelParserTest, InvalidRetryPolicyMaxAttempts) {
TEST_F(RetryParserTest, InvalidRetryPolicyMaxAttempts) {
const char* test_json =
"{\n"
" \"methodConfig\": [ {\n"
@ -853,13 +913,12 @@ TEST_F(ClientChannelParserTest, InvalidRetryPolicyMaxAttempts) {
"Service config parsing error.*referenced_errors.*"
"Method Params.*referenced_errors.*"
"methodConfig.*referenced_errors.*"
"Client channel parser.*referenced_errors.*"
"retryPolicy.*referenced_errors.*"
"field:maxAttempts error:should be at least 2"));
GRPC_ERROR_UNREF(error);
}
TEST_F(ClientChannelParserTest, InvalidRetryPolicyInitialBackoff) {
TEST_F(RetryParserTest, InvalidRetryPolicyInitialBackoff) {
const char* test_json =
"{\n"
" \"methodConfig\": [ {\n"
@ -882,14 +941,13 @@ TEST_F(ClientChannelParserTest, InvalidRetryPolicyInitialBackoff) {
"Service config parsing error.*referenced_errors.*"
"Method Params.*referenced_errors.*"
"methodConfig.*referenced_errors.*"
"Client channel parser.*referenced_errors.*"
"retryPolicy.*referenced_errors.*"
"field:initialBackoff error:type should be STRING of the "
"form given by google.proto.Duration"));
GRPC_ERROR_UNREF(error);
}
TEST_F(ClientChannelParserTest, InvalidRetryPolicyMaxBackoff) {
TEST_F(RetryParserTest, InvalidRetryPolicyMaxBackoff) {
const char* test_json =
"{\n"
" \"methodConfig\": [ {\n"
@ -912,14 +970,13 @@ TEST_F(ClientChannelParserTest, InvalidRetryPolicyMaxBackoff) {
"Service config parsing error.*referenced_errors.*"
"Method Params.*referenced_errors.*"
"methodConfig.*referenced_errors.*"
"Client channel parser.*referenced_errors.*"
"retryPolicy.*referenced_errors.*"
"field:maxBackoff error:type should be STRING of the form "
"given by google.proto.Duration"));
GRPC_ERROR_UNREF(error);
}
TEST_F(ClientChannelParserTest, InvalidRetryPolicyBackoffMultiplier) {
TEST_F(RetryParserTest, InvalidRetryPolicyBackoffMultiplier) {
const char* test_json =
"{\n"
" \"methodConfig\": [ {\n"
@ -942,13 +999,12 @@ TEST_F(ClientChannelParserTest, InvalidRetryPolicyBackoffMultiplier) {
"Service config parsing error.*referenced_errors.*"
"Method Params.*referenced_errors.*"
"methodConfig.*referenced_errors.*"
"Client channel parser.*referenced_errors.*"
"retryPolicy.*referenced_errors.*"
"field:backoffMultiplier error:should be of type number"));
GRPC_ERROR_UNREF(error);
}
TEST_F(ClientChannelParserTest, InvalidRetryPolicyRetryableStatusCodes) {
TEST_F(RetryParserTest, InvalidRetryPolicyRetryableStatusCodes) {
const char* test_json =
"{\n"
" \"methodConfig\": [ {\n"
@ -971,48 +1027,14 @@ TEST_F(ClientChannelParserTest, InvalidRetryPolicyRetryableStatusCodes) {
"Service config parsing error.*referenced_errors.*"
"Method Params.*referenced_errors.*"
"methodConfig.*referenced_errors.*"
"Client channel parser.*referenced_errors.*"
"retryPolicy.*referenced_errors.*"
"field:retryableStatusCodes error:should be non-empty"));
GRPC_ERROR_UNREF(error);
}
TEST_F(ClientChannelParserTest, ValidHealthCheck) {
const char* test_json =
"{\n"
" \"healthCheckConfig\": {\n"
" \"serviceName\": \"health_check_service_name\"\n"
" }\n"
"}";
grpc_error* error = GRPC_ERROR_NONE;
auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
ASSERT_EQ(error, GRPC_ERROR_NONE) << grpc_error_string(error);
const auto* parsed_config =
static_cast<grpc_core::internal::ClientChannelGlobalParsedConfig*>(
svc_cfg->GetGlobalParsedConfig(0));
ASSERT_NE(parsed_config, nullptr);
EXPECT_EQ(parsed_config->health_check_service_name(),
"health_check_service_name");
}
TEST_F(ClientChannelParserTest, InvalidHealthCheckMultipleEntries) {
const char* test_json =
"{\n"
" \"healthCheckConfig\": {\n"
" \"serviceName\": \"health_check_service_name\"\n"
" },\n"
" \"healthCheckConfig\": {\n"
" \"serviceName\": \"health_check_service_name1\"\n"
" }\n"
"}";
grpc_error* error = GRPC_ERROR_NONE;
auto svc_cfg = ServiceConfig::Create(nullptr, test_json, &error);
EXPECT_THAT(grpc_error_string(error),
::testing::ContainsRegex(
"JSON parsing failed.*referenced_errors.*"
"duplicate key \"healthCheckConfig\" at index 104"));
GRPC_ERROR_UNREF(error);
}
//
// message_size parser tests
//
class MessageSizeParserTest : public ::testing::Test {
protected:

@ -568,7 +568,8 @@ BENCHMARK_TEMPLATE(BM_IsolatedFilter, NoFilter, NoOp);
typedef Fixture<&phony_filter::phony_filter, 0> PhonyFilter;
BENCHMARK_TEMPLATE(BM_IsolatedFilter, PhonyFilter, NoOp);
BENCHMARK_TEMPLATE(BM_IsolatedFilter, PhonyFilter, SendEmptyMetadata);
typedef Fixture<&grpc_client_channel_filter, 0> ClientChannelFilter;
typedef Fixture<&grpc_core::ClientChannel::kFilterVtable, 0>
ClientChannelFilter;
BENCHMARK_TEMPLATE(BM_IsolatedFilter, ClientChannelFilter, NoOp);
typedef Fixture<&grpc_message_compress_filter, CHECKS_NOT_LAST> CompressFilter;
BENCHMARK_TEMPLATE(BM_IsolatedFilter, CompressFilter, NoOp);

@ -1131,6 +1131,10 @@ src/core/ext/filters/client_channel/resolver_registry.cc \
src/core/ext/filters/client_channel/resolver_registry.h \
src/core/ext/filters/client_channel/resolver_result_parsing.cc \
src/core/ext/filters/client_channel/resolver_result_parsing.h \
src/core/ext/filters/client_channel/retry_filter.cc \
src/core/ext/filters/client_channel/retry_filter.h \
src/core/ext/filters/client_channel/retry_service_config.cc \
src/core/ext/filters/client_channel/retry_service_config.h \
src/core/ext/filters/client_channel/retry_throttle.cc \
src/core/ext/filters/client_channel/retry_throttle.h \
src/core/ext/filters/client_channel/server_address.cc \

@ -959,6 +959,10 @@ src/core/ext/filters/client_channel/resolver_registry.cc \
src/core/ext/filters/client_channel/resolver_registry.h \
src/core/ext/filters/client_channel/resolver_result_parsing.cc \
src/core/ext/filters/client_channel/resolver_result_parsing.h \
src/core/ext/filters/client_channel/retry_filter.cc \
src/core/ext/filters/client_channel/retry_filter.h \
src/core/ext/filters/client_channel/retry_service_config.cc \
src/core/ext/filters/client_channel/retry_service_config.h \
src/core/ext/filters/client_channel/retry_throttle.cc \
src/core/ext/filters/client_channel/retry_throttle.h \
src/core/ext/filters/client_channel/server_address.cc \

Loading…
Cancel
Save