2023-07-05 17:21:33 +03:00
# Stamina
2023-07-04 17:36:38 +03:00
2023-12-30 18:14:51 +03:00
[![Active – The project has reached a stable, usable state and is being actively developed. ](https://www.repostatus.org/badges/latest/active.svg )](https://www.repostatus.org/#concept) [![Hackage ](https://img.shields.io/hackage/v/stamina.svg?style=flat )](https://hackage.haskell.org/package/stamina) ![CI status ](https://github.com/cachix/stamina.hs/actions/workflows/ci.yml/badge.svg )
2023-07-04 18:46:57 +03:00
2023-07-05 17:14:19 +03:00
A retry Haskell library for humans:
2023-07-05 17:18:27 +03:00
- **Exponential backoff** with **jitter** between retries.
- Limit the **attempts** of retries and **total** time.
2023-12-30 18:14:51 +03:00
- `Stamina.HTTP` for retrying retriable `Network.HTTP.Client` exceptions respecting `Retry-After` headers.
- Introspectable retry state for logging using `RetryStatus` , including the last exception that occurred.
- Support resetting the retry state when the action is long-running with an attempt that works.
2023-07-04 18:46:57 +03:00
2023-07-05 17:14:19 +03:00
## API
2023-12-30 18:14:51 +03:00
## Basics
2023-07-05 17:14:19 +03:00
2023-12-30 18:14:51 +03:00
- `Stamina.defaults :: (MonadIO m) => m RetrySettings`
- `Stamina.RetryStatus = RetryStatus { attempts :: Int, delay :: NominalDiffTime, totalDelay :: NominalDiffTime, resetInitial :: IO (), lastException :: Maybe SomeException }`
- `Stamina.retry :: (MonadCatch m, MonadIO m) => RetrySettings -> (RetryStatus -> m a) -> m a`
## Exceptions
- `Stamina.RetryAction = RaiseException | Retry | RetryDelay NominalDiffTime | RetryTime UTCTime`
- `Stamina.retryFor :: (MonadCatch m, MonadIO m, Exception exc) => RetrySettings -> (exc -> m RetryAction) -> (RetryStatus -> m a) -> m a`
## HTTP
- `Stamina.HTPP.retry :: (MonadIO m, MonadCatch m) => Stamina.RetrySettings -> (Stamina.RetryStatus -> m a) -> m a`
## Basic example
2023-07-05 17:14:19 +03:00
```haskell
import qualified Stamina
2023-12-30 14:38:00 +03:00
import Control.Monad.Catch (throwM)
2023-12-30 18:00:07 +03:00
import Control.Monad.IO.Class (MonadIO)
2023-07-05 17:14:19 +03:00
2023-12-30 14:38:00 +03:00
go :: IO ()
go = do
defaults < - Stamina . defaults
Stamina.retry defaults $ \retryStatus -> do
throwM $ userError "nope"
```
## Example to catch specific exceptions
2023-07-05 17:14:19 +03:00
2023-12-30 14:38:00 +03:00
```haskell
2023-12-30 18:00:07 +03:00
handler :: (MonadIO m) => IOError -> m Stamina.RetryAction
handler _ = return Stamina.Retry
2023-12-30 14:38:00 +03:00
go2 :: IO ()
go2 = do
defaults < - Stamina . defaults
2023-12-30 18:03:11 +03:00
Stamina.retryFor defaults handler $ \retryStatus -> do
2023-12-30 14:38:00 +03:00
throwM $ userError "nope"
2023-07-05 17:14:19 +03:00
```
2023-07-04 18:46:57 +03:00
## Development
1. Install [devenv.sh ](https://devenv.sh/getting-started/ ).
2. `devenv shell`
2023-07-05 17:14:19 +03:00
3. `stack build`
## Credits
- Heavily inspired by [stamina for Python ](https://stamina.hynek.me/en/stable/tutorial.html#retries ).
2023-12-21 15:09:45 +03:00
- [retry ](https://github.com/Soostone/retry ) as case study for what needs to be supported.
2023-12-30 14:38:00 +03:00
< details >
< summary > Test setup< / summary >
```haskell
main = undefined
```
< / details >