[backoff] Add random early detection classifier (#32354)

<!--

If you know who should review your pull request, please assign it to
that
person, otherwise the pull request would get assigned randomly.

If your pull request is for a specific language, please add the
appropriate
lang label.

-->

---------

Co-authored-by: ctiller <ctiller@users.noreply.github.com>
pull/32427/head
Craig Tiller 2 years ago committed by GitHub
parent 7fab06b923
commit d49e151306
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 39
      CMakeLists.txt
  2. 12
      build_autogenerated.yaml
  3. 12
      src/core/BUILD
  4. 31
      src/core/lib/backoff/random_early_detection.cc
  5. 56
      src/core/lib/backoff/random_early_detection.h
  6. 15
      test/core/backoff/BUILD
  7. 63
      test/core/backoff/random_early_detection_test.cc
  8. 1
      tools/distrib/fix_build_deps.py
  9. 24
      tools/run_tests/generated/tests.json

39
CMakeLists.txt generated

@ -1109,6 +1109,7 @@ if(gRPC_BUILD_TESTS)
add_dependencies(buildtests_cxx qps_json_driver)
add_dependencies(buildtests_cxx qps_worker)
add_dependencies(buildtests_cxx race_test)
add_dependencies(buildtests_cxx random_early_detection_test)
add_dependencies(buildtests_cxx raw_end2end_test)
add_dependencies(buildtests_cxx rbac_service_config_parser_test)
add_dependencies(buildtests_cxx rbac_translator_test)
@ -16959,6 +16960,44 @@ target_link_libraries(race_test
)
endif()
if(gRPC_BUILD_TESTS)
add_executable(random_early_detection_test
src/core/lib/backoff/random_early_detection.cc
test/core/backoff/random_early_detection_test.cc
third_party/googletest/googletest/src/gtest-all.cc
third_party/googletest/googlemock/src/gmock-all.cc
)
target_compile_features(random_early_detection_test PUBLIC cxx_std_14)
target_include_directories(random_early_detection_test
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/include
${_gRPC_ADDRESS_SORTING_INCLUDE_DIR}
${_gRPC_RE2_INCLUDE_DIR}
${_gRPC_SSL_INCLUDE_DIR}
${_gRPC_UPB_GENERATED_DIR}
${_gRPC_UPB_GRPC_GENERATED_DIR}
${_gRPC_UPB_INCLUDE_DIR}
${_gRPC_XXHASH_INCLUDE_DIR}
${_gRPC_ZLIB_INCLUDE_DIR}
third_party/googletest/googletest/include
third_party/googletest/googletest
third_party/googletest/googlemock/include
third_party/googletest/googlemock
${_gRPC_PROTO_GENS_DIR}
)
target_link_libraries(random_early_detection_test
${_gRPC_BASELIB_LIBRARIES}
${_gRPC_PROTOBUF_LIBRARIES}
${_gRPC_ZLIB_LIBRARIES}
${_gRPC_ALLTARGETS_LIBRARIES}
absl::random_random
)
endif()
if(gRPC_BUILD_TESTS)

@ -10200,6 +10200,18 @@ targets:
deps:
- absl/types:variant
uses_polling: false
- name: random_early_detection_test
gtest: true
build: test
language: c++
headers:
- src/core/lib/backoff/random_early_detection.h
src:
- src/core/lib/backoff/random_early_detection.cc
- test/core/backoff/random_early_detection_test.cc
deps:
- absl/random:random
uses_polling: false
- name: raw_end2end_test
gtest: true
build: test

@ -3700,6 +3700,18 @@ grpc_cc_library(
],
)
grpc_cc_library(
name = "random_early_detection",
srcs = [
"lib/backoff/random_early_detection.cc",
],
hdrs = [
"lib/backoff/random_early_detection.h",
],
external_deps = ["absl/random"],
deps = ["//:gpr_platform"],
)
grpc_cc_library(
name = "grpc_backend_metric_data",
hdrs = [

@ -0,0 +1,31 @@
// Copyright 2023 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/backoff/random_early_detection.h"
namespace grpc_core {
bool RandomEarlyDetection::Reject(uint64_t size) {
if (size <= soft_limit_) return false;
if (size < hard_limit_) {
return absl::Bernoulli(bitgen_,
static_cast<double>(size - soft_limit_) /
static_cast<double>(hard_limit_ - soft_limit_));
}
return true;
}
} // namespace grpc_core

@ -0,0 +1,56 @@
// Copyright 2023 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_BACKOFF_RANDOM_EARLY_DETECTION_H
#define GRPC_SRC_CORE_LIB_BACKOFF_RANDOM_EARLY_DETECTION_H
#include <grpc/support/port_platform.h>
#include <cstdint>
#include "absl/random/random.h"
namespace grpc_core {
// Implements the random early detection algorithm - allows items to be rejected
// or accepted based upon their size.
class RandomEarlyDetection {
public:
RandomEarlyDetection(uint64_t soft_limit, uint64_t hard_limit)
: soft_limit_(soft_limit), hard_limit_(hard_limit) {}
// Returns true if the size is greater than or equal to the hard limit - ie if
// this item must be rejected.
bool MustReject(uint64_t size) { return size >= hard_limit_; }
// Returns true if the item should be rejected.
bool Reject(uint64_t size);
uint64_t soft_limit() const { return soft_limit_; }
uint64_t hard_limit() const { return hard_limit_; }
private:
// The soft limit is the size at which we start rejecting items with a
// probability that increases linearly to 1 as the size approaches the hard
// limit.
uint64_t soft_limit_;
// The hard limit is the size at which we reject all items.
uint64_t hard_limit_;
// The bit generator used to generate random numbers.
absl::InsecureBitGen bitgen_;
};
} // namespace grpc_core
#endif // GRPC_SRC_CORE_LIB_BACKOFF_RANDOM_EARLY_DETECTION_H

@ -26,16 +26,25 @@ package(
grpc_cc_test(
name = "backoff_test",
srcs = ["backoff_test.cc"],
external_deps = [
"gtest",
],
external_deps = ["gtest"],
language = "C++",
uses_event_engine = False,
uses_polling = False,
deps = [
"//:backoff",
"//:exec_ctx",
"//:grpc",
"//src/core:time",
"//test/core/util:grpc_test_util",
],
)
grpc_cc_test(
name = "random_early_detection_test",
srcs = ["random_early_detection_test.cc"],
external_deps = ["gtest"],
language = "C++",
uses_event_engine = False,
uses_polling = False,
deps = ["//src/core:random_early_detection"],
)

@ -0,0 +1,63 @@
// Copyright 2023 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 "src/core/lib/backoff/random_early_detection.h"
#include "gtest/gtest.h"
namespace grpc_core {
namespace {
TEST(RandomEarlyDetectionTest, NoOp) {
RandomEarlyDetection red(100, 200);
EXPECT_EQ(red.soft_limit(), 100);
EXPECT_EQ(red.hard_limit(), 200);
}
TEST(RandomEarlyDetectionTest, Distribution) {
RandomEarlyDetection red(100, 200);
int64_t counts[300] = {};
for (int round = 0; round < 10000; round++) {
for (int64_t i = 0; i < 300; i++) {
if (red.Reject(i)) counts[i]++;
}
}
for (int64_t i = 0; i < 100; i++) {
// [0, 100) should never be rejected
EXPECT_EQ(counts[i], 0) << i;
// [100, 200) should be rejected with probability ramping from 0 to 1
EXPECT_GT(counts[i + 100], (i - 5) * 100) << i;
EXPECT_LT(counts[i + 100], (i + 5) * 100) << i;
// [200, 300) should always be rejected
EXPECT_EQ(counts[i + 200], 10000) << i;
}
}
TEST(RandomEarlyDetection, MustRejectWorks) {
RandomEarlyDetection red(100, 200);
for (int64_t i = 0; i < 200; i++) {
EXPECT_FALSE(red.MustReject(i));
}
for (int64_t i = 200; i < 300; i++) {
EXPECT_TRUE(red.MustReject(i));
}
}
} // namespace
} // namespace grpc_core
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

@ -421,6 +421,7 @@ for dirname in [
"src/core",
"src/cpp/ext/gcp",
"src/cpp/ext/filters/logging",
"test/core/backoff",
"test/core/uri",
"test/core/util",
"test/core/end2end",

@ -5681,6 +5681,30 @@
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,
"ci_platforms": [
"linux",
"mac",
"posix",
"windows"
],
"cpu_cost": 1.0,
"exclude_configs": [],
"exclude_iomgrs": [],
"flaky": false,
"gtest": true,
"language": "c++",
"name": "random_early_detection_test",
"platforms": [
"linux",
"mac",
"posix",
"windows"
],
"uses_polling": false
},
{
"args": [],
"benchmark": false,

Loading…
Cancel
Save