Skip to content

Commit 53035ee

Browse files
committed
make a start to stetting up hasql with db pools
1 parent 8a3433f commit 53035ee

File tree

10 files changed

+98
-54
lines changed

10 files changed

+98
-54
lines changed

cabal.project

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ repository cardano-haskell-packages
1010
d4a35cd3121aa00d18544bb0ac01c3e1691d618f462c46129271bccf39f7e8ee
1111

1212
index-state:
13-
, hackage.haskell.org 2024-10-10T00:52:24Z
14-
, cardano-haskell-packages 2024-11-26T16:00:26Z
13+
, hackage.haskell.org 2025-02-05T12:01:20Z
14+
, cardano-haskell-packages 2025-02-04T11:56:25Z
1515

1616
packages:
1717
cardano-db

cardano-db-sync/cardano-db-sync.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,8 @@ library
189189
, extra
190190
, filepath
191191
, groups
192+
, hasql
193+
, hasql-pool
192194
, http-client
193195
, http-client-tls
194196
, http-types

cardano-db-sync/src/Cardano/DbSync.hs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -170,19 +170,20 @@ runSyncNode metricsSetters trce iomgr dbConnString ranMigrations runMigrationFnc
170170
logInfo trce $ "Using alonzo genesis file from: " <> (show . unGenesisFile $ dncAlonzoGenesisFile syncNodeConfigFromFile)
171171

172172
let useLedger = shouldUseLedger (sioLedger $ dncInsertOptions syncNodeConfigFromFile)
173-
174-
Db.runIohkLogging trce $
175-
withPostgresqlConn dbConnString $
176-
\backend -> liftIO $ do
173+
cPool <- createPool dbConnString
174+
bracket
175+
cPool
176+
Pool.release
177+
(\pool -> do
177178
runOrThrowIO $ runExceptT $ do
178179
genCfg <- readCardanoGenesisConfig syncNodeConfigFromFile
179-
isJsonbInSchema <- queryIsJsonbInSchema backend
180+
isJsonbInSchema <- queryIsJsonbInSchema pool
180181
logProtocolMagicId trce $ genesisProtocolMagicId genCfg
181182
syncEnv <-
182183
ExceptT $
183184
mkSyncEnvFromConfig
184185
trce
185-
backend
186+
pool
186187
dbConnString
187188
syncOptions
188189
genCfg
@@ -203,7 +204,7 @@ runSyncNode metricsSetters trce iomgr dbConnString ranMigrations runMigrationFnc
203204
liftIO $ runExtraMigrationsMaybe syncEnv
204205
unless useLedger $ liftIO $ do
205206
logInfo trce "Migrating to a no ledger schema"
206-
Db.noLedgerMigrations backend trce
207+
Db.noLedgerMigrations pool trce
207208
insertValidateGenesisDist syncEnv (dncNetworkName syncNodeConfigFromFile) genCfg (useShelleyInit syncNodeConfigFromFile)
208209

209210
-- communication channel between datalayer thread and chainsync-client thread

cardano-db-sync/src/Cardano/DbSync/Api.hs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ getCurrentTipBlockNo env = do
334334

335335
mkSyncEnv ::
336336
Trace IO Text ->
337-
SqlBackend ->
337+
Pool ->
338338
ConnectionString ->
339339
SyncOptions ->
340340
ProtocolInfo CardanoBlock ->
@@ -346,7 +346,7 @@ mkSyncEnv ::
346346
Bool ->
347347
RunMigration ->
348348
IO SyncEnv
349-
mkSyncEnv trce backend connectionString syncOptions protoInfo nw nwMagic systemStart syncNodeConfigFromFile syncNP ranMigrations runMigrationFnc = do
349+
mkSyncEnv trce dbPool connectionString syncOptions protoInfo nw nwMagic systemStart syncNodeConfigFromFile syncNP ranMigrations runMigrationFnc = do
350350
dbCNamesVar <- newTVarIO =<< dbConstraintNamesExists backend
351351
cache <-
352352
if soptCache syncOptions
@@ -394,7 +394,7 @@ mkSyncEnv trce backend connectionString syncOptions protoInfo nw nwMagic systemS
394394

395395
pure $
396396
SyncEnv
397-
{ envBackend = backend
397+
{ envPool = dbPool
398398
, envBootstrap = bootstrapVar
399399
, envCache = cache
400400
, envConnectionString = connectionString
@@ -421,7 +421,7 @@ mkSyncEnv trce backend connectionString syncOptions protoInfo nw nwMagic systemS
421421

422422
mkSyncEnvFromConfig ::
423423
Trace IO Text ->
424-
SqlBackend ->
424+
Pool ->
425425
ConnectionString ->
426426
SyncOptions ->
427427
GenesisConfig ->
@@ -432,7 +432,7 @@ mkSyncEnvFromConfig ::
432432
-- | run migration function
433433
RunMigration ->
434434
IO (Either SyncNodeError SyncEnv)
435-
mkSyncEnvFromConfig trce backend connectionString syncOptions genCfg syncNodeConfigFromFile syncNodeParams ranMigration runMigrationFnc =
435+
mkSyncEnvFromConfig trce dbPool connectionString syncOptions genCfg syncNodeConfigFromFile syncNodeParams ranMigration runMigrationFnc =
436436
case genCfg of
437437
GenesisCardano _ bCfg sCfg _ _
438438
| unProtocolMagicId (Byron.configProtocolMagicId bCfg) /= Shelley.sgNetworkMagic (scConfig sCfg) ->
@@ -459,7 +459,7 @@ mkSyncEnvFromConfig trce backend connectionString syncOptions genCfg syncNodeCon
459459
Right
460460
<$> mkSyncEnv
461461
trce
462-
backend
462+
dbPool
463463
connectionString
464464
syncOptions
465465
(fst $ mkProtocolInfoCardano genCfg [])

cardano-db-sync/src/Cardano/DbSync/Api/Types.hs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,11 @@ import Control.Concurrent.Class.MonadSTM.Strict.TBQueue (StrictTBQueue)
3333
import qualified Data.Strict.Maybe as Strict
3434
import Data.Time.Clock (UTCTime)
3535
import Database.Persist.Postgresql (ConnectionString)
36-
import Database.Persist.Sql (SqlBackend)
3736
import Ouroboros.Consensus.BlockchainTime.WallClock.Types (SystemStart (..))
3837
import Ouroboros.Network.Magic (NetworkMagic (..))
3938

4039
data SyncEnv = SyncEnv
41-
{ envBackend :: !SqlBackend
40+
{ envPool :: !!Pool.Pool
4241
, envCache :: !CacheStatus
4342
, envConnectionString :: !ConnectionString
4443
, envConsistentLevel :: !(StrictTVar IO ConsistentLevel)

cardano-db-sync/src/Cardano/DbSync/Util/Constraint.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ dbConstraintNamesExists :: MonadIO m => SqlBackend -> m ManualDbConstraints
4141
dbConstraintNamesExists sqlBackend = do
4242
runReaderT queryRewardAndEpochStakeConstraints sqlBackend
4343

44-
queryIsJsonbInSchema :: MonadIO m => SqlBackend -> m Bool
45-
queryIsJsonbInSchema sqlBackend = do
46-
runReaderT DB.queryJsonbInSchemaExists sqlBackend
44+
queryIsJsonbInSchema :: MonadIO m => Pool -> m Bool
45+
queryIsJsonbInSchema pool = do
46+
runReaderT DB.queryJsonbInSchemaExists pool
4747

4848
queryRewardAndEpochStakeConstraints ::
4949
MonadIO m =>

cardano-db/cardano-db.cabal

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ library
8989
, filepath
9090
, file-embed
9191
, hasql
92+
, hasql-pool
9293
, hasql-transaction
9394
, iohk-monitoring
9495
, lifted-base

cardano-db/src/Cardano/Db/Error.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ module Cardano.Db.Error (
1010
) where
1111

1212
import Cardano.BM.Trace (Trace, logError)
13-
import Cardano.Db.Schema.Core
13+
import Cardano.Db.Schema.Ids
1414
import Cardano.Prelude (throwIO)
1515
import Control.Exception (Exception)
16-
import qualified Data.ByteString.Base16 as Base16
1716
import Data.ByteString.Char8 (ByteString)
1817
import Data.Text (Text)
19-
import qualified Data.Text.Encoding as Text
2018
import Data.Word (Word16, Word64)
2119
import GHC.Generics (Generic)
20+
import qualified Data.ByteString.Base16 as Base16
21+
import qualified Data.Text.Encoding as Text
2222

2323
data LookupFail
2424
= DbLookupBlockHash !ByteString

cardano-db/src/Cardano/Db/PGConfig.hs

Lines changed: 55 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -21,48 +21,65 @@ import qualified Control.Exception as Exception
2121
import Data.ByteString.Char8 (ByteString)
2222
import qualified Data.ByteString.Char8 as BS
2323
import qualified Data.Text as Text
24-
import Database.Persist.Postgresql (ConnectionString)
2524
import System.Environment (lookupEnv, setEnv)
2625
import System.Posix.User (getEffectiveUserName)
26+
import qualified Hasql.Connection.Setting.Connection as HCS
27+
import qualified Hasql.Connection.Setting.Connection.Param as HCSP
28+
import qualified Hasql.Connection.Setting as HC
29+
import Cardano.Prelude (decodeUtf8)
30+
import Data.Word (Word16)
31+
import qualified Data.Text.Read as Text (decimal)
32+
import Control.Monad.Extra (unless)
33+
2734

2835
data PGPassSource
2936
= PGPassDefaultEnv
3037
| PGPassEnv String
3138
| PGPassCached PGConfig
3239
deriving (Show)
3340

34-
-- | PGConfig as specified by https://www.postgresql.org/docs/11/libpq-pgpass.html
35-
-- However, this module expects the config data to be on the first line.
41+
-- | Preconstructed connection string according to <https://www.postgresql.org/docs/17/libpq-connect.html#LIBPQ-CONNSTRING the PostgreSQL format>.
3642
data PGConfig = PGConfig
37-
{ pgcHost :: ByteString
38-
, pgcPort :: ByteString
39-
, pgcDbname :: ByteString
40-
, pgcUser :: ByteString
41-
, pgcPassword :: ByteString
43+
{ pgcHost :: Text.Text
44+
, pgcPort :: Text.Text
45+
, pgcDbname :: Text.Text
46+
, pgcUser :: Text.Text
47+
, pgcPassword :: Text.Text
4248
}
4349
deriving (Show)
4450

4551
newtype PGPassFile
4652
= PGPassFile FilePath
4753

48-
toConnectionString :: PGConfig -> ConnectionString
49-
toConnectionString pgc =
50-
BS.concat
51-
[ "host="
52-
, pgcHost pgc
53-
, " "
54-
, "port="
55-
, pgcPort pgc
56-
, " "
57-
, "user="
58-
, pgcUser pgc
59-
, " "
60-
, "dbname="
61-
, pgcDbname pgc
62-
, " "
63-
, "password="
64-
, pgcPassword pgc
65-
]
54+
-- | Convert PGConfig to Hasql connection settings, or return an error message.
55+
toConnectionString :: PGConfig -> Either String HC.Setting
56+
toConnectionString pgc = do
57+
-- Convert the port from Text to Word16
58+
portWord16 <- textToWord16 (pgcPort pgc)
59+
-- Build the connection settings
60+
pure $ HC.connection (HCS.params [host, port portWord16 , user, dbname, password])
61+
where
62+
host = HCSP.host (pgcHost pgc)
63+
port = HCSP.port
64+
user = HCSP.user (pgcUser pgc)
65+
dbname = HCSP.dbname (pgcDbname pgc)
66+
password = HCSP.password (pgcPassword pgc)
67+
68+
-- | Convert a Text port to Word16, or return an error message.
69+
textToWord16 :: Text.Text -> Either String Word16
70+
textToWord16 portText =
71+
case Text.decimal portText of
72+
Left err ->
73+
Left $ "Invalid port: '" <> Text.unpack portText <> "'. " <> err
74+
Right (portInt, remainder) -> do
75+
-- Check for leftover characters (e.g., "123abc" is invalid)
76+
unless (Text.null remainder) $
77+
Left $ "Invalid port: '" <> Text.unpack portText <> "'. Contains non-numeric characters."
78+
-- Check if the port is within the valid Word16 range (0-65535)
79+
unless (portInt >= (0 :: Integer) && portInt <= 65535) $
80+
Left $ "Invalid port: '" <> Text.unpack portText <> "'. Port must be between 0 and 65535."
81+
-- Convert to Word16
82+
Right (fromIntegral portInt)
6683

6784
readPGPassDefault :: IO (Either PGPassError PGConfig)
6885
readPGPassDefault = readPGPass PGPassDefaultEnv
@@ -94,24 +111,31 @@ readPGPassFile (PGPassFile fpath) = do
94111
extract bs =
95112
case BS.lines bs of
96113
(b : _) -> parsePGConfig b
97-
_ -> pure $ Left (FailedToParsePGPassConfig bs)
114+
_otherwise -> pure $ Left (FailedToParsePGPassConfig bs)
98115

99116
parsePGConfig :: ByteString -> IO (Either PGPassError PGConfig)
100117
parsePGConfig bs =
101118
case BS.split ':' bs of
102-
[h, pt, d, u, pwd] -> replaceUser (PGConfig h pt d u pwd)
103-
_ -> pure $ Left (FailedToParsePGPassConfig bs)
119+
[h, pt, d, u, pwd] ->
120+
replaceUser (PGConfig
121+
(decodeUtf8 h)
122+
(decodeUtf8 pt)
123+
(decodeUtf8 d)
124+
(decodeUtf8 u)
125+
(decodeUtf8 pwd)
126+
)
127+
_otherwise -> pure $ Left (FailedToParsePGPassConfig bs)
104128
where
105129
replaceUser :: PGConfig -> IO (Either PGPassError PGConfig)
106130
replaceUser pgc
107-
| pgcUser pgc /= "*" = pure $ Right pgc
131+
| pgcUser pgc /= Text.pack "*" = pure $ Right pgc
108132
| otherwise = do
109133
euser <- Exception.try getEffectiveUserName
110134
case euser of
111135
Left (err :: IOException) ->
112136
pure $ Left (UserFailed err)
113137
Right user ->
114-
pure $ Right (pgc {pgcUser = BS.pack user})
138+
pure $ Right (pgc {pgcUser = Text.pack user})
115139

116140
-- | Read 'PGPassFile' into 'PGConfig'.
117141
-- If it fails it will raise an error.

cardano-db/src/Cardano/Db/Run.hs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
{-# LANGUAGE OverloadedStrings #-}
55

66
module Cardano.Db.Run (
7+
createPool,
78
getBackendGhci,
89
ghciDebugQuery,
910
runDbHandleLogger,
@@ -76,6 +77,22 @@ import Database.PostgreSQL.Simple (connectPostgreSQL)
7677
import Language.Haskell.TH.Syntax (Loc)
7778
import System.IO (Handle, stdout)
7879
import System.Log.FastLogger (LogStr, fromLogStr)
80+
import qualified Hasql.Pool as HP
81+
import qualified Hasql.Pool.Config as HPC
82+
83+
-- | Create a connection pool.
84+
createPool :: PGConfig -> IO HP.Pool
85+
createPool pgc =
86+
case toConnectionString pgc of
87+
Left err -> error $ "createPool: " ++ err
88+
Right connStr ->
89+
HP.acquire $ HPC.settings
90+
[ HPC.size 10 -- number of connections
91+
, HPC.acquisitionTimeout 10 -- seconds
92+
, HPC.agingTimeout 1800 -- 30 minutes
93+
, HPC.idlenessTimeout 1800 -- 30 minutes
94+
, HPC.staticConnectionSettings [connStr]
95+
]
7996

8097
-- | Run a DB action logging via the provided Handle.
8198
runDbHandleLogger :: Handle -> PGPassSource -> ReaderT SqlBackend (LoggingT IO) a -> IO a

0 commit comments

Comments
 (0)