1 {-# LANGUAGE MultiWayIf, FunctionalDependencies, MultiParamTypeClasses, TypeFamilies #-}
2 module Gargantext.Utils.Jobs.Monad (
27 import Gargantext.Utils.Jobs.Settings
28 import Gargantext.Utils.Jobs.Map
29 import Gargantext.Utils.Jobs.Queue
30 import Gargantext.Utils.Jobs.State
32 import Control.Concurrent.STM
33 import Control.Exception
34 import Control.Monad.Except
35 import Data.Kind (Type)
36 import Data.Map.Strict (Map)
37 import Data.Time.Clock
38 import Network.HTTP.Client (Manager)
41 import qualified Servant.Job.Core as SJ
42 import qualified Servant.Job.Types as SJ
44 data JobEnv t w a = JobEnv
45 { jeSettings :: JobSettings
46 , jeState :: JobsState t w a
47 , jeManager :: Manager
51 :: (EnumBounded t, Monoid w)
56 newJobEnv js prios mgr = JobEnv js <$> newJobsState js prios <*> pure mgr
60 defaultJobSettings :: NumRunners -> SJ.SecretKey -> JobSettings
61 defaultJobSettings numRunners k = JobSettings
62 { jsNumRunners = numRunners
63 , jsJobTimeout = 30 * 60 -- 30 minutes
64 , jsIDTimeout = 30 * 60 -- 30 minutes
65 , jsGcPeriod = 1 * 60 -- 1 minute
69 genSecret :: IO SJ.SecretKey
70 genSecret = SJ.generateSecretKey
72 class MonadIO m => MonadJob m t w a | m -> t w a where
73 getJobEnv :: m (JobEnv t w a)
75 getJobsSettings :: MonadJob m t w a => m JobSettings
76 getJobsSettings = jeSettings <$> getJobEnv
78 getJobsState :: MonadJob m t w a => m (JobsState t w a)
79 getJobsState = jeState <$> getJobEnv
81 getJobsMap :: MonadJob m t w a => m (JobMap (SJ.JobID 'SJ.Safe) w a)
82 getJobsMap = jobsData <$> getJobsState
84 getJobsQueue :: MonadJob m t w a => m (Queue t (SJ.JobID 'SJ.Safe))
85 getJobsQueue = jobsQ <$> getJobsState
88 :: (MonadJob m t w a, Ord t)
91 -> (SJ.JobID 'SJ.Safe -> i -> Logger w -> IO a)
92 -> m (SJ.JobID 'SJ.Safe)
93 queueJob jobkind input f = do
96 liftIO (pushJob jobkind input f js st)
101 -> m (Maybe (JobEntry (SJ.JobID 'SJ.Safe) w a))
104 liftIO $ lookupJob jid jmap
111 | JobException SomeException
116 => SJ.JobID 'SJ.Unsafe
117 -> m (Either JobError (SJ.JobID 'SJ.Safe))
118 checkJID (SJ.PrivateID tn n t d) = do
119 now <- liftIO getCurrentTime
120 js <- getJobsSettings
121 if | tn /= "job" -> return (Left InvalidIDType)
122 | now > addUTCTime (fromIntegral $ jsIDTimeout js) t -> return (Left IDExpired)
123 | d /= SJ.macID tn (jsSecretKey js) t n -> return (Left InvalidMacID)
124 | otherwise -> return $ Right (SJ.PrivateID tn n t d)
128 => SJ.JobID 'SJ.Unsafe
129 -> (SJ.JobID 'SJ.Safe -> JobEntry (SJ.JobID 'SJ.Safe) w a -> m r)
130 -> m (Either JobError (Maybe r))
134 Left e -> return (Left e)
138 Nothing -> return (Right Nothing)
139 Just j -> Right . Just <$> f jid' j
144 -> m (Either JobError a)
146 handleIDError toE act = act >>= \r -> case r of
147 Left err -> throwError (toE err)
151 :: (Ord t, MonadJob m t w a)
152 => Bool -- is it queued (and we have to remove jid from queue)
156 removeJob queued t jid = do
159 liftIO . atomically $ do
165 -- Tracking jobs status
168 -- | A monad to query for the status of a particular job /and/ submit updates for in-progress jobs.
169 class MonadJob m (JobType m) (t [JobEventType m]) (JobOutputType m) => MonadJobStatus m t where
170 type JobType m :: Type
171 type JobOutputType m :: Type
172 type JobEventType m :: Type
173 type JobErrorType m :: Type