packages feed

pdf-slave-server-0.1.1.0: src/Text/PDF/Slave/Server/DB.hs

module Text.PDF.Slave.Server.DB(
    Model
  , AcidState
  , createDB
  -- * Execution DB queries
  , HasAcidState(..)
  , runQuery
  , runUpdate
  -- * Persistent entities
  , RenderId(..)
  , RenderItem(..)
  , Notification(..)
  -- * Queries
  -- ** Render queue
  , AddRenderItem(..)
  , GetRenderQueueSize(..)
  , CheckNextRenderItem(..)
  , FetchRenderItem(..)
  -- ** Notification queue
  , AddNotification(..)
  , GetNotificationQueueSize(..)
  , CheckNextNotification(..)
  , FetchNotification(..)
  , GetNotificationNextTime(..)
  ) where

import Control.Concurrent.Thread.Delay
import Control.Monad (void, forever)
import Control.Monad.IO.Class
import Control.Monad.Trans.Control
import Data.Acid
import Data.Text (unpack)
import Data.Time

import Text.PDF.Slave.Server.Config
import Text.PDF.Slave.Server.DB.Model
import Text.PDF.Slave.Server.Util

import qualified Control.Immortal as Immortal

-- | Create connection pool for DB
createDB :: (MonadIO m, MonadBaseControl IO m)
  => DatabaseConfig -- ^ Connection configuration
  -> m (AcidState Model)
createDB DatabaseConfig{..} = do
  db <- liftIO $ openLocalStateFrom (unpack databasePath) initialModel
  whenJust databaseCheckpointTime $ \dt -> checkpointWorker dt db
  whenJust databaseArchiveTime $ \dt -> archiveWorker dt db
  return db

-- | Describes monads that has internal acid-state storage
class HasAcidState a m | m -> a where
  getAcidState :: m (AcidState a)

-- | Run acid-state query in monad with internal acid storage
runQuery :: (MonadIO m, HasAcidState (EventState event) m, QueryEvent event)
  => event -- ^ Event constructed from query with 'makeAcidic'
  -> m (EventResult event)
runQuery e = do
  db <- getAcidState
  liftIO $ query db e

-- | Run acid-state update query in monad with internal acid storage
runUpdate :: (MonadIO m, HasAcidState (EventState event) m, UpdateEvent event)
  => event -- ^ Event constructed from query with 'makeAcidic'
  -> m (EventResult event)
runUpdate e = do
  db <- getAcidState
  liftIO $ update db e

-- | Worker thread that creates DB checkpoints in given interval
checkpointWorker :: (MonadIO m, MonadBaseControl IO m)
  => NominalDiffTime -- ^ Time interval of creation of checkpoints
  -> AcidState Model -- ^ DB to make checkpoint from
  -> m ()
checkpointWorker dt db = liftIO . void . Immortal.createWithLabel "checkpointWorker" $
  const $ forever $ do
    delay . toMicroseconds $ dt
    createCheckpoint db

-- | Worker thread that creates archives from DB in given interval
archiveWorker :: (MonadIO m, MonadBaseControl IO m)
  => NominalDiffTime -- ^ Time interval of creation of checkpoints
  -> AcidState Model -- ^ DB to make checkpoint from
  -> m ()
archiveWorker dt db = liftIO . void . Immortal.createWithLabel "archiveWorker" $
  const $ forever $ do
    delay . toMicroseconds $ dt
    createArchive db