pgqueuer-hs-0.0.1.0: src/PGQueuer.hs
{- |
Module : PGQueuer
Description : Main interface for the PGQueuer job queue ecosystem.
Copyright : (c) Tushar Adhatrao, 2026
License : MIT
Maintainer : tusharadhatrao@gmail.com
Stability : experimental
This module provides the high-level API for interacting with PGQueuer.
It re-exports essential types and settings required to configure,
manage, and interact with the database-backed queue.
-}
module PGQueuer (
-- * Queue Manager
QueueManager (..),
createQueueManager,
withQueueManager,
-- * Entrypoint Management
registerEntrypoint,
-- * Job Operations
workerLoop,
enqueue,
enqueueMultiple,
dequeue,
markJobAsCancelled,
requeueJobs,
retryJob,
updateHeartbeat,
logJobs,
-- * Queue Statistics and Management
getQueueSize,
clearQueue,
listFailedJobs,
listJobStatusById,
-- * Schema management
verifyStructure,
installSchema,
uninstallSchema,
-- * Re-exported modules
module PGQueuer.Types,
module PGQueuer.Settings,
) where
import Control.Concurrent (threadDelay)
import Control.Exception (bracket)
import Control.Monad (forever)
import Data.Aeson (Value)
import Data.ByteString (ByteString)
import qualified Data.ByteString.Lazy as BL
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Text (Text)
import Data.Time (NominalDiffTime)
import Data.UUID (UUID)
import Database.PostgreSQL.Simple (Connection, close, connectPostgreSQL)
import qualified PGQueuer.Query as Q
import PGQueuer.Schema (install, uninstall, verifyStructure_)
import PGQueuer.Settings
import PGQueuer.Types
-- | Core state context for managing Queue.
data QueueManager = QueueManager
{ qmConnection :: Connection
-- ^ Database connection
, qmSettings :: DBSettings
-- ^ Database settings
, qmEntrypoints :: Map Text EntrypointHandler
-- ^ Registered entrypoint handlers
, qmQueueManagerId :: UUID
-- ^ Unique identifier for this QueueManager instance
}
-- | Entrypoint handler type
type EntrypointHandler = Job -> IO ()
-- | Create a new QueueManager from a connection
createQueueManager ::
-- | Database connection
Connection ->
-- | Database settings
DBSettings ->
-- | Unique identifier for this QueueManager instance
UUID ->
IO QueueManager
createQueueManager conn settings queueMgrId = do
return $
QueueManager
{ qmConnection = conn
, qmSettings = settings
, qmEntrypoints = Map.empty
, qmQueueManagerId = queueMgrId
}
-- | Register an entrypoint handler
registerEntrypoint ::
-- | QueueManager instance
QueueManager ->
-- | Entrypoint to register
Entrypoint ->
-- | Handler function for the entrypoint
EntrypointHandler ->
IO QueueManager
registerEntrypoint qm (Entrypoint ep) handler = do
return
qm
{ qmEntrypoints = Map.insert ep handler (qmEntrypoints qm)
}
-- | Continuously dequeue jobs and dispatch them to registered handlers.
workerLoop :: QueueManager -> [EntrypointExecutionParameter] -> IO ()
workerLoop qm params = forever $ do
jobs <- dequeue qm defaultBatchSize params Nothing defaultHeartbeatTimeout
if null jobs
then threadDelay 1000000
else mapM_ (dispatchJob qm) jobs
dispatchJob :: QueueManager -> Job -> IO ()
dispatchJob qm job =
case Map.lookup entrypointName (qmEntrypoints qm) of
Nothing -> fail $ "No handler registered for entrypoint: " ++ show entrypointName
Just handler -> handler job
where
Entrypoint entrypointName = jobEntrypoint job
-- ============================================================================
-- Queue operations (wrappers around Query module)
-- ============================================================================
-- | Enqueue a single job
enqueue ::
-- | QueueManager instance
QueueManager ->
-- | Entrypoint for the job
Entrypoint ->
-- | Optional payload for the job
Maybe BL.ByteString ->
-- | Priority of the job
Int ->
-- | Optional delay before the job can be executed
Maybe NominalDiffTime ->
-- | Optional deduplication key for the job
Maybe Text ->
-- | Optional headers for the job
Maybe Value ->
IO [JobId]
enqueue qm =
Q.enqueueSingle
(qmConnection qm)
(qmSettings qm)
-- | Enqueue multiple jobs
enqueueMultiple ::
-- | QueueManager instance
QueueManager ->
-- | List of entrypoints for the jobs
[Entrypoint] ->
-- | List of optional payloads for the jobs
[Maybe BL.ByteString] ->
-- | List of priorities for the jobs
[Int] ->
-- | List of optional delays before the jobs can be executed
[Maybe NominalDiffTime] ->
-- | List of optional deduplication keys for the jobs
[Maybe Text] ->
-- | List of optional headers for the jobs
[Maybe Value] ->
IO [JobId]
enqueueMultiple qm =
Q.enqueueMultiple
(qmConnection qm)
(qmSettings qm)
-- | Dequeue jobs
dequeue ::
-- | QueueManager instance
QueueManager ->
-- | Batch size for dequeuing jobs
Int ->
-- | List of entrypoint execution parameters
[EntrypointExecutionParameter] ->
-- | Optional maximum number of jobs to dequeue
Maybe Int ->
-- | Heartbeat timeout in seconds
Int ->
IO [Job]
dequeue qm batchSize params =
Q.dequeue
(qmConnection qm)
(qmSettings qm)
batchSize
params
(qmQueueManagerId qm)
-- | Log job status changes
logJobs ::
-- | QueueManager instance
QueueManager ->
-- | List of job status changes with optional traceback
[(JobId, JobStatus, Maybe Value)] ->
IO ()
logJobs qm =
Q.logJobs (qmConnection qm) (qmSettings qm)
-- | Get queue size statistics
getQueueSize :: QueueManager -> IO [QueueStatistics]
getQueueSize qm = Q.queueSize (qmConnection qm) (qmSettings qm)
-- | Clear the queue
clearQueue ::
-- | QueueManager instance
QueueManager ->
-- | Optional list of entrypoints to clear; if Nothing, clears all
Maybe [Entrypoint] ->
IO ()
clearQueue qm = Q.clearQueue (qmConnection qm) (qmSettings qm)
-- | List failed jobs
listFailedJobs :: QueueManager -> Int -> IO [Job]
listFailedJobs qm = Q.listFailedJobs (qmConnection qm) (qmSettings qm)
-- | List Job status by Id
listJobStatusById :: QueueManager -> [JobId] -> IO [(JobId, JobStatus)]
listJobStatusById qm = Q.jobStatusById (qmConnection qm) (qmSettings qm)
-- | Mark jobs as cancelled
markJobAsCancelled :: QueueManager -> [JobId] -> IO ()
markJobAsCancelled qm = Q.markJobAsCancelled (qmConnection qm) (qmSettings qm)
-- | Update heartbeat
updateHeartbeat :: QueueManager -> [JobId] -> IO ()
updateHeartbeat qm = Q.updateHeartbeat (qmConnection qm) (qmSettings qm)
-- | Requeue jobs
requeueJobs :: QueueManager -> [JobId] -> IO ()
requeueJobs qm = Q.requeueJobs (qmConnection qm) (qmSettings qm)
-- | Retry a job
retryJob :: QueueManager -> Job -> NominalDiffTime -> Maybe Value -> IO ()
retryJob qm = Q.retryJob (qmConnection qm) (qmSettings qm)
-- ============================================================================
-- Schema management
-- ============================================================================
-- | Verify schema structure
verifyStructure :: QueueManager -> IO (Either String ())
verifyStructure qm = PGQueuer.Schema.verifyStructure_ (qmConnection qm) (qmSettings qm)
-- | Install schema
installSchema :: QueueManager -> IO ()
installSchema qm = install (qmConnection qm) (qmSettings qm)
-- | Uninstall schema
uninstallSchema :: QueueManager -> IO ()
uninstallSchema qm = uninstall (qmConnection qm) (qmSettings qm)
-- | Run a QueueManager within a bracket (for resource safety)
withQueueManager ::
-- | Connection string for PostgreSQL
ByteString ->
-- | Database settings
DBSettings ->
-- | Unique identifier for this QueueManager instance
UUID ->
-- | Action to run with the QueueManager
(QueueManager -> IO a) ->
IO a
withQueueManager connStr settings queueMgrId action = do
bracket
(connectPostgreSQL connStr >>= \conn -> createQueueManager conn settings queueMgrId)
(close . qmConnection)
action