Skip to content

Commit

Permalink
[backoff] Add random early detection classifier (#32354)
Browse files Browse the repository at this point in the history
<!--

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>
  • Loading branch information
2 people authored and wanlin31 committed May 18, 2023
1 parent 5dba6a1 commit 0f1f72d
Show file tree
Hide file tree
Showing 9 changed files with 250 additions and 3 deletions.
39 changes: 39 additions & 0 deletions CMakeLists.txt

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions build_autogenerated.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions src/core/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down
31 changes: 31 additions & 0 deletions src/core/lib/backoff/random_early_detection.cc
Original file line number Diff line number Diff line change
@@ -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
56 changes: 56 additions & 0 deletions src/core/lib/backoff/random_early_detection.h
Original file line number Diff line number Diff line change
@@ -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
15 changes: 12 additions & 3 deletions test/core/backoff/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
)
63 changes: 63 additions & 0 deletions test/core/backoff/random_early_detection_test.cc
Original file line number Diff line number Diff line change
@@ -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();
}
1 change: 1 addition & 0 deletions tools/distrib/fix_build_deps.py
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ def score_best(proposed, existing):
"src/core",
"src/cpp/ext/gcp",
"src/cpp/ext/filters/logging",
"test/core/backoff",
"test/core/uri",
"test/core/util",
"test/core/end2end",
Expand Down
24 changes: 24 additions & 0 deletions tools/run_tests/generated/tests.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 0f1f72d

Please sign in to comment.