From 29480c4f6b547bbb9fc01a5b0251fe25da166e39 Mon Sep 17 00:00:00 2001 From: Qiancheng Zhao Date: Wed, 24 Jul 2019 10:04:41 -0700 Subject: [PATCH] add client idle filter --- BUILD | 12 + BUILD.gn | 1 + CMakeLists.txt | 2 + Makefile | 2 + build.yaml | 8 + config.m4 | 2 + config.w32 | 2 + gRPC-Core.podspec | 1 + grpc.gemspec | 1 + grpc.gyp | 2 + include/grpc/impl/codegen/grpc_types.h | 13 +- package.xml | 1 + .../filters/client_channel/client_channel.cc | 47 +++- .../resolver/fake/fake_resolver.cc | 183 ++++++++---- .../resolver/fake/fake_resolver.h | 11 +- .../filters/client_idle/client_idle_filter.cc | 264 ++++++++++++++++++ .../ext/filters/max_age/max_age_filter.cc | 2 +- src/core/lib/iomgr/error.cc | 2 + src/core/lib/iomgr/error.h | 2 + .../plugin_registry/grpc_plugin_registry.cc | 4 + .../grpc_unsecure_plugin_registry.cc | 4 + src/python/grpcio/grpc_core_dependencies.py | 1 + test/cpp/end2end/client_lb_end2end_test.cc | 28 +- tools/doxygen/Doxyfile.core.internal | 1 + .../generated/sources_and_headers.json | 17 ++ 25 files changed, 532 insertions(+), 81 deletions(-) create mode 100644 src/core/ext/filters/client_idle/client_idle_filter.cc diff --git a/BUILD b/BUILD index 474fc053ad6..56c1ff34afe 100644 --- a/BUILD +++ b/BUILD @@ -998,6 +998,7 @@ grpc_cc_library( "grpc_client_authority_filter", "grpc_lb_policy_pick_first", "grpc_lb_policy_round_robin", + "grpc_client_idle_filter", "grpc_max_age_filter", "grpc_message_size_filter", "grpc_resolver_dns_ares", @@ -1085,6 +1086,17 @@ grpc_cc_library( ], ) +grpc_cc_library( + name = "grpc_client_idle_filter", + srcs = [ + "src/core/ext/filters/client_idle/client_idle_filter.cc", + ], + language = "c++", + deps = [ + "grpc_base", + ], +) + grpc_cc_library( name = "grpc_max_age_filter", srcs = [ diff --git a/BUILD.gn b/BUILD.gn index 9dacd14bb45..83cc1ec4bda 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -348,6 +348,7 @@ config("grpc_config") { "src/core/ext/filters/client_channel/subchannel_interface.h", "src/core/ext/filters/client_channel/subchannel_pool_interface.cc", "src/core/ext/filters/client_channel/subchannel_pool_interface.h", + "src/core/ext/filters/client_idle/client_idle_filter.cc", "src/core/ext/filters/deadline/deadline_filter.cc", "src/core/ext/filters/deadline/deadline_filter.h", "src/core/ext/filters/http/client/http_client_filter.cc", diff --git a/CMakeLists.txt b/CMakeLists.txt index 23305ce6fd9..0ce5e5e0661 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1326,6 +1326,7 @@ add_library(grpc src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc src/core/ext/filters/census/grpc_context.cc + src/core/ext/filters/client_idle/client_idle_filter.cc src/core/ext/filters/max_age/max_age_filter.cc src/core/ext/filters/message_size/message_size_filter.cc src/core/ext/filters/http/client_authority_filter.cc @@ -2757,6 +2758,7 @@ add_library(grpc_unsecure src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc src/core/ext/filters/census/grpc_context.cc + src/core/ext/filters/client_idle/client_idle_filter.cc src/core/ext/filters/max_age/max_age_filter.cc src/core/ext/filters/message_size/message_size_filter.cc src/core/ext/filters/http/client_authority_filter.cc diff --git a/Makefile b/Makefile index 4755955de7d..fa9128187dd 100644 --- a/Makefile +++ b/Makefile @@ -3843,6 +3843,7 @@ LIBGRPC_SRC = \ src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc \ src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc \ src/core/ext/filters/census/grpc_context.cc \ + src/core/ext/filters/client_idle/client_idle_filter.cc \ src/core/ext/filters/max_age/max_age_filter.cc \ src/core/ext/filters/message_size/message_size_filter.cc \ src/core/ext/filters/http/client_authority_filter.cc \ @@ -5210,6 +5211,7 @@ LIBGRPC_UNSECURE_SRC = \ src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc \ src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc \ src/core/ext/filters/census/grpc_context.cc \ + src/core/ext/filters/client_idle/client_idle_filter.cc \ src/core/ext/filters/max_age/max_age_filter.cc \ src/core/ext/filters/message_size/message_size_filter.cc \ src/core/ext/filters/http/client_authority_filter.cc \ diff --git a/build.yaml b/build.yaml index 7b4a396b19c..92fbad06f4a 100644 --- a/build.yaml +++ b/build.yaml @@ -634,6 +634,12 @@ filegroups: - grpc_base - grpc_deadline_filter - health_proto +- name: grpc_client_idle_filter + src: + - src/core/ext/filters/client_idle/client_idle_filter.cc + plugin: grpc_client_idle_filter + uses: + - grpc_base - name: grpc_codegen public_headers: - include/grpc/impl/codegen/byte_buffer.h @@ -1606,6 +1612,7 @@ libs: - grpc_resolver_fake - grpc_secure - census + - grpc_client_idle_filter - grpc_max_age_filter - grpc_message_size_filter - grpc_deadline_filter @@ -1679,6 +1686,7 @@ libs: - grpc_lb_policy_pick_first - grpc_lb_policy_round_robin - census + - grpc_client_idle_filter - grpc_max_age_filter - grpc_message_size_filter - grpc_deadline_filter diff --git a/config.m4 b/config.m4 index 50b789bb7bb..6eeeafce9a5 100644 --- a/config.m4 +++ b/config.m4 @@ -417,6 +417,7 @@ if test "$PHP_GRPC" != "no"; then src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc \ src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc \ src/core/ext/filters/census/grpc_context.cc \ + src/core/ext/filters/client_idle/client_idle_filter.cc \ src/core/ext/filters/max_age/max_age_filter.cc \ src/core/ext/filters/message_size/message_size_filter.cc \ src/core/ext/filters/http/client_authority_filter.cc \ @@ -702,6 +703,7 @@ if test "$PHP_GRPC" != "no"; then PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/client_channel/resolver/dns/native) PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/client_channel/resolver/fake) PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/client_channel/resolver/sockaddr) + PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/client_idle) PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/deadline) PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/http) PHP_ADD_BUILD_DIR($ext_builddir/src/core/ext/filters/http/client) diff --git a/config.w32 b/config.w32 index 7048f83facf..fd7920f72fd 100644 --- a/config.w32 +++ b/config.w32 @@ -392,6 +392,7 @@ if (PHP_GRPC != "no") { "src\\core\\ext\\filters\\client_channel\\resolver\\dns\\native\\dns_resolver.cc " + "src\\core\\ext\\filters\\client_channel\\resolver\\sockaddr\\sockaddr_resolver.cc " + "src\\core\\ext\\filters\\census\\grpc_context.cc " + + "src\\core\\ext\\filters\\client_idle\\client_idle_filter.cc " + "src\\core\\ext\\filters\\max_age\\max_age_filter.cc " + "src\\core\\ext\\filters\\message_size\\message_size_filter.cc " + "src\\core\\ext\\filters\\http\\client_authority_filter.cc " + @@ -712,6 +713,7 @@ if (PHP_GRPC != "no") { FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\filters\\client_channel\\resolver\\dns\\native"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\filters\\client_channel\\resolver\\fake"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\filters\\client_channel\\resolver\\sockaddr"); + FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\filters\\client_idle"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\filters\\deadline"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\filters\\http"); FSO.CreateFolder(base_dir+"\\ext\\grpc\\src\\core\\ext\\filters\\http\\client"); diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec index 47cf5b8d47f..16ac9a571e5 100644 --- a/gRPC-Core.podspec +++ b/gRPC-Core.podspec @@ -879,6 +879,7 @@ Pod::Spec.new do |s| 'src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc', 'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc', 'src/core/ext/filters/census/grpc_context.cc', + 'src/core/ext/filters/client_idle/client_idle_filter.cc', 'src/core/ext/filters/max_age/max_age_filter.cc', 'src/core/ext/filters/message_size/message_size_filter.cc', 'src/core/ext/filters/http/client_authority_filter.cc', diff --git a/grpc.gemspec b/grpc.gemspec index 1f4cf237ada..eda96081c73 100644 --- a/grpc.gemspec +++ b/grpc.gemspec @@ -816,6 +816,7 @@ Gem::Specification.new do |s| s.files += %w( src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc ) s.files += %w( src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc ) s.files += %w( src/core/ext/filters/census/grpc_context.cc ) + s.files += %w( src/core/ext/filters/client_idle/client_idle_filter.cc ) s.files += %w( src/core/ext/filters/max_age/max_age_filter.cc ) s.files += %w( src/core/ext/filters/message_size/message_size_filter.cc ) s.files += %w( src/core/ext/filters/http/client_authority_filter.cc ) diff --git a/grpc.gyp b/grpc.gyp index 5eeab74d4c7..d3292a42ef9 100644 --- a/grpc.gyp +++ b/grpc.gyp @@ -599,6 +599,7 @@ 'src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc', 'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc', 'src/core/ext/filters/census/grpc_context.cc', + 'src/core/ext/filters/client_idle/client_idle_filter.cc', 'src/core/ext/filters/max_age/max_age_filter.cc', 'src/core/ext/filters/message_size/message_size_filter.cc', 'src/core/ext/filters/http/client_authority_filter.cc', @@ -1380,6 +1381,7 @@ 'src/core/ext/filters/client_channel/lb_policy/pick_first/pick_first.cc', 'src/core/ext/filters/client_channel/lb_policy/round_robin/round_robin.cc', 'src/core/ext/filters/census/grpc_context.cc', + 'src/core/ext/filters/client_idle/client_idle_filter.cc', 'src/core/ext/filters/max_age/max_age_filter.cc', 'src/core/ext/filters/message_size/message_size_filter.cc', 'src/core/ext/filters/http/client_authority_filter.cc', diff --git a/include/grpc/impl/codegen/grpc_types.h b/include/grpc/impl/codegen/grpc_types.h index 65582e6e85d..ab29e917ed8 100644 --- a/include/grpc/impl/codegen/grpc_types.h +++ b/include/grpc/impl/codegen/grpc_types.h @@ -157,8 +157,9 @@ typedef struct { /** Maximum message length that the channel can send. Int valued, bytes. -1 means unlimited. */ #define GRPC_ARG_MAX_SEND_MESSAGE_LENGTH "grpc.max_send_message_length" -/** Maximum time that a channel may have no outstanding rpcs. Int valued, - milliseconds. INT_MAX means unlimited. */ +/** Maximum time that a channel may have no outstanding rpcs, after which the + * server will close the connection. Int valued, milliseconds. INT_MAX means + * unlimited. */ #define GRPC_ARG_MAX_CONNECTION_IDLE_MS "grpc.max_connection_idle_ms" /** Maximum time that a channel may exist. Int valued, milliseconds. * INT_MAX means unlimited. */ @@ -166,6 +167,14 @@ typedef struct { /** Grace period after the channel reaches its max age. Int valued, milliseconds. INT_MAX means unlimited. */ #define GRPC_ARG_MAX_CONNECTION_AGE_GRACE_MS "grpc.max_connection_age_grace_ms" +/** Timeout after the last RPC finishes on the client channel at which the + * channel goes back into IDLE state. Int valued, milliseconds. INT_MAX means + * unlimited. */ +/** TODO(qianchengz): Currently the default value is INT_MAX, which means the + * client idle filter is disabled by default. After the client idle filter + * proves no perfomance issue, we will change the default value to a reasonable + * value. */ +#define GRPC_ARG_CLIENT_IDLE_TIMEOUT_MS "grpc.client_idle_timeout_ms" /** Enable/disable support for per-message compression. Defaults to 1, unless GRPC_ARG_MINIMAL_STACK is enabled, in which case it defaults to 0. */ #define GRPC_ARG_ENABLE_PER_MESSAGE_COMPRESSION "grpc.per_message_compression" diff --git a/package.xml b/package.xml index 584c6510a60..cb221ff932d 100644 --- a/package.xml +++ b/package.xml @@ -821,6 +821,7 @@ + diff --git a/src/core/ext/filters/client_channel/client_channel.cc b/src/core/ext/filters/client_channel/client_channel.cc index 9aeb4be76d8..3c2e75ad8d3 100644 --- a/src/core/ext/filters/client_channel/client_channel.cc +++ b/src/core/ext/filters/client_channel/client_channel.cc @@ -726,6 +726,13 @@ class ChannelData::ConnectivityStateAndPickerSetter { ChannelData* chand, grpc_connectivity_state state, const char* reason, UniquePtr picker) : chand_(chand), picker_(std::move(picker)) { + // Clean the control plane when entering IDLE, while holding control plane + // combiner. + if (picker_ == nullptr) { + chand->health_check_service_name_.reset(); + chand->saved_service_config_.reset(); + chand->received_first_resolver_result_ = false; + } // Update connectivity state here, while holding control plane combiner. grpc_connectivity_state_set(&chand->state_tracker_, state, reason); if (chand->channelz_node_ != nullptr) { @@ -749,6 +756,12 @@ class ChannelData::ConnectivityStateAndPickerSetter { auto* self = static_cast(arg); // Update picker. self->chand_->picker_ = std::move(self->picker_); + // Clean the data plane if the updated picker is nullptr. + if (self->chand_->picker_ == nullptr) { + self->chand_->received_service_config_data_ = false; + self->chand_->retry_throttle_data_.reset(); + self->chand_->service_config_.reset(); + } // Re-process queued picks. for (QueuedPick* pick = self->chand_->queued_picks_; pick != nullptr; pick = pick->next) { @@ -1486,19 +1499,31 @@ void ChannelData::StartTransportOpLocked(void* arg, grpc_error* ignored) { chand->resolving_lb_policy_->ResetBackoffLocked(); } } - // Disconnect. + // Disconnect or enter IDLE. if (op->disconnect_with_error != GRPC_ERROR_NONE) { - grpc_error* error = GRPC_ERROR_NONE; - GPR_ASSERT(chand->disconnect_error_.CompareExchangeStrong( - &error, op->disconnect_with_error, MemoryOrder::ACQ_REL, - MemoryOrder::ACQUIRE)); chand->DestroyResolvingLoadBalancingPolicyLocked(); - // Will delete itself. - New( - chand, GRPC_CHANNEL_SHUTDOWN, "shutdown from API", - UniquePtr( - New( - GRPC_ERROR_REF(op->disconnect_with_error)))); + intptr_t value; + if (grpc_error_get_int(op->disconnect_with_error, + GRPC_ERROR_INT_CHANNEL_CONNECTIVITY_STATE, &value) && + static_cast(value) == GRPC_CHANNEL_IDLE) { + if (chand->disconnect_error() == GRPC_ERROR_NONE) { + // Enter IDLE state. + New(chand, GRPC_CHANNEL_IDLE, + "channel entering IDLE", nullptr); + } + GRPC_ERROR_UNREF(op->disconnect_with_error); + } else { + // Disconnect. + grpc_error* error = GRPC_ERROR_NONE; + GPR_ASSERT(chand->disconnect_error_.CompareExchangeStrong( + &error, op->disconnect_with_error, MemoryOrder::ACQ_REL, + MemoryOrder::ACQUIRE)); + New( + chand, GRPC_CHANNEL_SHUTDOWN, "shutdown from API", + UniquePtr( + New( + GRPC_ERROR_REF(op->disconnect_with_error)))); + } } GRPC_CHANNEL_STACK_UNREF(chand->owning_stack_, "start_transport_op"); GRPC_CLOSURE_SCHED(op->on_consumed, GRPC_ERROR_NONE); diff --git a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc index 761b27292da..443a709ea6c 100644 --- a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc +++ b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.cc @@ -60,7 +60,13 @@ class FakeResolver : public Resolver { virtual ~FakeResolver(); - void ShutdownLocked() override { active_ = false; } + void ShutdownLocked() override { + shutdown_ = true; + if (response_generator_ != nullptr) { + response_generator_->SetFakeResolver(nullptr); + response_generator_.reset(); + } + } void MaybeSendResultLocked(); @@ -68,6 +74,7 @@ class FakeResolver : public Resolver { // passed-in parameters grpc_channel_args* channel_args_ = nullptr; + RefCountedPtr response_generator_; // If has_next_result_ is true, next_result_ is the next resolution result // to be returned. bool has_next_result_ = false; @@ -76,8 +83,10 @@ class FakeResolver : public Resolver { // RequestReresolutionLocked(). bool has_reresolution_result_ = false; Result reresolution_result_; - // True between the calls to StartLocked() ShutdownLocked(). - bool active_ = false; + // True after the call to StartLocked(). + bool started_ = false; + // True after the call to ShutdownLocked(). + bool shutdown_ = false; // if true, return failure bool return_failure_ = false; // pending re-resolution @@ -86,11 +95,11 @@ class FakeResolver : public Resolver { }; FakeResolver::FakeResolver(ResolverArgs args) - : Resolver(args.combiner, std::move(args.result_handler)) { + : Resolver(args.combiner, std::move(args.result_handler)), + response_generator_( + FakeResolverResponseGenerator::GetFromArgs(args.args)) { GRPC_CLOSURE_INIT(&reresolution_closure_, ReturnReresolutionResult, this, grpc_combiner_scheduler(combiner())); - FakeResolverResponseGenerator* response_generator = - FakeResolverResponseGenerator::GetFromArgs(args.args); // Channels sharing the same subchannels may have different resolver response // generators. If we don't remove this arg, subchannel pool will create new // subchannels for the same address instead of reusing existing ones because @@ -98,19 +107,15 @@ FakeResolver::FakeResolver(ResolverArgs args) const char* args_to_remove[] = {GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR}; channel_args_ = grpc_channel_args_copy_and_remove( args.args, args_to_remove, GPR_ARRAY_SIZE(args_to_remove)); - if (response_generator != nullptr) { - response_generator->resolver_ = this; - if (response_generator->has_result_) { - response_generator->SetResponse(std::move(response_generator->result_)); - response_generator->has_result_ = false; - } + if (response_generator_ != nullptr) { + response_generator_->SetFakeResolver(Ref()); } } FakeResolver::~FakeResolver() { grpc_channel_args_destroy(channel_args_); } void FakeResolver::StartLocked() { - active_ = true; + started_ = true; MaybeSendResultLocked(); } @@ -130,7 +135,7 @@ void FakeResolver::RequestReresolutionLocked() { } void FakeResolver::MaybeSendResultLocked() { - if (!active_) return; + if (!started_ || shutdown_) return; if (return_failure_) { // TODO(roth): Change resolver result generator to be able to inject // the error to be returned. @@ -165,9 +170,13 @@ void FakeResolver::ReturnReresolutionResult(void* arg, grpc_error* error) { // FakeResolverResponseGenerator // +FakeResolverResponseGenerator::FakeResolverResponseGenerator() {} + +FakeResolverResponseGenerator::~FakeResolverResponseGenerator() {} + struct SetResponseClosureArg { grpc_closure set_response_closure; - FakeResolverResponseGenerator* generator; + RefCountedPtr resolver; Resolver::Result result; bool has_result = false; bool immediate = true; @@ -176,97 +185,146 @@ struct SetResponseClosureArg { void FakeResolverResponseGenerator::SetResponseLocked(void* arg, grpc_error* error) { SetResponseClosureArg* closure_arg = static_cast(arg); - FakeResolver* resolver = closure_arg->generator->resolver_; - resolver->next_result_ = std::move(closure_arg->result); - resolver->has_next_result_ = true; - resolver->MaybeSendResultLocked(); - closure_arg->generator->Unref(); + auto& resolver = closure_arg->resolver; + if (!resolver->shutdown_) { + resolver->next_result_ = std::move(closure_arg->result); + resolver->has_next_result_ = true; + resolver->MaybeSendResultLocked(); + } Delete(closure_arg); } void FakeResolverResponseGenerator::SetResponse(Resolver::Result result) { - if (resolver_ != nullptr) { - Ref().release(); // ref to be held by closure - SetResponseClosureArg* closure_arg = New(); - closure_arg->generator = this; - closure_arg->result = std::move(result); - GRPC_CLOSURE_SCHED( - GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, SetResponseLocked, - closure_arg, - grpc_combiner_scheduler(resolver_->combiner())), - GRPC_ERROR_NONE); - } else { - has_result_ = true; - result_ = std::move(result); + RefCountedPtr resolver; + { + MutexLock lock(&mu_); + if (resolver_ == nullptr) { + has_result_ = true; + result_ = std::move(result); + return; + } + resolver = resolver_->Ref(); } + SetResponseClosureArg* closure_arg = New(); + closure_arg->resolver = std::move(resolver); + closure_arg->result = std::move(result); + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_INIT( + &closure_arg->set_response_closure, SetResponseLocked, closure_arg, + grpc_combiner_scheduler(closure_arg->resolver->combiner())), + GRPC_ERROR_NONE); } void FakeResolverResponseGenerator::SetReresolutionResponseLocked( void* arg, grpc_error* error) { SetResponseClosureArg* closure_arg = static_cast(arg); - FakeResolver* resolver = closure_arg->generator->resolver_; - resolver->reresolution_result_ = std::move(closure_arg->result); - resolver->has_reresolution_result_ = closure_arg->has_result; + auto& resolver = closure_arg->resolver; + if (!resolver->shutdown_) { + resolver->reresolution_result_ = std::move(closure_arg->result); + resolver->has_reresolution_result_ = closure_arg->has_result; + } Delete(closure_arg); } void FakeResolverResponseGenerator::SetReresolutionResponse( Resolver::Result result) { - GPR_ASSERT(resolver_ != nullptr); + RefCountedPtr resolver; + { + MutexLock lock(&mu_); + GPR_ASSERT(resolver_ != nullptr); + resolver = resolver_->Ref(); + } SetResponseClosureArg* closure_arg = New(); - closure_arg->generator = this; + closure_arg->resolver = std::move(resolver); closure_arg->result = std::move(result); closure_arg->has_result = true; GRPC_CLOSURE_SCHED( - GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, - SetReresolutionResponseLocked, closure_arg, - grpc_combiner_scheduler(resolver_->combiner())), + GRPC_CLOSURE_INIT( + &closure_arg->set_response_closure, SetReresolutionResponseLocked, + closure_arg, + grpc_combiner_scheduler(closure_arg->resolver->combiner())), GRPC_ERROR_NONE); } void FakeResolverResponseGenerator::UnsetReresolutionResponse() { - GPR_ASSERT(resolver_ != nullptr); + RefCountedPtr resolver; + { + MutexLock lock(&mu_); + GPR_ASSERT(resolver_ != nullptr); + resolver = resolver_->Ref(); + } SetResponseClosureArg* closure_arg = New(); - closure_arg->generator = this; + closure_arg->resolver = std::move(resolver); GRPC_CLOSURE_SCHED( - GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, - SetReresolutionResponseLocked, closure_arg, - grpc_combiner_scheduler(resolver_->combiner())), + GRPC_CLOSURE_INIT( + &closure_arg->set_response_closure, SetReresolutionResponseLocked, + closure_arg, + grpc_combiner_scheduler(closure_arg->resolver->combiner())), GRPC_ERROR_NONE); } void FakeResolverResponseGenerator::SetFailureLocked(void* arg, grpc_error* error) { SetResponseClosureArg* closure_arg = static_cast(arg); - FakeResolver* resolver = closure_arg->generator->resolver_; - resolver->return_failure_ = true; - if (closure_arg->immediate) resolver->MaybeSendResultLocked(); + auto& resolver = closure_arg->resolver; + if (!resolver->shutdown_) { + resolver->return_failure_ = true; + if (closure_arg->immediate) resolver->MaybeSendResultLocked(); + } Delete(closure_arg); } void FakeResolverResponseGenerator::SetFailure() { - GPR_ASSERT(resolver_ != nullptr); + RefCountedPtr resolver; + { + MutexLock lock(&mu_); + GPR_ASSERT(resolver_ != nullptr); + resolver = resolver_->Ref(); + } SetResponseClosureArg* closure_arg = New(); - closure_arg->generator = this; + closure_arg->resolver = std::move(resolver); GRPC_CLOSURE_SCHED( - GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, SetFailureLocked, - closure_arg, - grpc_combiner_scheduler(resolver_->combiner())), + GRPC_CLOSURE_INIT( + &closure_arg->set_response_closure, SetFailureLocked, closure_arg, + grpc_combiner_scheduler(closure_arg->resolver->combiner())), GRPC_ERROR_NONE); } void FakeResolverResponseGenerator::SetFailureOnReresolution() { - GPR_ASSERT(resolver_ != nullptr); + RefCountedPtr resolver; + { + MutexLock lock(&mu_); + GPR_ASSERT(resolver_ != nullptr); + resolver = resolver_->Ref(); + } SetResponseClosureArg* closure_arg = New(); - closure_arg->generator = this; + closure_arg->resolver = std::move(resolver); closure_arg->immediate = false; GRPC_CLOSURE_SCHED( - GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, SetFailureLocked, - closure_arg, - grpc_combiner_scheduler(resolver_->combiner())), + GRPC_CLOSURE_INIT( + &closure_arg->set_response_closure, SetFailureLocked, closure_arg, + grpc_combiner_scheduler(closure_arg->resolver->combiner())), GRPC_ERROR_NONE); } +void FakeResolverResponseGenerator::SetFakeResolver( + RefCountedPtr resolver) { + MutexLock lock(&mu_); + resolver_ = std::move(resolver); + if (resolver_ == nullptr) return; + if (has_result_) { + SetResponseClosureArg* closure_arg = New(); + closure_arg->resolver = resolver_->Ref(); + closure_arg->result = std::move(result_); + GRPC_CLOSURE_SCHED( + GRPC_CLOSURE_INIT(&closure_arg->set_response_closure, SetResponseLocked, + closure_arg, + grpc_combiner_scheduler(resolver_->combiner())), + GRPC_ERROR_NONE); + has_result_ = false; + } +} + namespace { static void* response_generator_arg_copy(void* p) { @@ -304,12 +362,13 @@ grpc_arg FakeResolverResponseGenerator::MakeChannelArg( return arg; } -FakeResolverResponseGenerator* FakeResolverResponseGenerator::GetFromArgs( - const grpc_channel_args* args) { +RefCountedPtr +FakeResolverResponseGenerator::GetFromArgs(const grpc_channel_args* args) { const grpc_arg* arg = grpc_channel_args_find(args, GRPC_ARG_FAKE_RESOLVER_RESPONSE_GENERATOR); if (arg == nullptr || arg->type != GRPC_ARG_POINTER) return nullptr; - return static_cast(arg->value.pointer.p); + return static_cast(arg->value.pointer.p) + ->Ref(); } // diff --git a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h index 3b1ea8e8909..c04c7c38e17 100644 --- a/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h +++ b/src/core/ext/filters/client_channel/resolver/fake/fake_resolver.h @@ -42,7 +42,8 @@ class FakeResolver; class FakeResolverResponseGenerator : public RefCounted { public: - FakeResolverResponseGenerator() {} + FakeResolverResponseGenerator(); + ~FakeResolverResponseGenerator(); // Instructs the fake resolver associated with the response generator // instance to trigger a new resolution with the specified result. If the @@ -71,17 +72,21 @@ class FakeResolverResponseGenerator static grpc_arg MakeChannelArg(FakeResolverResponseGenerator* generator); // Returns the response generator in \a args, or null if not found. - static FakeResolverResponseGenerator* GetFromArgs( + static RefCountedPtr GetFromArgs( const grpc_channel_args* args); private: friend class FakeResolver; + // Set the corresponding FakeResolver to this generator. + void SetFakeResolver(RefCountedPtr resolver); static void SetResponseLocked(void* arg, grpc_error* error); static void SetReresolutionResponseLocked(void* arg, grpc_error* error); static void SetFailureLocked(void* arg, grpc_error* error); - FakeResolver* resolver_ = nullptr; // Do not own. + // Mutex protecting the members below. + Mutex mu_; + RefCountedPtr resolver_; Resolver::Result result_; bool has_result_ = false; }; diff --git a/src/core/ext/filters/client_idle/client_idle_filter.cc b/src/core/ext/filters/client_idle/client_idle_filter.cc new file mode 100644 index 00000000000..d2b23a9d20f --- /dev/null +++ b/src/core/ext/filters/client_idle/client_idle_filter.cc @@ -0,0 +1,264 @@ +/* + * + * Copyright 2019 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 + +#include + +#include "src/core/lib/channel/channel_args.h" +#include "src/core/lib/channel/channel_stack_builder.h" +#include "src/core/lib/gprpp/atomic.h" +#include "src/core/lib/iomgr/timer.h" +#include "src/core/lib/surface/channel_init.h" +#include "src/core/lib/transport/http2_errors.h" + +// The idle filter is disabled in client channel by default. +// To enable the idle filte, set GRPC_ARG_CLIENT_IDLE_TIMEOUT_MS to [0, INT_MAX) +// in channel args. +// TODO(qianchengz): Find a reasonable default value. Maybe check what deault +// value Java uses. +#define DEFAULT_IDLE_TIMEOUT_MS INT_MAX + +namespace grpc_core { + +TraceFlag grpc_trace_client_idle_filter(false, "client_idle_filter"); + +#define GRPC_IDLE_FILTER_LOG(format, ...) \ + do { \ + if (GRPC_TRACE_FLAG_ENABLED(grpc_trace_client_idle_filter)) { \ + gpr_log(GPR_INFO, "(client idle filter) " format, ##__VA_ARGS__); \ + } \ + } while (0) + +namespace { + +grpc_millis GetClientIdleTimeout(const grpc_channel_args* args) { + return grpc_channel_arg_get_integer( + grpc_channel_args_find(args, GRPC_ARG_CLIENT_IDLE_TIMEOUT_MS), + {DEFAULT_IDLE_TIMEOUT_MS, 0, INT_MAX}); +} + +class ChannelData { + public: + 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); + + void IncreaseCallCount(); + + void DecreaseCallCount(); + + private: + ChannelData(grpc_channel_element* elem, grpc_channel_element_args* args, + grpc_error** error); + ~ChannelData() = default; + + static void IdleTimerCallback(void* arg, grpc_error* error); + static void IdleTransportOpCompleteCallback(void* arg, grpc_error* error); + + void StartIdleTimer(); + + void EnterIdle(); + + grpc_channel_element* elem_; + // The channel stack to which we take refs for pending callbacks. + grpc_channel_stack* channel_stack_; + // Timeout after the last RPC finishes on the client channel at which the + // channel goes back into IDLE state. + const grpc_millis client_idle_timeout_; + + // Member data used to track the state of channel. + Mutex call_count_mu_; + size_t call_count_; + + // Idle timer and its callback closure. + grpc_timer idle_timer_; + grpc_closure idle_timer_callback_; + + // The transport op telling the client channel to enter IDLE. + grpc_transport_op idle_transport_op_; + grpc_closure idle_transport_op_complete_callback_; +}; + +grpc_error* ChannelData::Init(grpc_channel_element* elem, + grpc_channel_element_args* args) { + grpc_error* error = GRPC_ERROR_NONE; + new (elem->channel_data) ChannelData(elem, args, &error); + return error; +} + +void ChannelData::Destroy(grpc_channel_element* elem) { + ChannelData* chand = static_cast(elem->channel_data); + chand->~ChannelData(); +} + +void ChannelData::StartTransportOp(grpc_channel_element* elem, + grpc_transport_op* op) { + ChannelData* chand = static_cast(elem->channel_data); + // Catch the disconnect_with_error transport op. + if (op->disconnect_with_error != nullptr) { + // Disconnect. Cancel the timer if we set it before. + // IncreaseCallCount() introduces a dummy call. It will cancel the timer and + // prevent the timer from being reset by other threads. + chand->IncreaseCallCount(); + } + // Pass the op to the next filter. + grpc_channel_next_op(elem, op); +} + +void ChannelData::IncreaseCallCount() { + MutexLock lock(&call_count_mu_); + if (call_count_++ == 0) { + grpc_timer_cancel(&idle_timer_); + } + GRPC_IDLE_FILTER_LOG("call counter has increased to %" PRIuPTR, call_count_); +} + +void ChannelData::DecreaseCallCount() { + MutexLock lock(&call_count_mu_); + if (call_count_-- == 1) { + StartIdleTimer(); + } + GRPC_IDLE_FILTER_LOG("call counter has decreased to %" PRIuPTR, call_count_); +} + +ChannelData::ChannelData(grpc_channel_element* elem, + grpc_channel_element_args* args, grpc_error** error) + : elem_(elem), + channel_stack_(args->channel_stack), + client_idle_timeout_(GetClientIdleTimeout(args->channel_args)), + call_count_(0) { + // If the idle filter is explicitly disabled in channel args, this ctor should + // not get called. + GPR_ASSERT(client_idle_timeout_ != GRPC_MILLIS_INF_FUTURE); + GRPC_IDLE_FILTER_LOG("created with max_leisure_time = %" PRId64 " ms", + client_idle_timeout_); + // Initialize the idle timer without setting it. + grpc_timer_init_unset(&idle_timer_); + // Initialize the idle timer callback closure. + GRPC_CLOSURE_INIT(&idle_timer_callback_, IdleTimerCallback, this, + grpc_schedule_on_exec_ctx); + // Initialize the idle transport op complete callback. + GRPC_CLOSURE_INIT(&idle_transport_op_complete_callback_, + IdleTransportOpCompleteCallback, this, + grpc_schedule_on_exec_ctx); +} + +void ChannelData::IdleTimerCallback(void* arg, grpc_error* error) { + GRPC_IDLE_FILTER_LOG("timer alarms"); + ChannelData* chand = static_cast(arg); + { + MutexLock lock(&chand->call_count_mu_); + if (error == GRPC_ERROR_NONE && chand->call_count_ == 0) { + chand->EnterIdle(); + } + } + GRPC_IDLE_FILTER_LOG("timer finishes"); + GRPC_CHANNEL_STACK_UNREF(chand->channel_stack_, "max idle timer callback"); +} + +void ChannelData::IdleTransportOpCompleteCallback(void* arg, + grpc_error* error) { + ChannelData* chand = static_cast(arg); + GRPC_CHANNEL_STACK_UNREF(chand->channel_stack_, "idle transport op"); +} + +void ChannelData::StartIdleTimer() { + GRPC_IDLE_FILTER_LOG("timer has started"); + // Hold a ref to the channel stack for the timer callback. + GRPC_CHANNEL_STACK_REF(channel_stack_, "max idle timer callback"); + grpc_timer_init(&idle_timer_, ExecCtx::Get()->Now() + client_idle_timeout_, + &idle_timer_callback_); +} + +void ChannelData::EnterIdle() { + GRPC_IDLE_FILTER_LOG("the channel will enter IDLE"); + // Hold a ref to the channel stack for the transport op. + GRPC_CHANNEL_STACK_REF(channel_stack_, "idle transport op"); + // Initialize the transport op. + memset(&idle_transport_op_, 0, sizeof(idle_transport_op_)); + idle_transport_op_.disconnect_with_error = grpc_error_set_int( + GRPC_ERROR_CREATE_FROM_STATIC_STRING("enter idle"), + GRPC_ERROR_INT_CHANNEL_CONNECTIVITY_STATE, GRPC_CHANNEL_IDLE); + idle_transport_op_.on_consumed = &idle_transport_op_complete_callback_; + // Pass the transport op down to the channel stack. + grpc_channel_next_op(elem_, &idle_transport_op_); +} + +class CallData { + public: + static grpc_error* Init(grpc_call_element* elem, + const grpc_call_element_args* args); + static void Destroy(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* then_schedule_closure); +}; + +grpc_error* CallData::Init(grpc_call_element* elem, + const grpc_call_element_args* args) { + ChannelData* chand = static_cast(elem->channel_data); + chand->IncreaseCallCount(); + return GRPC_ERROR_NONE; +} + +void CallData::Destroy(grpc_call_element* elem, + const grpc_call_final_info* final_info, + grpc_closure* ignored) { + ChannelData* chand = static_cast(elem->channel_data); + chand->DecreaseCallCount(); +} + +const grpc_channel_filter grpc_client_idle_filter = { + grpc_call_next_op, + ChannelData::StartTransportOp, + sizeof(CallData), + CallData::Init, + grpc_call_stack_ignore_set_pollset_or_pollset_set, + CallData::Destroy, + sizeof(ChannelData), + ChannelData::Init, + ChannelData::Destroy, + grpc_channel_next_get_info, + "client_idle"}; + +static bool MaybeAddClientIdleFilter(grpc_channel_stack_builder* builder, + void* arg) { + const grpc_channel_args* channel_args = + grpc_channel_stack_builder_get_channel_arguments(builder); + if (!grpc_channel_args_want_minimal_stack(channel_args) && + GetClientIdleTimeout(channel_args) != INT_MAX) { + return grpc_channel_stack_builder_prepend_filter( + builder, &grpc_client_idle_filter, nullptr, nullptr); + } else { + return true; + } +} + +} // namespace +} // namespace grpc_core + +void grpc_client_idle_filter_init(void) { + grpc_channel_init_register_stage( + GRPC_CLIENT_CHANNEL, GRPC_CHANNEL_INIT_BUILTIN_PRIORITY, + grpc_core::MaybeAddClientIdleFilter, nullptr); +} + +void grpc_client_idle_filter_shutdown(void) {} diff --git a/src/core/ext/filters/max_age/max_age_filter.cc b/src/core/ext/filters/max_age/max_age_filter.cc index f2308581c13..c8823cc5c6a 100644 --- a/src/core/ext/filters/max_age/max_age_filter.cc +++ b/src/core/ext/filters/max_age/max_age_filter.cc @@ -47,7 +47,7 @@ namespace { struct channel_data { - /* We take a reference to the channel stack for the timer callback */ + /* The channel stack to which we take refs for pending callbacks. */ grpc_channel_stack* channel_stack; /* Guards access to max_age_timer, max_age_timer_pending, max_age_grace_timer and max_age_grace_timer_pending */ diff --git a/src/core/lib/iomgr/error.cc b/src/core/lib/iomgr/error.cc index eb44c9a2c90..dedc8376578 100644 --- a/src/core/lib/iomgr/error.cc +++ b/src/core/lib/iomgr/error.cc @@ -73,6 +73,8 @@ static const char* error_int_name(grpc_error_ints key) { return "limit"; case GRPC_ERROR_INT_OCCURRED_DURING_WRITE: return "occurred_during_write"; + case GRPC_ERROR_INT_CHANNEL_CONNECTIVITY_STATE: + return "channel_connectivity_state"; case GRPC_ERROR_INT_MAX: GPR_UNREACHABLE_CODE(return "unknown"); } diff --git a/src/core/lib/iomgr/error.h b/src/core/lib/iomgr/error.h index 0a72e5ca101..8c22357b951 100644 --- a/src/core/lib/iomgr/error.h +++ b/src/core/lib/iomgr/error.h @@ -73,6 +73,8 @@ typedef enum { GRPC_ERROR_INT_LIMIT, /// chttp2: did the error occur while a write was in progress GRPC_ERROR_INT_OCCURRED_DURING_WRITE, + /// channel connectivity state associated with the error + GRPC_ERROR_INT_CHANNEL_CONNECTIVITY_STATE, /// Must always be last GRPC_ERROR_INT_MAX, diff --git a/src/core/plugin_registry/grpc_plugin_registry.cc b/src/core/plugin_registry/grpc_plugin_registry.cc index cde40ef65cf..9b011d8df0f 100644 --- a/src/core/plugin_registry/grpc_plugin_registry.cc +++ b/src/core/plugin_registry/grpc_plugin_registry.cc @@ -46,6 +46,8 @@ void grpc_resolver_dns_native_init(void); void grpc_resolver_dns_native_shutdown(void); void grpc_resolver_sockaddr_init(void); void grpc_resolver_sockaddr_shutdown(void); +void grpc_client_idle_filter_init(void); +void grpc_client_idle_filter_shutdown(void); void grpc_max_age_filter_init(void); void grpc_max_age_filter_shutdown(void); void grpc_message_size_filter_init(void); @@ -82,6 +84,8 @@ void grpc_register_built_in_plugins(void) { grpc_resolver_dns_native_shutdown); grpc_register_plugin(grpc_resolver_sockaddr_init, grpc_resolver_sockaddr_shutdown); + grpc_register_plugin(grpc_client_idle_filter_init, + grpc_client_idle_filter_shutdown); grpc_register_plugin(grpc_max_age_filter_init, grpc_max_age_filter_shutdown); grpc_register_plugin(grpc_message_size_filter_init, diff --git a/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc b/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc index 5749ab6b954..055c3ccc134 100644 --- a/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc +++ b/src/core/plugin_registry/grpc_unsecure_plugin_registry.cc @@ -46,6 +46,8 @@ void grpc_lb_policy_pick_first_init(void); void grpc_lb_policy_pick_first_shutdown(void); void grpc_lb_policy_round_robin_init(void); void grpc_lb_policy_round_robin_shutdown(void); +void grpc_client_idle_filter_init(void); +void grpc_client_idle_filter_shutdown(void); void grpc_max_age_filter_init(void); void grpc_max_age_filter_shutdown(void); void grpc_message_size_filter_init(void); @@ -82,6 +84,8 @@ void grpc_register_built_in_plugins(void) { grpc_lb_policy_pick_first_shutdown); grpc_register_plugin(grpc_lb_policy_round_robin_init, grpc_lb_policy_round_robin_shutdown); + grpc_register_plugin(grpc_client_idle_filter_init, + grpc_client_idle_filter_shutdown); grpc_register_plugin(grpc_max_age_filter_init, grpc_max_age_filter_shutdown); grpc_register_plugin(grpc_message_size_filter_init, diff --git a/src/python/grpcio/grpc_core_dependencies.py b/src/python/grpcio/grpc_core_dependencies.py index 6a8b208b914..aab3768b02e 100644 --- a/src/python/grpcio/grpc_core_dependencies.py +++ b/src/python/grpcio/grpc_core_dependencies.py @@ -391,6 +391,7 @@ CORE_SOURCE_FILES = [ 'src/core/ext/filters/client_channel/resolver/dns/native/dns_resolver.cc', 'src/core/ext/filters/client_channel/resolver/sockaddr/sockaddr_resolver.cc', 'src/core/ext/filters/census/grpc_context.cc', + 'src/core/ext/filters/client_idle/client_idle_filter.cc', 'src/core/ext/filters/max_age/max_age_filter.cc', 'src/core/ext/filters/message_size/message_size_filter.cc', 'src/core/ext/filters/http/client_authority_filter.cc', diff --git a/test/cpp/end2end/client_lb_end2end_test.cc b/test/cpp/end2end/client_lb_end2end_test.cc index 8fc0eacc2fc..dac7860141c 100644 --- a/test/cpp/end2end/client_lb_end2end_test.cc +++ b/test/cpp/end2end/client_lb_end2end_test.cc @@ -209,7 +209,6 @@ class ClientLbEnd2endTest : public ::testing::Test { // Explicitly destroy all the members so that we can make sure grpc_shutdown // has finished by the end of this function, and thus all the registered // LB policy factories are removed. - stub_.reset(); servers_.clear(); creds_.reset(); grpc_shutdown_blocking(); @@ -421,7 +420,6 @@ class ClientLbEnd2endTest : public ::testing::Test { } const grpc::string server_host_; - std::unique_ptr stub_; std::vector> servers_; const grpc::string kRequestMessage_; std::shared_ptr creds_; @@ -1467,6 +1465,32 @@ TEST_F(ClientLbEnd2endTest, RoundRobinWithHealthCheckingServiceNamePerChannel) { EnableDefaultHealthCheckService(false); } +TEST_F(ClientLbEnd2endTest, ChannelIdleness) { + // Start server. + const int kNumServers = 1; + StartServers(kNumServers); + // Set max idle time and build the channel. + ChannelArguments args; + args.SetInt(GRPC_ARG_CLIENT_IDLE_TIMEOUT_MS, 100); + auto response_generator = BuildResolverResponseGenerator(); + auto channel = BuildChannel("", response_generator, args); + auto stub = BuildStub(channel); + // The initial channel state should be IDLE. + EXPECT_EQ(channel->GetState(false), GRPC_CHANNEL_IDLE); + // After sending RPC, channel state should be READY. + response_generator.SetNextResolution(GetServersPorts()); + CheckRpcSendOk(stub, DEBUG_LOCATION); + EXPECT_EQ(channel->GetState(false), GRPC_CHANNEL_READY); + // After a period time not using the channel, the channel state should switch + // to IDLE. + gpr_sleep_until(grpc_timeout_milliseconds_to_deadline(120)); + EXPECT_EQ(channel->GetState(false), GRPC_CHANNEL_IDLE); + // Sending a new RPC should awake the IDLE channel. + response_generator.SetNextResolution(GetServersPorts()); + CheckRpcSendOk(stub, DEBUG_LOCATION); + EXPECT_EQ(channel->GetState(false), GRPC_CHANNEL_READY); +} + class ClientLbInterceptTrailingMetadataTest : public ClientLbEnd2endTest { protected: void SetUp() override { diff --git a/tools/doxygen/Doxyfile.core.internal b/tools/doxygen/Doxyfile.core.internal index 9bb01cb67f8..6c1253f7ae8 100644 --- a/tools/doxygen/Doxyfile.core.internal +++ b/tools/doxygen/Doxyfile.core.internal @@ -978,6 +978,7 @@ src/core/ext/filters/client_channel/subchannel.h \ src/core/ext/filters/client_channel/subchannel_interface.h \ src/core/ext/filters/client_channel/subchannel_pool_interface.cc \ src/core/ext/filters/client_channel/subchannel_pool_interface.h \ +src/core/ext/filters/client_idle/client_idle_filter.cc \ src/core/ext/filters/deadline/deadline_filter.cc \ src/core/ext/filters/deadline/deadline_filter.h \ src/core/ext/filters/http/client/http_client_filter.cc \ diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json index ceec3f0e6c3..179ce39407b 100644 --- a/tools/run_tests/generated/sources_and_headers.json +++ b/tools/run_tests/generated/sources_and_headers.json @@ -6680,6 +6680,7 @@ "gpr", "grpc_base", "grpc_client_authority_filter", + "grpc_client_idle_filter", "grpc_deadline_filter", "grpc_lb_policy_grpclb_secure", "grpc_lb_policy_pick_first", @@ -6772,6 +6773,7 @@ "gpr", "grpc_base", "grpc_client_authority_filter", + "grpc_client_idle_filter", "grpc_deadline_filter", "grpc_lb_policy_grpclb", "grpc_lb_policy_pick_first", @@ -9132,6 +9134,21 @@ "third_party": false, "type": "filegroup" }, + { + "deps": [ + "gpr", + "grpc_base" + ], + "headers": [], + "is_filegroup": true, + "language": "c", + "name": "grpc_client_idle_filter", + "src": [ + "src/core/ext/filters/client_idle/client_idle_filter.cc" + ], + "third_party": false, + "type": "filegroup" + }, { "deps": [ "gpr_codegen"