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

Support Result-based retries -- return the last result's error instead of a custom error type #17

Open
mmstick opened this issue Jan 3, 2019 · 3 comments

Comments

@mmstick
Copy link

mmstick commented Jan 3, 2019

No description provided.

@mmstick
Copy link
Author

mmstick commented Jan 3, 2019

Looking for a solution like this:

#[derive(SmartDefault)]
struct Retry {
    #[default = "3"]
    attempts: u64,
    #[default = "1000"]
    interval: u64,
}

impl Retry {
    pub fn attempts(mut self, attempts: u64) -> Self {
        self.attempts = attempts;
        self
    }

    pub fn interval(mut self, interval: u64) -> Self {
        self.interval = interval;
        self
    }

    pub fn retry_until_ok<F, T, E>(&self, mut func: F) -> Result<T, E>
        where F: FnMut() -> Result<T, E>
    {
        let duration = ::std::time::Duration::from_millis(self.interval);
        let mut attempt = 0;
        loop {
            match func() {
                Ok(value) => return Ok(value),
                Err(why)  => {
                    if attempt == self.attempts {
                        return Err(why);
                    } else {
                        attempt += 1;
                        ::std::thread::sleep(duration);
                    }
                }
            }
        }
    }
}
pub fn fsck<P: AsRef<Path>>(part: P, cmd: Option<(&str, &str)>) -> io::Result<()> {
    let (cmd, arg) = cmd.unwrap_or(("fsck", "-fy"));

    // Attempt this once every second, with up to 3 attempts before giving up.
    Retry::default()
        .attempts(3)
        .interval(1000)
        .retry_until_ok(move || exec(cmd, None, None, &[arg.into(), part.as_ref().into()]))
}

@jimmycuadra
Copy link
Owner

Sounds like a good idea to me!

@wellcaffeinated
Copy link

This is a great idea.

I just implemented this for myself:

use ::std::time::Duration;

pub enum RetryResult<T, E> {
  Ok(T),
  Retry(E),
  Err(E),
}

impl<T, E> From<Result<T, E>> for RetryResult<T, E> {
  fn from(r : Result<T, E>) -> Self {
    match r {
      Ok(v) => RetryResult::Ok(v),
      Err(e) => RetryResult::Retry(e),
    }
  }
}

pub struct Retry {
  attempts: Option<u64>,
  interval: Duration,
}

impl Default for Retry {
  fn default() -> Self {
    Retry {
      attempts: None,
      interval: Duration::from_millis(1000),
    }
  }
}

impl Retry {
  pub fn attempts(mut self, attempts: u64) -> Self {
    self.attempts = Some(attempts);
    self
  }

  pub fn interval(mut self, interval: u64) -> Self {
    self.interval = Duration::from_millis(interval);
    self
  }

  pub fn retry_until_ok<F, T, E, R>(&self, mut func: F) -> Result<T, E>
    where F: FnMut() -> R,
      R : Into<RetryResult<T, E>>
  {
    let mut attempt = 0;
    loop {
      match func().into() {
        RetryResult::Ok(value) => return Ok(value),
        RetryResult::Err(why) => return Err(why),
        RetryResult::Retry(why) => {
          if Some(attempt) == self.attempts {
            return Err(why);
          } else {
            attempt += 1;
            ::std::thread::sleep(self.interval);
          }
        }
      }
    }
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants