packages feed

http-tower-hs-0.1.0.0: src/Network/HTTP/Tower/Middleware/Hedge.hs

{-# LANGUAGE NumericUnderscores #-}

-- |
-- Module      : Network.HTTP.Tower.Middleware.Hedge
-- Description : Speculative retry via async race
-- License     : MIT
--
-- If the primary request is slow, fire a second speculative request after
-- a delay and return whichever finishes first.
--
-- @
-- client '|>' 'withHedge' 200  -- hedge after 200ms
-- @
--
-- __Only use for idempotent requests__ (GET, etc.) since the request may
-- be sent twice.
module Network.HTTP.Tower.Middleware.Hedge
  ( withHedge
  ) where

import Control.Concurrent (threadDelay)
import Control.Concurrent.Async (race)

import Network.HTTP.Tower.Core (Service(..), Middleware)
import Network.HTTP.Tower.Error (ServiceError)

-- | Hedging middleware: if the primary request doesn't complete within
-- @delayMs@ milliseconds, fire a second request and return whichever
-- finishes first.
withHedge :: Int -> Middleware req res
withHedge delayMs inner = Service $ \req -> do
  let primary = runService inner req
      hedged = do
        threadDelay (delayMs * 1_000)
        runService inner req
  result <- race primary hedged
  pure $ case result of
    Left res  -> res
    Right res -> res