stamina.hs/README.md

74 lines
2.5 KiB
Markdown
Raw Permalink Normal View History

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:17:39 +03:00
- **Stamina.HTTP** for retrying retriable [Network.HTTP.Client](https://hackage.haskell.org/package/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 for **long-running tasks** 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
2024-01-05 14:45:26 +03:00
- `Stamina.HTTP.retry :: (MonadIO m, MonadCatch m) => Stamina.RetrySettings -> (Stamina.RetryStatus -> m a) -> m a`
2023-12-30 18:14:51 +03:00
## 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 ()
2023-12-30 19:18:23 +03:00
go = Stamina.retry Stamina.defaults $ \retryStatus -> do
throwM $ userError "nope"
2023-12-30 14:38:00 +03:00
```
## 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 ()
2023-12-30 19:18:23 +03:00
go2 = Stamina.retryFor Stamina.defaults handler $ \retryStatus -> do
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
```
2024-01-03 15:51:52 +03:00
</details>