Skip to content

Ignore canceled requests in the circuit breaker - #3532

Open
wushinanqiyi wants to merge 1 commit into
apache:masterfrom
wushinanqiyi:fix/ignore-ecanceled-in-circuit-breaker
Open

Ignore canceled requests in the circuit breaker#3532
wushinanqiyi wants to merge 1 commit into
apache:masterfrom
wushinanqiyi:fix/ignore-ecanceled-in-circuit-breaker

Conversation

@wushinanqiyi

Copy link
Copy Markdown

What problem does this PR solve?

Canceled RPCs use ECANCELED, which currently contributes to circuit-breaker error statistics and immediately reopens a half-open circuit. Caller cancellation does not indicate a downstream server failure.

What is changed and the side effects?

Ignore ECANCELED alongside ELIMIT at the start of CircuitBreaker::OnCallEnd. Canceled requests do not affect sampling, error cost, latency, or the successful half-open probe count.

Add regression tests for cancellation during initialization, interleaved cancellation after initialization, and cancellation in the half-open state. The tests also check that genuine errors still trigger isolation.

Performance effects: one additional error-code comparison per call.
Breaking backward compatibility: no API changes; cancellation no longer causes circuit-breaker isolation.

Validation

  • git diff --check: passed.
  • Native brpc unit tests: not run locally; this Windows host has no configured Linux/WSL build environment or brpc dependencies. Linux/macOS CI validation is still required.

@wwbmmm
wwbmmm requested a lite review from Copilot September 8, 2026 09:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@wwbmmm
wwbmmm requested a lite review from Copilot September 8, 2026 10:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@wwbmmm
wwbmmm requested a lite review from Copilot September 8, 2026 11:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@wwbmmm
wwbmmm requested a lite review from Copilot September 8, 2026 12:14

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@wasphin
wasphin requested a lite review from Copilot September 8, 2026 13:59

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Pull request overview

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

Comment on lines +154 to +170
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());
}
Comment on lines +172 to +191
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());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants