Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

321 update the circuit breaker retries logic #336

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

Blindspot22
Copy link
Collaborator

No description provided.

@Blindspot22 Blindspot22 added enhancement New feature or request implementation update This doesn't seem right labels Feb 24, 2025
@Blindspot22 Blindspot22 self-assigned this Feb 24, 2025
@Blindspot22 Blindspot22 linked an issue Feb 24, 2025 that may be closed by this pull request
@Blindspot22 Blindspot22 requested review from Hermann-Core, IngridPuppet and Christiantyemele and removed request for Hermann-Core February 24, 2025 09:27
@Blindspot22
Copy link
Collaborator Author

Still adjusting my test. Should be done by the end of the day

Copy link
Collaborator

@Hermann-Core Hermann-Core left a comment

Choose a reason for hiding this comment

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

The goal of this ticket was to adjust the retry logic for the futures. The future should initially fail once before the specified number of retries is executed. Currently, the implementation only retries the future the specified number of times, without accounting for the initial failure. For example, if the retry count is set to 3, the future is retried only 3 times instead of the expected 4.

I should look first where the number of retries is checked.

Comment on lines 175 to 210
pub fn call<F, Fut, T, E>(&self, mut f: F) -> impl Future<Output = Result<T, Error<E>>>
where
F: FnMut() -> Fut,
Fut: Future<Output = Result<T, E>>,
{
ResultFuture {
factory: f,
state: State::Initial,
breaker: self.clone(),
let breaker = self.clone();

async move {
if !breaker.should_allow_call() {
return Err(Error::CircuitOpen);
}

let mut attempts = 0;

loop {
attempts += 1;
let result = f().await;

match result {
Ok(val) => {
breaker.success();
return Ok(val);
}
Err(err) => {
breaker.failure();

if attempts >= breaker.inner.lock().max_retries {
return Err(Error::Inner(err));
}

if !breaker.should_allow_call() {
return Err(Error::CircuitOpen);
}
}
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Don't change the implementation logic. That is not the aim of the ticket.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Okay

Comment on lines 64 to 71
let attempts_clone = attempts.clone();

let retry_operation = || {
let attempts = attempts_clone.clone();
async move {
attempts.fetch_add(1, Ordering::AcqRel);
Err::<(), ()>(()) // Simulate a failure
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

You don't need to change anything here.

Comment on lines 100 to 143
// #[tokio::test]
// async fn test_exponential_backoff() {
// let breaker = CircuitBreaker::new()
// .retries(3)
// .exponential_backoff(Duration::from_millis(100));

// let start = Instant::now();
// let _ = breaker.call(|| async { Err::<(), ()>(()) }).await;
// let elapsed = start.elapsed();

// Log the elapsed time for debugging
// println!("Elapsed time: {:?}", elapsed);

// After the first failure, we wait 100ms before retrying
// After the second failure, we wait 200ms before retrying
// After the third failure, we should not retry anymore, and the circuit should open
// assert!(elapsed >= Duration::from_millis(290) && elapsed < Duration::from_millis(600)); // Widen range further
// assert!(!breaker.should_allow_call()); // Circuit should be open after 3 retries
// }

// #[tokio::test]
// async fn test_half_open_state_failure() {
// let breaker = CircuitBreaker::new()
// .retries(1) // Allow 1 retry
// .reset_timeout(Duration::from_millis(100));

// Trigger a failure to open the circuit
// let _ = breaker.call(|| async { Err::<(), ()>(()) }).await;
// assert!(!breaker.should_allow_call()); // Circuit should be open

// Wait for the reset timeout to allow the circuit to move to half-open state
// sleep(Duration::from_millis(150)).await;

// Verify the circuit is in the half-open state
// assert!(breaker.should_allow_call());
// assert_eq!(breaker.inner.lock().state, CircuitState::HalfOpen);

// Call again and trigger another failure
// let result = breaker.call(|| async { Err::<(), ()>(()) }).await;

// The circuit should open again after this failure
// assert_eq!(result, Err(Error::CircuitOpen)); // Circuit should be open again after failure
// assert_eq!(breaker.inner.lock().state, CircuitState::Open);
// }
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why have you commented out the tests?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

No don't worry about that, I have already un-commented the test. I was just testing out something.
I will do a new push with all your suggestions 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request implementation update This doesn't seem right
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Update the Circuit Breaker Retries Logic Implementation
2 participants