Cache getAllLectures requests for 60 seconds
We do this because Squeak doesn't have a getLectureById endpoint yet.
This commit is contained in:
parent
bb9a0643f1
commit
28598ad2cd
@ -40,7 +40,7 @@ library
|
||||
, blaze-html >=0.9
|
||||
, req >=3.10
|
||||
, text >=1.0
|
||||
, time
|
||||
, time >=1.13
|
||||
default-language: Haskell2010
|
||||
|
||||
executable lisa-exe
|
||||
@ -58,7 +58,7 @@ executable lisa-exe
|
||||
, lisa
|
||||
, req >=3.10
|
||||
, text >=1.0
|
||||
, time
|
||||
, time >=1.13
|
||||
default-language: Haskell2010
|
||||
|
||||
test-suite lisa-test
|
||||
@ -77,5 +77,5 @@ test-suite lisa-test
|
||||
, lisa
|
||||
, req >=3.10
|
||||
, text >=1.0
|
||||
, time
|
||||
, time >=1.13
|
||||
default-language: Haskell2010
|
||||
|
@ -26,7 +26,7 @@ dependencies:
|
||||
- req >= 3.10
|
||||
- Spock >= 0.14
|
||||
- text >= 1.0
|
||||
- time
|
||||
- time >= 1.13
|
||||
|
||||
library:
|
||||
source-dirs: src
|
||||
|
34
src/Lisa.hs
34
src/Lisa.hs
@ -10,9 +10,11 @@ module Lisa
|
||||
import Control.Concurrent (MVar, newMVar, putMVar, takeMVar)
|
||||
import Control.Monad.IO.Class (MonadIO, liftIO)
|
||||
import Data.Maybe (fromMaybe)
|
||||
import Data.Time.Clock (UTCTime, getCurrentTime, addUTCTime)
|
||||
import Network.HTTP.Req (https)
|
||||
import Text.Blaze.Html (Html)
|
||||
import Text.Blaze.Html.Renderer.Utf8 (renderHtml)
|
||||
import Text.Printf (printf)
|
||||
import Web.Spock (SpockAction, SpockM, ActionCtxT, get, getState, lazyBytes, param, root, setHeader, var, (<//>))
|
||||
import Web.Spock.Config (PoolOrConn(PCNoDatabase), SpockCfg, defaultSpockCfg)
|
||||
|
||||
@ -36,27 +38,39 @@ instance SqueakCtx (SpockAction () () State) where
|
||||
-- State
|
||||
|
||||
data State = State
|
||||
{ stateLectures :: MVar (Maybe [Lecture]) -- ^ Cached lectures
|
||||
{ stateLectures :: MVar (UTCTime, [Lecture]) -- ^ Cached lectures with cache expiration time.
|
||||
}
|
||||
|
||||
-- | Get lectures from cache if they've been retrieved before or from Squeak.
|
||||
-- Uses 'MVar's for synchronisation: As long as each thread 'takeMVar's before it 'putMVar's, we're in the clear.
|
||||
-- TODO: Evaluate lectures to NF before 'putMVar'ing them.
|
||||
getAllLecturesCached :: SpockAction () () State (Either SqueakError [Lecture])
|
||||
getAllLecturesCached = do
|
||||
lecturesMVar <- stateLectures <$> getState
|
||||
contents <- liftIO $ takeMVar lecturesMVar
|
||||
lectures' <- case contents of
|
||||
Just lectures -> pure $ Right lectures
|
||||
Nothing -> do
|
||||
liftIO $ putStrLn "Fetching lectures for the first time..."
|
||||
Squeak.getAllLectures
|
||||
liftIO $ putMVar lecturesMVar $ either (const Nothing) Just lectures'
|
||||
pure lectures'
|
||||
(expirationTime, lectures) <- liftIO $ takeMVar lecturesMVar
|
||||
now <- liftIO getCurrentTime
|
||||
(newContents, result) <- if now >= expirationTime
|
||||
then -- Cache expired => fetch lectures again.
|
||||
Squeak.getAllLectures >>= \case
|
||||
-- On error: Clear cache.
|
||||
Left err ->
|
||||
pure ((now, []), Left err)
|
||||
-- On success: Cache fetched lectures for 60 seconds.
|
||||
Right lectures' -> do
|
||||
let expirationTime' = addUTCTime 60 now
|
||||
liftIO $ printf "Fetched %d lectures, valid until %s\n" (length lectures') (show expirationTime')
|
||||
pure ((expirationTime', lectures'), Right lectures')
|
||||
else -- Cache still valid => return it.
|
||||
pure ((expirationTime, lectures), Right lectures)
|
||||
liftIO $ putMVar lecturesMVar newContents
|
||||
pure result
|
||||
|
||||
-- Exports
|
||||
|
||||
mkConfig :: IO (SpockCfg () () State)
|
||||
mkConfig = do
|
||||
lecturesMVar <- newMVar Nothing
|
||||
now <- getCurrentTime
|
||||
lecturesMVar <- newMVar (now, [])
|
||||
defaultSpockCfg () PCNoDatabase (State lecturesMVar)
|
||||
|
||||
app :: SpockM () () State ()
|
||||
|
Loading…
x
Reference in New Issue
Block a user