Skip to content
This repository was archived by the owner on Sep 1, 2022. It is now read-only.

Commit a488736

Browse files
committed
Acquisition timeout
1 parent d28225c commit a488736

File tree

4 files changed

+49
-16
lines changed

4 files changed

+49
-16
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Unreleased (PostgREST fork)
22

3+
Added support for timing out resource acquisition ([PR #](https://github.com/PostgREST/hasql-pool/pull/3)).
34
Added support for flushing the pool without destroying it ([PR #2](https://github.com/PostgREST/hasql-pool/pull/2)).
45

56
# 0.7.2

hasql-pool.cabal

+2
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,11 @@ test-suite test
6666
Arrows, BangPatterns, ConstraintKinds, DataKinds, DefaultSignatures, DeriveDataTypeable, DeriveFoldable, DeriveFunctor, DeriveGeneric, DeriveTraversable, EmptyDataDecls, FlexibleContexts, FlexibleInstances, FunctionalDependencies, GADTs, GeneralizedNewtypeDeriving, LambdaCase, LiberalTypeSynonyms, MagicHash, MultiParamTypeClasses, MultiWayIf, NoImplicitPrelude, NoMonomorphismRestriction, OverloadedStrings, PatternGuards, ParallelListComp, QuasiQuotes, RankNTypes, RecordWildCards, ScopedTypeVariables, StandaloneDeriving, TemplateHaskell, TupleSections, TypeFamilies, TypeOperators, UnboxedTuples
6767
default-language:
6868
Haskell2010
69+
ghc-options: -threaded
6970
build-depends:
7071
hasql,
7172
hasql-pool,
73+
async,
7274
hspec >=2.6 && <3,
7375
rerebase >=1.15 && <2,
7476
stm >=2.5 && <3

library/Hasql/Pool.hs

+22-7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import qualified Hasql.Session as Session
2222
data Pool = Pool
2323
{ -- | Connection settings.
2424
poolFetchConnectionSettings :: IO Connection.Settings,
25+
-- | Acquisition timeout, in microseconds.
26+
poolAcquisitionTimeout :: Maybe Int,
2527
-- | Avail connections.
2628
poolConnectionQueue :: TQueue Connection,
2729
-- | Remaining capacity.
@@ -40,9 +42,9 @@ data Pool = Pool
4042
--
4143
-- No connections actually get established by this function. It is delegated
4244
-- to 'use'.
43-
acquire :: Int -> Connection.Settings -> IO Pool
44-
acquire poolSize connectionSettings =
45-
acquireDynamically poolSize (pure connectionSettings)
45+
acquire :: Int -> Maybe Int -> Connection.Settings -> IO Pool
46+
acquire poolSize timeout connectionSettings =
47+
acquireDynamically poolSize timeout (pure connectionSettings)
4648

4749
-- | Given the pool-size and connection settings constructor action
4850
-- create a connection-pool.
@@ -52,9 +54,9 @@ acquire poolSize connectionSettings =
5254
--
5355
-- In difference to 'acquire' new settings get fetched each time a connection
5456
-- is created. This may be useful for some security models.
55-
acquireDynamically :: Int -> IO Connection.Settings -> IO Pool
56-
acquireDynamically poolSize fetchConnectionSettings = do
57-
Pool fetchConnectionSettings
57+
acquireDynamically :: Int -> Maybe Int -> IO Connection.Settings -> IO Pool
58+
acquireDynamically poolSize timeout fetchConnectionSettings = do
59+
Pool fetchConnectionSettings timeout
5860
<$> newTQueueIO
5961
<*> newTVarIO poolSize
6062
<*> (newTVarIO =<< newTVarIO True)
@@ -95,7 +97,13 @@ flush Pool {..} =
9597
-- and a slot gets freed up for a new connection to be established the next
9698
-- time one is needed. The error still gets returned from this function.
9799
use :: Pool -> Session.Session a -> IO (Either UsageError a)
98-
use Pool {..} sess =
100+
use Pool {..} sess = do
101+
timeout <- case poolAcquisitionTimeout of
102+
Just delta -> do
103+
delay <- registerDelay delta
104+
return $ readTVar delay
105+
Nothing ->
106+
return $ return False
99107
join . atomically $ do
100108
aliveVar <- readTVar poolAlive
101109
alive <- readTVar aliveVar
@@ -109,6 +117,11 @@ use Pool {..} sess =
109117
then do
110118
writeTVar poolCapacity $! pred capVal
111119
return $ onNewConn aliveVar
120+
else retry,
121+
do
122+
timedOut <- timeout
123+
if timedOut
124+
then return . return . Left $ AcquisitionTimeout
112125
else retry
113126
]
114127
else return . return . Left $ PoolIsReleasedUsageError
@@ -152,6 +165,8 @@ data UsageError
152165
SessionUsageError Session.QueryError
153166
| -- | Attempt to use a pool, which has already been called 'release' upon.
154167
PoolIsReleasedUsageError
168+
| -- | Timeout acquiring a connection.
169+
AcquisitionTimeout
155170
deriving (Show, Eq)
156171

157172
instance Exception UsageError

test/Main.hs

+24-9
Original file line numberDiff line numberDiff line change
@@ -10,70 +10,85 @@ import Test.Hspec
1010
import Prelude
1111
import qualified System.Environment
1212
import qualified Data.ByteString.Char8 as B8
13+
import Control.Concurrent.Async (race)
1314

1415
main = do
1516
connectionSettings <- getConnectionSettings
1617
hspec $ describe "" $ do
1718
it "Releases a spot in the pool when there is a query error" $ do
18-
pool <- acquire 1 connectionSettings
19+
pool <- acquire 1 Nothing connectionSettings
1920
use pool badQuerySession `shouldNotReturn` (Right ())
2021
use pool selectOneSession `shouldReturn` (Right 1)
2122
it "Simulation of connection error works" $ do
22-
pool <- acquire 3 connectionSettings
23+
pool <- acquire 3 Nothing connectionSettings
2324
res <- use pool $ closeConnSession >> selectOneSession
2425
shouldSatisfy res $ \case
2526
Left (SessionUsageError (Session.QueryError _ _ (Session.ClientError _))) -> True
2627
_ -> False
2728
it "Connection errors cause eviction of connection" $ do
28-
pool <- acquire 3 connectionSettings
29+
pool <- acquire 3 Nothing connectionSettings
2930
res <- use pool $ closeConnSession >> selectOneSession
3031
res <- use pool $ closeConnSession >> selectOneSession
3132
res <- use pool $ closeConnSession >> selectOneSession
3233
res <- use pool $ selectOneSession
3334
shouldSatisfy res $ isRight
3435
it "Connection gets returned to the pool after normal use" $ do
35-
pool <- acquire 3 connectionSettings
36+
pool <- acquire 3 Nothing connectionSettings
3637
res <- use pool $ selectOneSession
3738
res <- use pool $ selectOneSession
3839
res <- use pool $ selectOneSession
3940
res <- use pool $ selectOneSession
4041
res <- use pool $ selectOneSession
4142
shouldSatisfy res $ isRight
4243
it "Connection gets returned to the pool after non-connection error" $ do
43-
pool <- acquire 3 connectionSettings
44+
pool <- acquire 3 Nothing connectionSettings
4445
res <- use pool $ badQuerySession
4546
res <- use pool $ badQuerySession
4647
res <- use pool $ badQuerySession
4748
res <- use pool $ badQuerySession
4849
res <- use pool $ selectOneSession
4950
shouldSatisfy res $ isRight
5051
it "Getting and setting session variables works" $ do
51-
pool <- acquire 1 connectionSettings
52+
pool <- acquire 1 Nothing connectionSettings
5253
res <- use pool $ getSettingSession "testing.foo"
5354
res `shouldBe` Right Nothing
5455
res <- use pool $ do
5556
setSettingSession "testing.foo" "hello world"
5657
getSettingSession "testing.foo"
5758
res `shouldBe` Right (Just "hello world")
5859
it "Session variables stay set when a connection gets reused" $ do
59-
pool <- acquire 1 connectionSettings
60+
pool <- acquire 1 Nothing connectionSettings
6061
res <- use pool $ setSettingSession "testing.foo" "hello world"
6162
res `shouldBe` Right ()
6263
res2 <- use pool $ getSettingSession "testing.foo"
6364
res2 `shouldBe` Right (Just "hello world")
6465
it "Flushing the pool resets session variables" $ do
65-
pool <- acquire 1 connectionSettings
66+
pool <- acquire 1 Nothing connectionSettings
6667
res <- use pool $ setSettingSession "testing.foo" "hello world"
6768
res `shouldBe` Right ()
6869
flush pool
6970
res <- use pool $ getSettingSession "testing.foo"
7071
res `shouldBe` Right Nothing
7172
it "Flushing a released pool leaves it dead" $ do
72-
pool <- acquire 1 connectionSettings
73+
pool <- acquire 1 Nothing connectionSettings
7374
release pool
7475
flush pool
7576
res <- use pool $ selectOneSession
7677
res `shouldBe` Left PoolIsReleasedUsageError
78+
it "Times out connection acquisition" $ do
79+
pool <- acquire 1 (Just 1000) connectionSettings -- 1ms timeout
80+
sleeping <- newEmptyMVar
81+
t0 <- getCurrentTime
82+
res <- race
83+
(use pool $ liftIO $ do
84+
putMVar sleeping ()
85+
threadDelay 1000000) -- 1s
86+
(do
87+
takeMVar sleeping
88+
use pool $ selectOneSession)
89+
t1 <- getCurrentTime
90+
res `shouldBe` Right (Left AcquisitionTimeout)
91+
diffUTCTime t1 t0 `shouldSatisfy` (< 0.5) -- 0.5s
7792

7893
getConnectionSettings :: IO Connection.Settings
7994
getConnectionSettings = B8.unwords . catMaybes <$> sequence

0 commit comments

Comments
 (0)