fix(async/unstable): evaluate circuit breaker failure rate on success - #7308
tomas-zijdemans wants to merge 2 commits into
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7308 +/- ##
========================================
Coverage 95.04% 95.04%
========================================
Files 619 618 -1
Lines 52012 51760 -252
Branches 9450 9399 -51
========================================
- Hits 49433 49195 -238
+ Misses 2031 2022 -9
+ Partials 548 543 -5 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Thanks, the fix looks correct and matches Resilience4j's behaviour of checking thresholds on every recorded outcome. This conflicts with #7306 (in Optional nit: the closed → open block here repeats |
0b452d0 to
80072c4
Compare
|
Rebased onto main now that #7306 and #7307 are in. #handleSuccess takes generation and keeps the stale half-open guard, and both groups of tests are there. I took the nit. #open() now does the state write plus onStateChange and onOpen, and both handlers call it. To keep the callbacks in the order they had (onFailure, then onStateChange, then onOpen), #handleFailure fires onFailure first and decides whether to open afterwards. That has two visible effects:
The second one corrects a mislabel. If you'd rather keep "open" visible inside onFailure, I can split the state write out of the helper. There's a new test that pins the callback order on a tripping failure. I left forceOpen() alone because it also resets openedAt when the circuit is already open, which the helper doesn't do. |
A success in closed state returned before the failure rate was checked. When that success was the request that lifted the window to
minimumThroughput, the circuit stayed closed even though the rate already qualified. WithminimumThroughput: 3andfailureRateThreshold: 0.5, failure, failure, success left the circuit closed at a 67% failure rate. The docs say it opens.Closed-state successes now run the same rate check as failures. Both handlers share
#exceedsFailureRate()for the check and#open()for the transition, so they can't drift apart.#handleFailurenow firesonFailurebefore deciding whether to open, which keeps the callback order atonFailure,onStateChange,onOpen. Two visible effects:breaker.stateinsideonFailurereads the state before the transition, not"open".frominonStateChangeis the circuit's current state, not the state captured at request start. These only differ for stale requests. A half-open request that fails after the circuit closed now reportsclosed -> open.Tests cover a success that trips the circuit, a success that keeps the rate below the threshold, and the callback order on a tripping failure.
I used Claude Code to help investigate and write this change.