module Glue.Preload(
PreloadedOptions
, defaultPreloadedOptions
, preloadingService
) where
import Glue.Types
import Data.Hashable
import qualified Data.HashSet as S
import qualified Data.HashMap.Strict as M
import qualified Control.Monad.Loops as L
import Control.Concurrent.Lifted
import Control.Monad.Trans.Control
import Control.Monad.IO.Class
data PreloadedOptions a m n = PreloadedOptions {
preloadedKeys :: S.HashSet a,
preloadingRefreshTimeMs :: Int,
executePreload :: m () -> n ()
}
defaultPreloadedOptions :: S.HashSet a -> (m () -> n ()) -> PreloadedOptions a m n
defaultPreloadedOptions toPreload executeP = PreloadedOptions {
preloadedKeys = toPreload,
preloadingRefreshTimeMs = 30 * 1000,
executePreload = executeP
}
preloadingService :: (MonadIO m, MonadBaseControl IO m, MonadBaseControl IO n, Eq a, Hashable a)
=> PreloadedOptions a m n
-> MultiGetService m a b
-> n (MultiGetService m a b, () -> n ())
preloadingService PreloadedOptions{..} service = do
!preloadedVar <- newEmptyMVar
!shouldContinueVar <- newMVar True
let !updatePreloaded = do
result <- makeCall service preloadedKeys
_ <- tryTakeMVar preloadedVar
putMVar preloadedVar result
threadDelay (preloadingRefreshTimeMs * 1000)
_ <- fork $ L.whileM_ (readMVar shouldContinueVar) $ executePreload $ updatePreloaded
let plService request = do
let fromPreloadKeys = S.intersection request preloadedKeys
let fromServiceKeys = S.difference request preloadedKeys
!fromPreload <- if S.null fromPreloadKeys then return M.empty else fmap (M.filterWithKey (\k -> \_ -> S.member k fromPreloadKeys)) $ getResult preloadedVar
!fromService <- if S.null fromServiceKeys then return M.empty else service fromServiceKeys
return $ M.union fromService fromPreload
return (plService, \_ -> tryTakeMVar shouldContinueVar >> putMVar shouldContinueVar False)