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.Sequence (Seq)
38 import Data.Time.Clock
39 import Network.HTTP.Client (Manager)
42 import qualified Servant.Job.Core as SJ
43 import qualified Servant.Job.Types as SJ
45 data JobEnv t w a = JobEnv
46 { jeSettings :: JobSettings
47 , jeState :: JobsState t w a
48 , jeManager :: Manager
52 :: (EnumBounded t, Monoid w)
57 newJobEnv js prios mgr = JobEnv js <$> newJobsState js prios <*> pure mgr
61 defaultJobSettings :: NumRunners -> SJ.SecretKey -> JobSettings
62 defaultJobSettings numRunners k = JobSettings
63 { jsNumRunners = numRunners
64 , jsJobTimeout = 30 * 60 -- 30 minutes
65 , jsIDTimeout = 30 * 60 -- 30 minutes
66 , jsGcPeriod = 1 * 60 -- 1 minute
70 genSecret :: IO SJ.SecretKey
71 genSecret = SJ.generateSecretKey
73 class MonadIO m => MonadJob m t w a | m -> t w a where
74 getJobEnv :: m (JobEnv t w a)
76 getJobsSettings :: MonadJob m t w a => m JobSettings
77 getJobsSettings = jeSettings <$> getJobEnv
79 getJobsState :: MonadJob m t w a => m (JobsState t w a)
80 getJobsState = jeState <$> getJobEnv
82 getJobsMap :: MonadJob m t w a => m (JobMap (SJ.JobID 'SJ.Safe) w a)
83 getJobsMap = jobsData <$> getJobsState
85 getJobsQueue :: MonadJob m t w a => m (Queue t (SJ.JobID 'SJ.Safe))
86 getJobsQueue = jobsQ <$> getJobsState
89 :: (MonadJob m t w a, Ord t)
92 -> (SJ.JobID 'SJ.Safe -> i -> Logger w -> IO a)
93 -> m (SJ.JobID 'SJ.Safe)
94 queueJob jobkind input f = do
97 liftIO (pushJob jobkind input f js st)
102 -> m (Maybe (JobEntry (SJ.JobID 'SJ.Safe) w a))
105 liftIO $ lookupJob jid jmap
112 | JobException SomeException
117 => SJ.JobID 'SJ.Unsafe
118 -> m (Either JobError (SJ.JobID 'SJ.Safe))
119 checkJID (SJ.PrivateID tn n t d) = do
120 now <- liftIO getCurrentTime
121 js <- getJobsSettings
122 if | tn /= "job" -> return (Left InvalidIDType)
123 | now > addUTCTime (fromIntegral $ jsIDTimeout js) t -> return (Left IDExpired)
124 | d /= SJ.macID tn (jsSecretKey js) t n -> return (Left InvalidMacID)
125 | otherwise -> return $ Right (SJ.PrivateID tn n t d)
129 => SJ.JobID 'SJ.Unsafe
130 -> (SJ.JobID 'SJ.Safe -> JobEntry (SJ.JobID 'SJ.Safe) w a -> m r)
131 -> m (Either JobError (Maybe r))
135 Left e -> return (Left e)
139 Nothing -> return (Right Nothing)
140 Just j -> Right . Just <$> f jid' j
145 -> m (Either JobError a)
147 handleIDError toE act = act >>= \r -> case r of
148 Left err -> throwError (toE err)
152 :: (Ord t, MonadJob m t w a)
153 => Bool -- is it queued (and we have to remove jid from queue)
157 removeJob queued t jid = do
160 liftIO . atomically $ do
166 -- Tracking jobs status
169 -- | A monad to query for the status of a particular job /and/ submit updates for in-progress jobs.
170 class MonadJob m (JobType m) (Seq (JobEventType m)) (JobOutputType m) => MonadJobStatus m where
171 type JobType m :: Type
172 type JobOutputType m :: Type
173 type JobEventType m :: Type
174 type JobErrorType m :: Type