packages feed

yesod-job-queue 0.1.0.2 → 0.1.0.3

raw patch · 6 files changed

+131/−119 lines, 6 filesPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

API changes (from Hackage documentation)

- Yesod.JobQueue: allJobTypes :: YesodJobQueue master => master -> [JobType master]
- Yesod.JobQueue: listQueue :: YesodJobQueue master => master -> IO (Either String [JobQueueItem])
- Yesod.JobQueue: readJobType :: YesodJobQueue master => master -> String -> Maybe (JobType master)
- Yesod.JobQueue: showJobType :: YesodJobQueue master => JobType master -> String
- Yesod.JobQueue.APIData: JobQueueClassInfo :: Text -> [Text] -> JobQueueClassInfo
- Yesod.JobQueue.APIData: PostJobQueueRequest :: String -> PostJobQueueRequest
- Yesod.JobQueue.APIData: [_jobQueueClassInfoClassName] :: JobQueueClassInfo -> Text
- Yesod.JobQueue.APIData: [_jobQueueClassInfoValues] :: JobQueueClassInfo -> [Text]
- Yesod.JobQueue.APIData: [_postJobQueueRequestJob] :: PostJobQueueRequest -> String
- Yesod.JobQueue.APIData: class HasClassName s a | s -> a
- Yesod.JobQueue.APIData: class HasJob s a | s -> a
- Yesod.JobQueue.APIData: class HasValues s a | s -> a
- Yesod.JobQueue.APIData: className :: HasClassName s a => Lens' s a
- Yesod.JobQueue.APIData: data JobQueueClassInfo
- Yesod.JobQueue.APIData: data PostJobQueueRequest
- Yesod.JobQueue.APIData: instance Data.Aeson.Types.Class.FromJSON Yesod.JobQueue.APIData.JobQueueClassInfo
- Yesod.JobQueue.APIData: instance Data.Aeson.Types.Class.FromJSON Yesod.JobQueue.APIData.PostJobQueueRequest
- Yesod.JobQueue.APIData: instance Data.Aeson.Types.Class.ToJSON Yesod.JobQueue.APIData.JobQueueClassInfo
- Yesod.JobQueue.APIData: instance Data.Aeson.Types.Class.ToJSON Yesod.JobQueue.APIData.PostJobQueueRequest
- Yesod.JobQueue.APIData: instance Yesod.JobQueue.APIData.HasClassName Yesod.JobQueue.APIData.JobQueueClassInfo Data.Text.Internal.Text
- Yesod.JobQueue.APIData: instance Yesod.JobQueue.APIData.HasJob Yesod.JobQueue.APIData.PostJobQueueRequest GHC.Base.String
- Yesod.JobQueue.APIData: instance Yesod.JobQueue.APIData.HasValues Yesod.JobQueue.APIData.JobQueueClassInfo [Data.Text.Internal.Text]
- Yesod.JobQueue.APIData: job :: HasJob s a => Lens' s a
- Yesod.JobQueue.APIData: values :: HasValues s a => Lens' s a
+ Yesod.JobQueue.Types: JobQueueClassInfo :: Text -> [Text] -> JobQueueClassInfo
+ Yesod.JobQueue.Types: PostJobQueueRequest :: String -> PostJobQueueRequest
+ Yesod.JobQueue.Types: [_jobQueueClassInfoClassName] :: JobQueueClassInfo -> Text
+ Yesod.JobQueue.Types: [_jobQueueClassInfoValues] :: JobQueueClassInfo -> [Text]
+ Yesod.JobQueue.Types: [_postJobQueueRequestJob] :: PostJobQueueRequest -> String
+ Yesod.JobQueue.Types: class HasClassName s a | s -> a
+ Yesod.JobQueue.Types: class HasJob s a | s -> a
+ Yesod.JobQueue.Types: class HasValues s a | s -> a
+ Yesod.JobQueue.Types: className :: HasClassName s a => Lens' s a
+ Yesod.JobQueue.Types: data JobQueueClassInfo
+ Yesod.JobQueue.Types: data PostJobQueueRequest
+ Yesod.JobQueue.Types: instance Data.Aeson.Types.Class.FromJSON Yesod.JobQueue.Types.JobQueueClassInfo
+ Yesod.JobQueue.Types: instance Data.Aeson.Types.Class.FromJSON Yesod.JobQueue.Types.PostJobQueueRequest
+ Yesod.JobQueue.Types: instance Data.Aeson.Types.Class.ToJSON Yesod.JobQueue.Types.JobQueueClassInfo
+ Yesod.JobQueue.Types: instance Data.Aeson.Types.Class.ToJSON Yesod.JobQueue.Types.PostJobQueueRequest
+ Yesod.JobQueue.Types: instance Yesod.JobQueue.Types.HasClassName Yesod.JobQueue.Types.JobQueueClassInfo Data.Text.Internal.Text
+ Yesod.JobQueue.Types: instance Yesod.JobQueue.Types.HasJob Yesod.JobQueue.Types.PostJobQueueRequest GHC.Base.String
+ Yesod.JobQueue.Types: instance Yesod.JobQueue.Types.HasValues Yesod.JobQueue.Types.JobQueueClassInfo [Data.Text.Internal.Text]
+ Yesod.JobQueue.Types: job :: HasJob s a => Lens' s a
+ Yesod.JobQueue.Types: values :: HasValues s a => Lens' s a
- Yesod.JobQueue: class (Yesod master, Read (JobType master), Show (JobType master), Enum (JobType master), Bounded (JobType master), JobInfo (JobType master)) => YesodJobQueue master where type family JobType master queueKey _ = "yesod-job-queue" readJobType _ = readMay showJobType = show allJobTypes _ = [minBound .. ] startDequeue m = do { liftIO $ forkIO $ do { conn <- connect defaultConnectInfo; runRedis conn $ do { forever $ do { emr <- blpop [queueKey m] 600; liftIO $ case emr of { Left e -> putStrLn "[dequeue] error in at connection redis" Right Nothing -> putStrLn "[dequeue] timeout retry" Right (Just (k, v)) -> do { let item = readMay $ unpack v :: Maybe JobQueueItem; case readJobType m =<< queueJobType <$> item of { Just jt -> do { jid <- nextRandom; time <- getCurrentTime; let job = RunningJob (show jt) 1 jid time; atomically $ modifyTVar (getJobState m) (job :); putStrLn . pack $ "dequeued: " ++ (show jt); runReaderT (runJob m jt) m; atomically $ modifyTVar (getJobState m) (delete job) } Nothing -> putStrLn "[dequeue] unknown JobType" } } }; return () } } }; return () } jobAPIBaseUrl _ = "/job" jobManagerJSUrl m = (jobAPIBaseUrl m) ++ "/manager/app.js" enqueue m jt = do { time <- getCurrentTime; let item = JobQueueItem (show jt) time; conn <- liftIO $ connect defaultConnectInfo; runRedis conn $ do { rpush (queueKey m) [pack $ show item] }; return () } listQueue m = do { conn <- connect defaultConnectInfo; exs <- runRedis conn $ do { lrange (queueKey m) 0 (- 1) }; case exs of { Right xs -> return $ Right $ catMaybes $ map (readMay . unpack) xs Left r -> return $ Left $ show r } } getClassInformation _ = []
+ Yesod.JobQueue: class (Yesod master, Read (JobType master), Show (JobType master), Enum (JobType master), Bounded (JobType master), JobInfo (JobType master)) => YesodJobQueue master where type family JobType master queueKey _ = "yesod-job-queue" jobAPIBaseUrl _ = "/job" jobManagerJSUrl m = (jobAPIBaseUrl m) ++ "/manager/app.js" getClassInformation _ = []

Files

Yesod/JobQueue.hs view
@@ -1,18 +1,25 @@ {-# LANGUAGE ImpredicativeTypes, UndecidableInstances #-}+-- |+--+-- Background jobs library for Yesod.+-- Use example is in README.md.+-- module Yesod.JobQueue (     YesodJobQueue (..)     , JobQueue-    , getJobQueue-    , JobState     , JobInfo (..)-    , newJobState) where+    , startDequeue+    , enqueue+    , JobState+    , newJobState+    , getJobQueue+    ) where  import qualified Prelude as P  import Yesod.JobQueue.Routes-import Yesod.JobQueue.APIData+import Yesod.JobQueue.Types --- import Data.Maybe import qualified Data.List as L import ClassyPrelude.Yesod import Control.Concurrent@@ -30,9 +37,8 @@  import Data.FileEmbed (embedFile) --- persist config for job env--- type JobDBConf c = (PersistConfig c) => (c, PersistConfigPool c) +-- | description for JobType class JobInfo j where     describe :: j -> String     describe _ = ""@@ -67,72 +73,28 @@ } deriving (Show, Read) $(deriveToJSON defaultOptions ''JobQueueItem) +-- | Backend jobs for Yesod class (Yesod master, Read (JobType master), Show (JobType master)       , Enum (JobType master), Bounded (JobType master)       , JobInfo (JobType master))       => YesodJobQueue master where     -    -- persistent config for job-    -- jobDBConfig :: master -> JobDBConf config-    -    -- Custom Job Type+    -- | Custom Job Type     type JobType master     -    -- Execute Job Handler+    -- | Job Handler     runJob :: (MonadBaseControl IO m, MonadIO m)               => master -> JobType master -> ReaderT master m ()     -    -- queue key name for redis+    -- | queue key name for redis     queueKey :: master -> ByteString     queueKey _ = "yesod-job-queue"-    -    -- serialize JobType-    readJobType :: master -> String -> Maybe (JobType master)-    readJobType _ = readMay-    -    -- desserialize JobType-    showJobType :: JobType master -> String-    showJobType = show-    -    -- get all job type list-    allJobTypes :: master -> [JobType master]-    allJobTypes _ = [minBound..]-    -    -- runDB with config for job+        +    -- | runDB for job     runDBJob :: (MonadBaseControl IO m, MonadIO m)                 => ReaderT (YesodPersistBackend master) (ReaderT master m) a                 -> ReaderT master m a -    -- start thread of dequeue-ing job-    startDequeue :: (MonadBaseControl IO m, MonadIO m) => master -> m ()-    startDequeue m = do-        liftIO $ forkIO $ do-            conn <- R.connect R.defaultConnectInfo-            R.runRedis conn $ do-                forever $ do-                    emr <- R.blpop [queueKey m] 600-                    liftIO $ case emr of-                        Left e -> putStrLn "[dequeue] error in at connection redis"-                        Right Nothing -> putStrLn "[dequeue] timeout retry"-                        Right (Just (k, v)) -> do-                            let item = readMay $ BSC.unpack v :: Maybe JobQueueItem-                            case readJobType m =<< queueJobType <$> item of-                             Just jt -> do-                                 jid <- U.nextRandom-                                 time <- getCurrentTime-                                 let job = RunningJob (show jt) 1 jid time-                                 STM.atomically-                                     $ STM.modifyTVar (getJobState m)-                                     (job:)-                                 putStrLn . pack $  "dequeued: " ++ (show jt)-                                 runReaderT (runJob m jt) m-                                 STM.atomically-                                     $ STM.modifyTVar (getJobState m)-                                     (L.delete job)-                             Nothing -> putStrLn "[dequeue] unknown JobType"-                    return ()-        return ()-     -- | get job state     getJobState :: master -> JobState @@ -144,29 +106,69 @@     jobManagerJSUrl :: master -> String     jobManagerJSUrl m = (jobAPIBaseUrl m) ++ "/manager/app.js" -    enqueue :: master -> JobType master -> IO ()-    enqueue m jt = do-        time <- getCurrentTime-        let item = JobQueueItem (show jt) time-        conn <- liftIO $ R.connect R.defaultConnectInfo-        R.runRedis conn $ do-            R.rpush (queueKey m) [BSC.pack $ show item]-        return ()--    listQueue :: master -> IO (Either String [JobQueueItem])-    listQueue m = do-        conn <- R.connect R.defaultConnectInfo-        exs <- R.runRedis conn $ do-            R.lrange (queueKey m) 0 (-1)-        case exs of-         Right xs ->-             return $ Right $ catMaybes-             $ map (readMay . BSC.unpack) xs-         Left r -> return $ Left $ show r-     -- | get information of all type classes related job-queue     getClassInformation :: master -> [JobQueueClassInfo]     getClassInformation _ = []++-- | start dequeue-ing job in new thread+startDequeue :: (YesodJobQueue master, MonadBaseControl IO m, MonadIO m) => master -> m ()+startDequeue m = do+    liftIO $ forkIO $ do+        conn <- R.connect R.defaultConnectInfo+        R.runRedis conn $ do+            forever $ do+                emr <- R.blpop [queueKey m] 600+                liftIO $ case emr of+                    Left e -> putStrLn "[dequeue] error in at connection redis"+                    Right Nothing -> putStrLn "[dequeue] timeout retry"+                    Right (Just (k, v)) -> do+                        let item = readMay $ BSC.unpack v :: Maybe JobQueueItem+                        case readJobType m =<< queueJobType <$> item of+                         Just jt -> do+                             jid <- U.nextRandom+                             time <- getCurrentTime+                             let job = RunningJob (show jt) 1 jid time+                             STM.atomically+                                 $ STM.modifyTVar (getJobState m)+                                 (job:)+                             putStrLn . pack $  "dequeued: " ++ (show jt)+                             runReaderT (runJob m jt) m+                             STM.atomically+                                 $ STM.modifyTVar (getJobState m)+                                 (L.delete job)+                         Nothing -> putStrLn "[dequeue] unknown JobType"+                return ()+    return ()++-- | Add job to end of the queue+enqueue :: YesodJobQueue master => master -> JobType master -> IO ()+enqueue m jt = do+    time <- getCurrentTime+    let item = JobQueueItem (show jt) time+    conn <- liftIO $ R.connect R.defaultConnectInfo+    R.runRedis conn $ do+        R.rpush (queueKey m) [BSC.pack $ show item]+    return ()++-- | Get all job in the queue+listQueue :: YesodJobQueue master => master -> IO (Either String [JobQueueItem])+listQueue m = do+    conn <- R.connect R.defaultConnectInfo+    exs <- R.runRedis conn $ do+        R.lrange (queueKey m) 0 (-1)+    case exs of+     Right xs ->+         return $ Right $ catMaybes+         $ map (readMay . BSC.unpack) xs+     Left r -> return $ Left $ show r++-- | read JobType from String+readJobType :: YesodJobQueue master => master -> String -> Maybe (JobType master)+readJobType _ = readMay++-- | get all job type list+allJobTypes :: (YesodJobQueue master) => master -> [JobType master]+allJobTypes _ = [minBound..]  -- | Handler for job manager api routes type JobHandler master a =
− Yesod/JobQueue/APIData.hs
@@ -1,22 +0,0 @@-module Yesod.JobQueue.APIData where--import ClassyPrelude.Yesod-import Data.Aeson.APIFieldJsonTH-import Control.Lens--data PostJobQueueRequest = PostJobQueueRequest {-    _postJobQueueRequestJob :: String-}--makeFields ''PostJobQueueRequest-deriveApiFieldJSON ''PostJobQueueRequest----data JobQueueClassInfo = JobQueueClassInfo {-    _jobQueueClassInfoClassName :: Text-    , _jobQueueClassInfoValues :: [Text]-    }--makeFields ''JobQueueClassInfo-deriveApiFieldJSON ''JobQueueClassInfo
Yesod/JobQueue/Scheduler.hs view
@@ -1,7 +1,8 @@+-- | Cron Job for Yesod module Yesod.JobQueue.Scheduler where  import Yesod.JobQueue-import Yesod.JobQueue.APIData+import Yesod.JobQueue.Types import System.Cron.Schedule  import qualified Prelude as P@@ -9,6 +10,7 @@  import qualified Data.Text as T (pack) +-- | Cron Scheduler for YesodJobQueue class (YesodJobQueue master) => YesodJobQueueScheduler master where     -- | job schedules     getJobSchedules :: master -> [(String, JobType master)]@@ -20,6 +22,7 @@         tids <- liftIO $ execSchedule $ mapM_ add $ getJobSchedules master         print tids +-- | Need by 'getClassInformation' schedulerInfo :: YesodJobQueueScheduler master => master ->  JobQueueClassInfo schedulerInfo m = JobQueueClassInfo "Scheduler" $  map (T.pack . showSchedule) $ getJobSchedules m-  where showSchedule (s, jt) = s ++ " " ++ (show jt)+  where showSchedule (s, jt) = s ++ " | " ++ (show jt)
+ Yesod/JobQueue/Types.hs view
@@ -0,0 +1,22 @@+module Yesod.JobQueue.Types where++import ClassyPrelude.Yesod+import Data.Aeson.APIFieldJsonTH+import Control.Lens++data PostJobQueueRequest = PostJobQueueRequest {+    _postJobQueueRequestJob :: String+}++makeFields ''PostJobQueueRequest+deriveApiFieldJSON ''PostJobQueueRequest++++data JobQueueClassInfo = JobQueueClassInfo {+    _jobQueueClassInfoClassName :: Text+    , _jobQueueClassInfoValues :: [Text]+    }++makeFields ''JobQueueClassInfo+deriveApiFieldJSON ''JobQueueClassInfo
example/Main.hs view
@@ -1,22 +1,14 @@-import qualified Prelude as P+import qualified Prelude as P () import ClassyPrelude.Yesod-import Yesod+-- import Yesod import Yesod.JobQueue import Yesod.JobQueue.Scheduler import Control.Concurrent import Database.Persist.Sqlite-import Control.Monad.Trans.Resource (runResourceT)+--import Control.Monad.Trans.Resource (runResourceT) import Control.Monad.Logger (runStderrLoggingT)  --- Handlers--- getHomeR :: Handler Html-getHomeR = defaultLayout $ do-    setTitle "JobQueue sample"-    [whamlet|-        <h1>Hello-    |]- -- Yesod Persist settings (Nothing special here) share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase| Person@@ -56,12 +48,12 @@     runDBJob action = do         app <- ask         runSqlPool action $ appConnPool app-    runJob app AggregationUser = do+    runJob _ AggregationUser = do         us <- runDBJob $ selectList ([] :: [Filter Person]) []         liftIO $ threadDelay $ 10 * 1000 * 1000         print us         putStrLn "complate job!"-    runJob app PushNotification = do+    runJob _ PushNotification = do         putStrLn "send norification!"     getClassInformation app = [schedulerInfo app]     -- jobManagerJSUrl _ = "http://localhost:3001/dist/app.bundle.js" -- use for development with "npm run bs"@@ -69,6 +61,14 @@ instance YesodJobQueueScheduler App  where     getJobSchedules _ = [("* * * * *", AggregationUser)                          , ("* * * * *", PushNotification)]++-- Handlers+getHomeR :: HandlerT App IO Html+getHomeR = defaultLayout $ do+    setTitle "JobQueue sample"+    [whamlet|+        <h1>Hello+    |]  -- Main main :: IO ()
yesod-job-queue.cabal view
@@ -1,7 +1,15 @@ name:                yesod-job-queue-version:             0.1.0.2+version:             0.1.0.3 synopsis:            Background jobs library for Yesod.-description:         Please see README.md+description:+  Background jobs library for Yesod+  .+  * It contains management API and web interface. +  * Queue backend is Redis.+  * Support cron scheduler+  .+  Usage and screen shot are available in README.md+  homepage:            https://github.com/nakaji-dayo/yesod-job-queue#readme license:             BSD3 license-file:        LICENSE@@ -18,10 +26,9 @@   default: False                                library-  -- hs-source-dirs:      src   exposed-modules:     Yesod.JobQueue                      , Yesod.JobQueue.Scheduler-                     , Yesod.JobQueue.APIData+                     , Yesod.JobQueue.Types                      , Yesod.JobQueue.Routes   default-extensions:  TemplateHaskell                      , MultiParamTypeClasses@@ -79,7 +86,7 @@                 , classy-prelude-yesod    ghc-options:    -threaded -O2 -rtsopts -with-rtsopts=-N-+  default-language:    Haskell2010    test-suite yesod-job-queue-test   type:                exitcode-stdio-1.0