[server] Allow configuring max incoming connections at the server (#36088)

Closes #36088

COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/36088 from Vignesh2208:server-fix e62a8f7ae7
PiperOrigin-RevId: 614729878
pull/35229/head
Vignesh Babu 9 months ago committed by Copybara-Service
parent f8cfe1c16d
commit 7533328075
  1. 8
      CMakeLists.txt
  2. 1
      Makefile
  3. 2
      Package.swift
  4. 16
      build_autogenerated.yaml
  5. 1
      config.m4
  6. 1
      config.w32
  7. 2
      gRPC-C++.podspec
  8. 3
      gRPC-Core.podspec
  9. 2
      grpc.gemspec
  10. 3
      grpc.gyp
  11. 4
      include/grpc/impl/channel_arg_names.h
  12. 2
      package.xml
  13. 19
      src/core/BUILD
  14. 32
      src/core/ext/transport/chttp2/server/chttp2_server.cc
  15. 71
      src/core/lib/resource_quota/connection_quota.cc
  16. 61
      src/core/lib/resource_quota/connection_quota.h
  17. 1
      src/python/grpcio/grpc_core_dependencies.py
  18. 74
      test/cpp/end2end/resource_quota_end2end_stress_test.cc
  19. 2
      tools/doxygen/Doxyfile.c++.internal
  20. 2
      tools/doxygen/Doxyfile.core.internal

8
CMakeLists.txt generated

@ -2383,6 +2383,7 @@ add_library(grpc
src/core/lib/promise/trace.cc
src/core/lib/resource_quota/api.cc
src/core/lib/resource_quota/arena.cc
src/core/lib/resource_quota/connection_quota.cc
src/core/lib/resource_quota/memory_quota.cc
src/core/lib/resource_quota/periodic_update.cc
src/core/lib/resource_quota/resource_quota.cc
@ -3145,6 +3146,7 @@ add_library(grpc_unsecure
src/core/lib/promise/trace.cc
src/core/lib/resource_quota/api.cc
src/core/lib/resource_quota/arena.cc
src/core/lib/resource_quota/connection_quota.cc
src/core/lib/resource_quota/memory_quota.cc
src/core/lib/resource_quota/periodic_update.cc
src/core/lib/resource_quota/resource_quota.cc
@ -5252,6 +5254,7 @@ add_library(grpc_authorization_provider
src/core/lib/promise/trace.cc
src/core/lib/resource_quota/api.cc
src/core/lib/resource_quota/arena.cc
src/core/lib/resource_quota/connection_quota.cc
src/core/lib/resource_quota/memory_quota.cc
src/core/lib/resource_quota/periodic_update.cc
src/core/lib/resource_quota/resource_quota.cc
@ -10177,6 +10180,7 @@ add_executable(chunked_vector_test
src/core/lib/promise/activity.cc
src/core/lib/promise/trace.cc
src/core/lib/resource_quota/arena.cc
src/core/lib/resource_quota/connection_quota.cc
src/core/lib/resource_quota/memory_quota.cc
src/core/lib/resource_quota/periodic_update.cc
src/core/lib/resource_quota/resource_quota.cc
@ -14175,6 +14179,7 @@ add_executable(flow_control_test
src/core/lib/iomgr/iomgr_internal.cc
src/core/lib/promise/activity.cc
src/core/lib/promise/trace.cc
src/core/lib/resource_quota/connection_quota.cc
src/core/lib/resource_quota/memory_quota.cc
src/core/lib/resource_quota/periodic_update.cc
src/core/lib/resource_quota/resource_quota.cc
@ -14263,6 +14268,7 @@ add_executable(for_each_test
src/core/lib/promise/activity.cc
src/core/lib/promise/trace.cc
src/core/lib/resource_quota/arena.cc
src/core/lib/resource_quota/connection_quota.cc
src/core/lib/resource_quota/memory_quota.cc
src/core/lib/resource_quota/periodic_update.cc
src/core/lib/resource_quota/resource_quota.cc
@ -17709,6 +17715,7 @@ add_executable(interceptor_list_test
src/core/lib/promise/activity.cc
src/core/lib/promise/trace.cc
src/core/lib/resource_quota/arena.cc
src/core/lib/resource_quota/connection_quota.cc
src/core/lib/resource_quota/memory_quota.cc
src/core/lib/resource_quota/periodic_update.cc
src/core/lib/resource_quota/resource_quota.cc
@ -18900,6 +18907,7 @@ add_executable(map_pipe_test
src/core/lib/promise/activity.cc
src/core/lib/promise/trace.cc
src/core/lib/resource_quota/arena.cc
src/core/lib/resource_quota/connection_quota.cc
src/core/lib/resource_quota/memory_quota.cc
src/core/lib/resource_quota/periodic_update.cc
src/core/lib/resource_quota/resource_quota.cc

1
Makefile generated

@ -1309,6 +1309,7 @@ LIBGRPC_SRC = \
src/core/lib/promise/trace.cc \
src/core/lib/resource_quota/api.cc \
src/core/lib/resource_quota/arena.cc \
src/core/lib/resource_quota/connection_quota.cc \
src/core/lib/resource_quota/memory_quota.cc \
src/core/lib/resource_quota/periodic_update.cc \
src/core/lib/resource_quota/resource_quota.cc \

2
Package.swift generated

@ -1608,6 +1608,8 @@ let package = Package(
"src/core/lib/resource_quota/api.h",
"src/core/lib/resource_quota/arena.cc",
"src/core/lib/resource_quota/arena.h",
"src/core/lib/resource_quota/connection_quota.cc",
"src/core/lib/resource_quota/connection_quota.h",
"src/core/lib/resource_quota/memory_quota.cc",
"src/core/lib/resource_quota/memory_quota.h",
"src/core/lib/resource_quota/periodic_update.cc",

@ -1041,6 +1041,7 @@ libs:
- src/core/lib/promise/try_seq.h
- src/core/lib/resource_quota/api.h
- src/core/lib/resource_quota/arena.h
- src/core/lib/resource_quota/connection_quota.h
- src/core/lib/resource_quota/memory_quota.h
- src/core/lib/resource_quota/periodic_update.h
- src/core/lib/resource_quota/resource_quota.h
@ -1837,6 +1838,7 @@ libs:
- src/core/lib/promise/trace.cc
- src/core/lib/resource_quota/api.cc
- src/core/lib/resource_quota/arena.cc
- src/core/lib/resource_quota/connection_quota.cc
- src/core/lib/resource_quota/memory_quota.cc
- src/core/lib/resource_quota/periodic_update.cc
- src/core/lib/resource_quota/resource_quota.cc
@ -2542,6 +2544,7 @@ libs:
- src/core/lib/promise/try_seq.h
- src/core/lib/resource_quota/api.h
- src/core/lib/resource_quota/arena.h
- src/core/lib/resource_quota/connection_quota.h
- src/core/lib/resource_quota/memory_quota.h
- src/core/lib/resource_quota/periodic_update.h
- src/core/lib/resource_quota/resource_quota.h
@ -2961,6 +2964,7 @@ libs:
- src/core/lib/promise/trace.cc
- src/core/lib/resource_quota/api.cc
- src/core/lib/resource_quota/arena.cc
- src/core/lib/resource_quota/connection_quota.cc
- src/core/lib/resource_quota/memory_quota.cc
- src/core/lib/resource_quota/periodic_update.cc
- src/core/lib/resource_quota/resource_quota.cc
@ -4619,6 +4623,7 @@ libs:
- src/core/lib/promise/try_seq.h
- src/core/lib/resource_quota/api.h
- src/core/lib/resource_quota/arena.h
- src/core/lib/resource_quota/connection_quota.h
- src/core/lib/resource_quota/memory_quota.h
- src/core/lib/resource_quota/periodic_update.h
- src/core/lib/resource_quota/resource_quota.h
@ -4916,6 +4921,7 @@ libs:
- src/core/lib/promise/trace.cc
- src/core/lib/resource_quota/api.cc
- src/core/lib/resource_quota/arena.cc
- src/core/lib/resource_quota/connection_quota.cc
- src/core/lib/resource_quota/memory_quota.cc
- src/core/lib/resource_quota/periodic_update.cc
- src/core/lib/resource_quota/resource_quota.cc
@ -7689,6 +7695,7 @@ targets:
- src/core/lib/promise/seq.h
- src/core/lib/promise/trace.h
- src/core/lib/resource_quota/arena.h
- src/core/lib/resource_quota/connection_quota.h
- src/core/lib/resource_quota/memory_quota.h
- src/core/lib/resource_quota/periodic_update.h
- src/core/lib/resource_quota/resource_quota.h
@ -7738,6 +7745,7 @@ targets:
- src/core/lib/promise/activity.cc
- src/core/lib/promise/trace.cc
- src/core/lib/resource_quota/arena.cc
- src/core/lib/resource_quota/connection_quota.cc
- src/core/lib/resource_quota/memory_quota.cc
- src/core/lib/resource_quota/periodic_update.cc
- src/core/lib/resource_quota/resource_quota.cc
@ -9785,6 +9793,7 @@ targets:
- src/core/lib/promise/race.h
- src/core/lib/promise/seq.h
- src/core/lib/promise/trace.h
- src/core/lib/resource_quota/connection_quota.h
- src/core/lib/resource_quota/memory_quota.h
- src/core/lib/resource_quota/periodic_update.h
- src/core/lib/resource_quota/resource_quota.h
@ -9839,6 +9848,7 @@ targets:
- src/core/lib/iomgr/iomgr_internal.cc
- src/core/lib/promise/activity.cc
- src/core/lib/promise/trace.cc
- src/core/lib/resource_quota/connection_quota.cc
- src/core/lib/resource_quota/memory_quota.cc
- src/core/lib/resource_quota/periodic_update.cc
- src/core/lib/resource_quota/resource_quota.cc
@ -9928,6 +9938,7 @@ targets:
- src/core/lib/promise/trace.h
- src/core/lib/promise/try_seq.h
- src/core/lib/resource_quota/arena.h
- src/core/lib/resource_quota/connection_quota.h
- src/core/lib/resource_quota/memory_quota.h
- src/core/lib/resource_quota/periodic_update.h
- src/core/lib/resource_quota/resource_quota.h
@ -9978,6 +9989,7 @@ targets:
- src/core/lib/promise/activity.cc
- src/core/lib/promise/trace.cc
- src/core/lib/resource_quota/arena.cc
- src/core/lib/resource_quota/connection_quota.cc
- src/core/lib/resource_quota/memory_quota.cc
- src/core/lib/resource_quota/periodic_update.cc
- src/core/lib/resource_quota/resource_quota.cc
@ -11578,6 +11590,7 @@ targets:
- src/core/lib/promise/seq.h
- src/core/lib/promise/trace.h
- src/core/lib/resource_quota/arena.h
- src/core/lib/resource_quota/connection_quota.h
- src/core/lib/resource_quota/memory_quota.h
- src/core/lib/resource_quota/periodic_update.h
- src/core/lib/resource_quota/resource_quota.h
@ -11628,6 +11641,7 @@ targets:
- src/core/lib/promise/activity.cc
- src/core/lib/promise/trace.cc
- src/core/lib/resource_quota/arena.cc
- src/core/lib/resource_quota/connection_quota.cc
- src/core/lib/resource_quota/memory_quota.cc
- src/core/lib/resource_quota/periodic_update.cc
- src/core/lib/resource_quota/resource_quota.cc
@ -12256,6 +12270,7 @@ targets:
- src/core/lib/promise/trace.h
- src/core/lib/promise/try_seq.h
- src/core/lib/resource_quota/arena.h
- src/core/lib/resource_quota/connection_quota.h
- src/core/lib/resource_quota/memory_quota.h
- src/core/lib/resource_quota/periodic_update.h
- src/core/lib/resource_quota/resource_quota.h
@ -12306,6 +12321,7 @@ targets:
- src/core/lib/promise/activity.cc
- src/core/lib/promise/trace.cc
- src/core/lib/resource_quota/arena.cc
- src/core/lib/resource_quota/connection_quota.cc
- src/core/lib/resource_quota/memory_quota.cc
- src/core/lib/resource_quota/periodic_update.cc
- src/core/lib/resource_quota/resource_quota.cc

1
config.m4 generated

@ -684,6 +684,7 @@ if test "$PHP_GRPC" != "no"; then
src/core/lib/promise/trace.cc \
src/core/lib/resource_quota/api.cc \
src/core/lib/resource_quota/arena.cc \
src/core/lib/resource_quota/connection_quota.cc \
src/core/lib/resource_quota/memory_quota.cc \
src/core/lib/resource_quota/periodic_update.cc \
src/core/lib/resource_quota/resource_quota.cc \

1
config.w32 generated

@ -649,6 +649,7 @@ if (PHP_GRPC != "no") {
"src\\core\\lib\\promise\\trace.cc " +
"src\\core\\lib\\resource_quota\\api.cc " +
"src\\core\\lib\\resource_quota\\arena.cc " +
"src\\core\\lib\\resource_quota\\connection_quota.cc " +
"src\\core\\lib\\resource_quota\\memory_quota.cc " +
"src\\core\\lib\\resource_quota\\periodic_update.cc " +
"src\\core\\lib\\resource_quota\\resource_quota.cc " +

2
gRPC-C++.podspec generated

@ -1147,6 +1147,7 @@ Pod::Spec.new do |s|
'src/core/lib/promise/try_seq.h',
'src/core/lib/resource_quota/api.h',
'src/core/lib/resource_quota/arena.h',
'src/core/lib/resource_quota/connection_quota.h',
'src/core/lib/resource_quota/memory_quota.h',
'src/core/lib/resource_quota/periodic_update.h',
'src/core/lib/resource_quota/resource_quota.h',
@ -2410,6 +2411,7 @@ Pod::Spec.new do |s|
'src/core/lib/promise/try_seq.h',
'src/core/lib/resource_quota/api.h',
'src/core/lib/resource_quota/arena.h',
'src/core/lib/resource_quota/connection_quota.h',
'src/core/lib/resource_quota/memory_quota.h',
'src/core/lib/resource_quota/periodic_update.h',
'src/core/lib/resource_quota/resource_quota.h',

3
gRPC-Core.podspec generated

@ -1722,6 +1722,8 @@ Pod::Spec.new do |s|
'src/core/lib/resource_quota/api.h',
'src/core/lib/resource_quota/arena.cc',
'src/core/lib/resource_quota/arena.h',
'src/core/lib/resource_quota/connection_quota.cc',
'src/core/lib/resource_quota/connection_quota.h',
'src/core/lib/resource_quota/memory_quota.cc',
'src/core/lib/resource_quota/memory_quota.h',
'src/core/lib/resource_quota/periodic_update.cc',
@ -3191,6 +3193,7 @@ Pod::Spec.new do |s|
'src/core/lib/promise/try_seq.h',
'src/core/lib/resource_quota/api.h',
'src/core/lib/resource_quota/arena.h',
'src/core/lib/resource_quota/connection_quota.h',
'src/core/lib/resource_quota/memory_quota.h',
'src/core/lib/resource_quota/periodic_update.h',
'src/core/lib/resource_quota/resource_quota.h',

2
grpc.gemspec generated

@ -1614,6 +1614,8 @@ Gem::Specification.new do |s|
s.files += %w( src/core/lib/resource_quota/api.h )
s.files += %w( src/core/lib/resource_quota/arena.cc )
s.files += %w( src/core/lib/resource_quota/arena.h )
s.files += %w( src/core/lib/resource_quota/connection_quota.cc )
s.files += %w( src/core/lib/resource_quota/connection_quota.h )
s.files += %w( src/core/lib/resource_quota/memory_quota.cc )
s.files += %w( src/core/lib/resource_quota/memory_quota.h )
s.files += %w( src/core/lib/resource_quota/periodic_update.cc )

3
grpc.gyp generated

@ -883,6 +883,7 @@
'src/core/lib/promise/trace.cc',
'src/core/lib/resource_quota/api.cc',
'src/core/lib/resource_quota/arena.cc',
'src/core/lib/resource_quota/connection_quota.cc',
'src/core/lib/resource_quota/memory_quota.cc',
'src/core/lib/resource_quota/periodic_update.cc',
'src/core/lib/resource_quota/resource_quota.cc',
@ -1422,6 +1423,7 @@
'src/core/lib/promise/trace.cc',
'src/core/lib/resource_quota/api.cc',
'src/core/lib/resource_quota/arena.cc',
'src/core/lib/resource_quota/connection_quota.cc',
'src/core/lib/resource_quota/memory_quota.cc',
'src/core/lib/resource_quota/periodic_update.cc',
'src/core/lib/resource_quota/resource_quota.cc',
@ -2228,6 +2230,7 @@
'src/core/lib/promise/trace.cc',
'src/core/lib/resource_quota/api.cc',
'src/core/lib/resource_quota/arena.cc',
'src/core/lib/resource_quota/connection_quota.cc',
'src/core/lib/resource_quota/memory_quota.cc',
'src/core/lib/resource_quota/periodic_update.cc',
'src/core/lib/resource_quota/resource_quota.cc',

@ -395,6 +395,10 @@
* factory. */
#define GRPC_ARG_EVENT_ENGINE_USE_MEMORY_ALLOCATOR_FACTORY \
"grpc.event_engine_use_memory_allocator_factory"
/** Configure the max number of allowed incoming connections to the server.
* If unspecified, it is unlimited */
#define GRPC_ARG_MAX_ALLOWED_INCOMING_CONNECTIONS \
"grpc.max_allowed_incoming_connections"
/** \} */
#endif /* GRPC_IMPL_CHANNEL_ARG_NAMES_H */

2
package.xml generated

@ -1596,6 +1596,8 @@
<file baseinstalldir="/" name="src/core/lib/resource_quota/api.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/resource_quota/arena.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/resource_quota/arena.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/resource_quota/connection_quota.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/resource_quota/connection_quota.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/resource_quota/memory_quota.cc" role="src" />
<file baseinstalldir="/" name="src/core/lib/resource_quota/memory_quota.h" role="src" />
<file baseinstalldir="/" name="src/core/lib/resource_quota/periodic_update.cc" role="src" />

@ -1399,6 +1399,23 @@ grpc_cc_library(
],
)
grpc_cc_library(
name = "connection_quota",
srcs = [
"lib/resource_quota/connection_quota.cc",
],
hdrs = [
"lib/resource_quota/connection_quota.h",
],
external_deps = ["absl/base:core_headers"],
deps = [
"memory_quota",
"ref_counted",
"//:gpr",
"//:ref_counted_ptr",
],
)
grpc_cc_library(
name = "resource_quota_trace",
srcs = [
@ -1426,6 +1443,7 @@ grpc_cc_library(
"@grpc:alt_grpc_base_legacy",
],
deps = [
"connection_quota",
"memory_quota",
"ref_counted",
"thread_quota",
@ -6747,6 +6765,7 @@ grpc_cc_library(
"channel_args",
"channel_args_endpoint_config",
"closure",
"connection_quota",
"error",
"error_utils",
"grpc_insecure_credentials",

@ -72,6 +72,7 @@
#include "src/core/lib/iomgr/tcp_server.h"
#include "src/core/lib/iomgr/unix_sockets_posix.h"
#include "src/core/lib/iomgr/vsock.h"
#include "src/core/lib/resource_quota/connection_quota.h"
#include "src/core/lib/resource_quota/memory_quota.h"
#include "src/core/lib/resource_quota/resource_quota.h"
#include "src/core/lib/security/credentials/credentials.h"
@ -283,6 +284,7 @@ class Chttp2ServerListener : public Server::ListenerInterface {
grpc_closure* on_destroy_done_ ABSL_GUARDED_BY(mu_) = nullptr;
RefCountedPtr<channelz::ListenSocketNode> channelz_listen_socket_;
MemoryQuotaRefPtr memory_quota_;
ConnectionQuotaRefPtr connection_quota_;
};
//
@ -504,6 +506,18 @@ void Chttp2ServerListener::ActiveConnection::HandshakingState::OnHandshakeDone(
} else {
// Remove the connection from the connections_ map since OnClose()
// will not be invoked when a config fetcher is set.
auto connection_quota =
self->connection_->listener_->connection_quota_->Ref()
.release();
auto on_close_transport = [](void* arg,
grpc_error_handle /*handle*/) {
ConnectionQuota* connection_quota =
static_cast<ConnectionQuota*>(arg);
connection_quota->ReleaseConnections(1);
connection_quota->Unref();
};
on_close = GRPC_CLOSURE_CREATE(on_close_transport, connection_quota,
grpc_schedule_on_exec_ctx_);
cleanup_connection = true;
}
grpc_chttp2_transport_start_reading(transport, args->read_buffer,
@ -658,6 +672,7 @@ void Chttp2ServerListener::ActiveConnection::OnClose(
self->drain_grace_timer_handle_.reset();
}
}
self->listener_->connection_quota_->ReleaseConnections(1);
self->Unref();
}
@ -762,7 +777,14 @@ Chttp2ServerListener::Chttp2ServerListener(
: server_(server),
args_modifier_(args_modifier),
args_(args),
memory_quota_(args.GetObject<ResourceQuota>()->memory_quota()) {
memory_quota_(args.GetObject<ResourceQuota>()->memory_quota()),
connection_quota_(MakeRefCounted<ConnectionQuota>()) {
auto max_allowed_incoming_connections =
args.GetInt(GRPC_ARG_MAX_ALLOWED_INCOMING_CONNECTIONS);
if (max_allowed_incoming_connections.has_value()) {
connection_quota_->SetMaxIncomingConnections(
max_allowed_incoming_connections.value());
}
GRPC_CLOSURE_INIT(&tcp_server_shutdown_complete_, TcpServerShutdownComplete,
this, grpc_schedule_on_exec_ctx);
}
@ -821,6 +843,14 @@ void Chttp2ServerListener::OnAccept(void* arg, grpc_endpoint* tcp,
grpc_endpoint_destroy(tcp);
gpr_free(acceptor);
};
if (!self->connection_quota_->AllowIncomingConnection(
self->memory_quota_, grpc_endpoint_get_peer(tcp))) {
grpc_error_handle error = GRPC_ERROR_CREATE(
"Rejected incoming connection because configured connection quota "
"limits have been exceeded.");
endpoint_cleanup(error);
return;
}
if (self->server_->config_fetcher() != nullptr) {
if (connection_manager == nullptr) {
grpc_error_handle error = GRPC_ERROR_CREATE(

@ -0,0 +1,71 @@
// Copyright 2024 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/lib/resource_quota/connection_quota.h"
#include <atomic>
#include <cstdint>
#include <grpc/support/log.h>
namespace grpc_core {
ConnectionQuota::ConnectionQuota() = default;
void ConnectionQuota::SetMaxIncomingConnections(int max_incoming_connections) {
// The maximum can only be configured once.
GPR_ASSERT(max_incoming_connections < INT_MAX);
GPR_ASSERT(max_incoming_connections_.exchange(max_incoming_connections,
std::memory_order_release) ==
INT_MAX);
}
// Returns true if the incoming connection is allowed to be accepted on the
// server.
bool ConnectionQuota::AllowIncomingConnection(MemoryQuotaRefPtr mem_quota,
absl::string_view /*peer*/) {
if (mem_quota->IsMemoryPressureHigh()) {
return false;
}
if (max_incoming_connections_.load(std::memory_order_relaxed) == INT_MAX) {
return true;
}
int curr_active_connections =
active_incoming_connections_.load(std::memory_order_acquire);
do {
if (curr_active_connections >=
max_incoming_connections_.load(std::memory_order_relaxed)) {
return false;
}
} while (!active_incoming_connections_.compare_exchange_weak(
curr_active_connections, curr_active_connections + 1,
std::memory_order_acq_rel, std::memory_order_relaxed));
return true;
}
// Mark connections as closed.
void ConnectionQuota::ReleaseConnections(int num_connections) {
if (max_incoming_connections_.load(std::memory_order_relaxed) == INT_MAX) {
return;
}
GPR_ASSERT(active_incoming_connections_.fetch_sub(
num_connections, std::memory_order_acq_rel) >=
num_connections);
}
} // namespace grpc_core

@ -0,0 +1,61 @@
// Copyright 2024 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_SRC_CORE_LIB_RESOURCE_QUOTA_CONNECTION_QUOTA_H
#define GRPC_SRC_CORE_LIB_RESOURCE_QUOTA_CONNECTION_QUOTA_H
#include <grpc/support/port_platform.h>
#include <cstddef>
#include <limits>
#include "absl/base/thread_annotations.h"
#include "src/core/lib/gprpp/ref_counted.h"
#include "src/core/lib/gprpp/ref_counted_ptr.h"
#include "src/core/lib/gprpp/sync.h"
#include "src/core/lib/resource_quota/memory_quota.h"
namespace grpc_core {
// Tracks the amount of threads in a resource quota.
class ConnectionQuota : public RefCounted<ConnectionQuota> {
public:
ConnectionQuota();
~ConnectionQuota() override = default;
ConnectionQuota(const ConnectionQuota&) = delete;
ConnectionQuota& operator=(const ConnectionQuota&) = delete;
// Set the maximum number of allowed incoming connections on the server.
void SetMaxIncomingConnections(int max_incoming_connections);
// Returns true if the incoming connection is allowed to be accepted on the
// server.
bool AllowIncomingConnection(MemoryQuotaRefPtr mem_quota,
absl::string_view peer);
// Mark connections as closed.
void ReleaseConnections(int num_connections);
private:
std::atomic<int> active_incoming_connections_{0};
std::atomic<int> max_incoming_connections_{std::numeric_limits<int>::max()};
};
using ConnectionQuotaRefPtr = RefCountedPtr<ConnectionQuota>;
} // namespace grpc_core
#endif // GRPC_SRC_CORE_LIB_RESOURCE_QUOTA_CONNECTION_QUOTA_H

@ -658,6 +658,7 @@ CORE_SOURCE_FILES = [
'src/core/lib/promise/trace.cc',
'src/core/lib/resource_quota/api.cc',
'src/core/lib/resource_quota/arena.cc',
'src/core/lib/resource_quota/connection_quota.cc',
'src/core/lib/resource_quota/memory_quota.cc',
'src/core/lib/resource_quota/periodic_update.cc',
'src/core/lib/resource_quota/resource_quota.cc',

@ -53,7 +53,6 @@ class EchoClientUnaryReactor : public grpc::ClientUnaryReactor {
EchoClientUnaryReactor(ClientContext* ctx, EchoTestService::Stub* stub,
const std::string payload, Status* status)
: ctx_(ctx), payload_(payload), status_(status) {
ctx_->set_wait_for_ready(true);
request_.set_message(payload);
stub->async()->Echo(ctx_, &request_, &response_, this);
StartCall();
@ -120,6 +119,7 @@ class End2EndResourceQuotaUnaryTest : public ::testing::Test {
Status status;
auto stub = EchoTestService::NewStub(
CreateChannel(server_address_, grpc::InsecureChannelCredentials()));
ctx.set_wait_for_ready(true);
EchoClientUnaryReactor reactor(&ctx, stub.get(), payload_, &status);
reactor.Await();
}
@ -145,6 +145,78 @@ class End2EndResourceQuotaUnaryTest : public ::testing::Test {
TEST_F(End2EndResourceQuotaUnaryTest, MultipleUnaryRPCTest) { MakeGrpcCalls(); }
class End2EndConnectionQuotaTest : public ::testing::TestWithParam<int> {
protected:
End2EndConnectionQuotaTest() {
port_ = grpc_pick_unused_port_or_die();
server_address_ = absl::StrCat("[::]:", port_);
payload_ = std::string(kPayloadSizeBytes, 'a');
ServerBuilder builder;
builder.AddListeningPort(server_address_, InsecureServerCredentials());
builder.AddChannelArgument(GRPC_ARG_MAX_ALLOWED_INCOMING_CONNECTIONS,
GetParam());
builder.AddChannelArgument(
GRPC_ARG_HTTP2_MIN_RECV_PING_INTERVAL_WITHOUT_DATA_MS, 10000);
builder.RegisterService(&grpc_service_);
server_ = builder.BuildAndStart();
}
~End2EndConnectionQuotaTest() override { server_->Shutdown(); }
std::unique_ptr<EchoTestService::Stub> CreateGrpcChannelStub() {
grpc::ChannelArguments args;
args.SetInt(GRPC_ARG_USE_LOCAL_SUBCHANNEL_POOL, 1);
args.SetInt(GRPC_ARG_ENABLE_RETRIES, 0);
args.SetInt(GRPC_ARG_KEEPALIVE_TIME_MS, 20000);
args.SetInt(GRPC_ARG_KEEPALIVE_TIMEOUT_MS, 10000);
args.SetInt(GRPC_ARG_HTTP2_MIN_SENT_PING_INTERVAL_WITHOUT_DATA_MS, 15000);
args.SetInt(GRPC_ARG_KEEPALIVE_PERMIT_WITHOUT_CALLS, 1);
return EchoTestService::NewStub(
CreateCustomChannel(absl::StrCat("ipv6:[::1]:", port_),
grpc::InsecureChannelCredentials(), args));
}
void TestExceedingConnectionQuota() {
const int kNumConnections = 2 * GetParam();
std::vector<std::unique_ptr<EchoTestService::Stub>> stubs;
stubs.reserve(kNumConnections);
for (int i = 0; i < kNumConnections; i++) {
stubs.push_back(CreateGrpcChannelStub());
}
for (int i = 0; i < kNumConnections; ++i) {
ClientContext ctx;
Status status;
ctx.set_wait_for_ready(false);
EchoClientUnaryReactor reactor(&ctx, stubs[i].get(), payload_, &status);
reactor.Await();
// The first half RPCs should succeed.
if (i < kNumConnections / 2) {
EXPECT_TRUE(status.ok());
} else {
// The second half should fail because they would attempt to create a
// new connection and fail since it would exceed the connection quota
// limit set at the server.
EXPECT_FALSE(status.ok());
}
}
}
int port_;
std::unique_ptr<Server> server_;
string server_address_;
GrpcCallbackServiceImpl grpc_service_;
std::string payload_;
};
TEST_P(End2EndConnectionQuotaTest, ConnectionQuotaTest) {
TestExceedingConnectionQuota();
}
INSTANTIATE_TEST_SUITE_P(ConnectionQuotaParamTest, End2EndConnectionQuotaTest,
::testing::ValuesIn<int>({10, 100}));
} // namespace testing
} // namespace grpc

@ -2613,6 +2613,8 @@ src/core/lib/resource_quota/api.cc \
src/core/lib/resource_quota/api.h \
src/core/lib/resource_quota/arena.cc \
src/core/lib/resource_quota/arena.h \
src/core/lib/resource_quota/connection_quota.cc \
src/core/lib/resource_quota/connection_quota.h \
src/core/lib/resource_quota/memory_quota.cc \
src/core/lib/resource_quota/memory_quota.h \
src/core/lib/resource_quota/periodic_update.cc \

@ -2388,6 +2388,8 @@ src/core/lib/resource_quota/api.cc \
src/core/lib/resource_quota/api.h \
src/core/lib/resource_quota/arena.cc \
src/core/lib/resource_quota/arena.h \
src/core/lib/resource_quota/connection_quota.cc \
src/core/lib/resource_quota/connection_quota.h \
src/core/lib/resource_quota/memory_quota.cc \
src/core/lib/resource_quota/memory_quota.h \
src/core/lib/resource_quota/periodic_update.cc \

Loading…
Cancel
Save