This is an automated email from the ASF dual-hosted git repository.
chenBright pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git
The following commit(s) were added to refs/heads/master by this push:
new 7f646631 Fix WeightedRandomizedLoadBalancer skipping the last server
(#3426)
7f646631 is described below
commit 7f6466313574b7e9159f3a81fddc1d1288400003
Author: nas <[email protected]>
AuthorDate: Sun Aug 16 05:39:37 2026 -0400
Fix WeightedRandomizedLoadBalancer skipping the last server (#3426)
* fix WeightedRandomizedLoadBalancer skipping the last server
SelectServer() draws random_weight from fast_rand_less_than(weight_sum),
i.e. from [0, weight_sum - 1], and then lower_bound()s it against
Server::current_weight_sum, which Add() fills with an inclusive prefix
sum. lower_bound() returns the first server whose prefix sum is >=
random_weight, but a server owns the half-open range
[prefix(i-1), prefix(i)), so the predicate has to be > random_weight.
Because of that the first server in the list also serves
random_weight == prefix(0) and the last server never serves anything at
all, since random_weight can never reach weight_sum. With four servers of
equal weight the measured distribution is 49.8/25.1/25.1/0.0 percent
instead of 25 percent each.
Search for random_weight + 1 so that lower_bound() lands on the first
prefix sum strictly greater than random_weight.
The existing weighted_randomized test does not catch this: its servers
have weights 3/2/5/10 and it only asserts that each rate is within
0.5x~2x of the expected one. The weight-10 server measures 0.448 before
this change and 0.494 after it, both inside that band. Add
weighted_randomized_equal_weight, which uses equal weights so that a
single misplaced slot is visible, and check the rates within 0.9x~1.1x.
Signed-off-by: Anas <[email protected]>
* use upper_bound for the weighted prefix-sum search
upper_bound(random_weight) states the intent directly: the first server
whose
inclusive prefix sum is strictly greater than random_weight. It is the same
search as lower_bound(random_weight + 1) without the increment.
Also correct the tolerance comment in the unit test: with run_times=40000
and
p=0.25 the count has sigma ~= 86.6, so the 0.9x~1.1x band is about 11 sigma,
not more than 20.
---------
Signed-off-by: Anas <[email protected]>
---
.../policy/weighted_randomized_load_balancer.cpp | 4 +-
test/brpc_load_balancer_unittest.cpp | 50 ++++++++++++++++++++++
2 files changed, 53 insertions(+), 1 deletion(-)
diff --git a/src/brpc/policy/weighted_randomized_load_balancer.cpp
b/src/brpc/policy/weighted_randomized_load_balancer.cpp
index 46923acb..d2786ed8 100644
--- a/src/brpc/policy/weighted_randomized_load_balancer.cpp
+++ b/src/brpc/policy/weighted_randomized_load_balancer.cpp
@@ -131,9 +131,11 @@ int WeightedRandomizedLoadBalancer::SelectServer(const
SelectIn& in, SelectOut*
uint64_t weight_sum = s->weight_sum;
for (size_t i = 0; i < n; ++i) {
uint64_t random_weight = butil::fast_rand_less_than(weight_sum);
+ // current_weight_sum is an inclusive prefix sum, so random_weight
belongs
+ // to the first server whose prefix sum is strictly greater than it.
const Server random_server(0, 0, random_weight);
const auto& server =
- std::lower_bound(s->server_list.begin(), s->server_list.end(),
+ std::upper_bound(s->server_list.begin(), s->server_list.end(),
random_server, server_compare);
const SocketId id = server->id;
if (ExcludedServers::IsExcluded(in.excluded, id)) {
diff --git a/test/brpc_load_balancer_unittest.cpp
b/test/brpc_load_balancer_unittest.cpp
index 0f0ecccc..1b326a8f 100644
--- a/test/brpc_load_balancer_unittest.cpp
+++ b/test/brpc_load_balancer_unittest.cpp
@@ -1026,6 +1026,56 @@ TEST_F(LoadBalancerTest, weighted_randomized) {
}
}
+TEST_F(LoadBalancerTest, weighted_randomized_equal_weight) {
+ // With equal weights every server must get the same share of the traffic.
+ // The tolerance of `weighted_randomized` above is +/-2x, which is too
loose
+ // to catch a single misplaced slot, so check the distribution tightly
here.
+ const char* servers[] = {
+ "10.92.115.19:8831",
+ "10.42.108.25:8832",
+ "10.36.150.31:8833",
+ "10.36.150.32:8899"
+ };
+ brpc::policy::WeightedRandomizedLoadBalancer wrlb;
+ for (size_t i = 0; i < ARRAY_SIZE(servers); ++i) {
+ butil::EndPoint dummy;
+ ASSERT_EQ(0, str2endpoint(servers[i], &dummy));
+ brpc::ServerId id(8888);
+ brpc::SocketOptions options;
+ options.remote_side = dummy;
+ options.user = new SaveRecycle;
+ ASSERT_EQ(0, brpc::Socket::Create(options, &id.id));
+ id.tag = "1";
+ ASSERT_TRUE(wrlb.AddServer(id));
+ }
+
+ std::map<butil::EndPoint, size_t> select_result;
+ brpc::SocketUniquePtr ptr;
+ brpc::LoadBalancer::SelectIn in = { 0, false, false, 0u, NULL };
+ brpc::LoadBalancer::SelectOut out(&ptr);
+ const int run_times = 40000;
+ for (int i = 0; i < run_times; ++i) {
+ ASSERT_EQ(0, wrlb.SelectServer(in, &out));
+ ++select_result[ptr->remote_side()];
+ }
+
+ // Every server must be selected at least once, in particular the one added
+ // last, which owns the largest prefix sum.
+ ASSERT_EQ(ARRAY_SIZE(servers), select_result.size());
+ const double expect_rate = 1.0 / ARRAY_SIZE(servers);
+ for (const auto& result : select_result) {
+ const double actual_rate = result.second * 1.0 / run_times;
+ std::cout << result.first << " select_times=" << result.second
+ << " actual_rate=" << actual_rate
+ << " expect_rate=" << expect_rate << std::endl;
+ // 0.9x ~ 1.1x of the expected rate. With n=40000 and p=0.25 the count
has
+ // sigma = sqrt(n*p*(1-p)) ~= 86.6, so the +-10% band is about 11 sigma
+ // wide and a passing run is not luck.
+ ASSERT_GE(actual_rate, expect_rate * 0.9);
+ ASSERT_LE(actual_rate, expect_rate * 1.1);
+ }
+}
+
TEST_F(LoadBalancerTest, health_check_no_valid_server) {
const char* servers[] = {
"10.92.115.19:8832",
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]