w41ter / sekas

Sekas is a fault-tolerant, scalable, distributed transactional key-value store, used as a cache, database, and storage engine for other distributed systems.
Apache License 2.0
12 stars 1 forks source link

Optimize `RetryState` to reduce boilerplate code for retries #59

Open w41ter opened 7 months ago

w41ter commented 7 months ago

Here is the structure to use RetryState, maybe it can be optimized here:

    async fn try_create_shard(&self, group_id: u64, desc: &ShardDesc) -> Result<()> {
        let mut group_client = self.core.root_shared.transport_manager.lazy_group_client(group_id);
        let mut retry_state = RetryState::new(Duration::from_secs(10));
        loop {
            match group_client.create_shard(desc).await {
                Ok(()) => return Ok(()),
                Err(err) => {
                    retry_state.retry(err).await?;
                }
            }
        }
    }
w41ter commented 7 months ago

Here is a simple solution:

pub trait Retryable<T> {
    async fn retry(&self, retry_state: RetryState) -> Result<T>;
}

impl<F, Future, T> Retryable<T> for F
where
    F: Fn() -> Future,
    Future: std::future::Future<Output = Result<T>>,
{
    async fn retry(&self, retry_state: RetryState) -> Result<T> {
        let mut retry_state = retry_state;
        loop {
            match self().await {
                Ok(result) => return Ok(result),
                Err(err) => retry_state.retry(err).await?,
            }
        }
    }
}

// usage

    pub async fn acquire_shard(&mut self, desc: &MoveShardDesc) -> Result<()> {
        { || async { self.group_client().acquire_shard(desc).await } }
            .retry(RetryState::default())
            .await
    }