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 2a75bbc3 Ignore canceled requests in the circuit breaker (#3532)
2a75bbc3 is described below

commit 2a75bbc3878c14505b92e580e6d0c7f9bcf3e686
Author: wushinanqiyi <[email protected]>
AuthorDate: Sun Sep 13 22:28:37 2026 +0800

    Ignore canceled requests in the circuit breaker (#3532)
---
 src/brpc/circuit_breaker.cpp           |  5 +++-
 test/brpc_circuit_breaker_unittest.cpp | 55 ++++++++++++++++++++++++++++++++++
 2 files changed, 59 insertions(+), 1 deletion(-)

diff --git a/src/brpc/circuit_breaker.cpp b/src/brpc/circuit_breaker.cpp
index 785ec77a..385c5a7b 100644
--- a/src/brpc/circuit_breaker.cpp
+++ b/src/brpc/circuit_breaker.cpp
@@ -18,6 +18,7 @@
 #include "brpc/circuit_breaker.h"
 
 #include <cmath>
+#include <errno.h>
 #include <gflags/gflags.h>
 
 #include "brpc/errno.pb.h"
@@ -191,7 +192,9 @@ bool CircuitBreaker::OnCallEnd(int error_code, int64_t 
latency) {
     // since the latency corresponding to ELIMIT is usually very small, we
     // cannot handle it as a successful request. Here we simply ignore the 
requests
     // that returned ELIMIT.
-    if (error_code == ELIMIT) {
+    // Canceled requests do not indicate a server failure and should not
+    // contribute samples or count as successful half-open probes either.
+    if (error_code == ELIMIT || error_code == ECANCELED) {
         return true;
     }
     if (_broken.load(butil::memory_order_relaxed)) {
diff --git a/test/brpc_circuit_breaker_unittest.cpp 
b/test/brpc_circuit_breaker_unittest.cpp
index 607fede9..975f3c65 100644
--- a/test/brpc_circuit_breaker_unittest.cpp
+++ b/test/brpc_circuit_breaker_unittest.cpp
@@ -19,6 +19,7 @@
 
 // Date: 2018/09/19 14:51:06
 
+#include <errno.h>
 #include <pthread.h>
 #include <gtest/gtest.h>
 #include <gflags/gflags.h>
@@ -150,6 +151,60 @@ TEST_F(CircuitBreakerTest, should_not_isolate) {
     }
 }
 
+TEST_F(CircuitBreakerTest, canceled_requests_during_initialization) {
+    brpc::CircuitBreaker baseline;
+    for (int i = 0; i < 2 * kLongWindowSize; ++i) {
+        ASSERT_TRUE(_circuit_breaker.OnCallEnd(ECANCELED, kLatency));
+    }
+    EXPECT_EQ(0, _circuit_breaker.isolated_times());
+
+    // Cancellations must not advance initialization or consume its error 
budget.
+    bool healthy = true;
+    for (int i = 0; i < kLongWindowSize && healthy; ++i) {
+        healthy = baseline.OnCallEnd(kErrorCodeForFailed, kErrorCost);
+        ASSERT_EQ(healthy,
+                  _circuit_breaker.OnCallEnd(kErrorCodeForFailed, kErrorCost));
+    }
+    EXPECT_FALSE(healthy);
+    EXPECT_EQ(1, _circuit_breaker.isolated_times());
+}
+
+TEST_F(CircuitBreakerTest, canceled_requests_after_initialization) {
+    brpc::CircuitBreaker baseline;
+    for (int i = 0; i < 2 * kLongWindowSize; ++i) {
+        ASSERT_TRUE(baseline.OnCallEnd(0, kLatency));
+        ASSERT_TRUE(_circuit_breaker.OnCallEnd(0, kLatency));
+    }
+
+    // Interleaved cancellations must neither add error cost nor decay it
+    // like successful requests, regardless of their latency.
+    bool healthy = true;
+    for (int i = 0; i < 2 * kLongWindowSize && healthy; ++i) {
+        ASSERT_TRUE(_circuit_breaker.OnCallEnd(ECANCELED, 1));
+        ASSERT_TRUE(_circuit_breaker.OnCallEnd(ECANCELED, 100 * kLatency));
+        healthy = baseline.OnCallEnd(kErrorCodeForFailed, kErrorCost);
+        ASSERT_EQ(healthy,
+                  _circuit_breaker.OnCallEnd(kErrorCodeForFailed, kErrorCost));
+    }
+    EXPECT_FALSE(healthy);
+    EXPECT_EQ(1, _circuit_breaker.isolated_times());
+}
+
+TEST_F(CircuitBreakerTest, canceled_requests_in_half_open) {
+    GFLAGS_NAMESPACE::FlagSaver flag_saver;
+    brpc::FLAGS_circuit_breaker_half_open_window_size = 2;
+    _circuit_breaker.Reset();
+    ASSERT_TRUE(_circuit_breaker.OnCallEnd(0, kLatency));
+    for (int i = 0; i < 2 * kLongWindowSize; ++i) {
+        ASSERT_TRUE(_circuit_breaker.OnCallEnd(ECANCELED, kLatency));
+    }
+    EXPECT_EQ(0, _circuit_breaker.isolated_times());
+
+    // One successful probe is still missing: a real error must reopen it.
+    EXPECT_FALSE(_circuit_breaker.OnCallEnd(kErrorCodeForFailed, kErrorCost));
+    EXPECT_EQ(1, _circuit_breaker.isolated_times());
+}
+
 TEST_F(CircuitBreakerTest, should_isolate) {
     std::vector<pthread_t> thread_list;
     std::vector<std::unique_ptr<FeedbackControl>> fc_list;


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to