Small refactor around error responses and more tests (#1282)
* Fix #880, Clean and consolidate error responses * Fix #1285, Abort on fatal errors * Add / Detail tests
This commit is contained in:
committed by
Steve Chávez
parent
e2d917f7b9
commit
1cf54e6575
+42
-33
@@ -7,9 +7,9 @@ import PostgREST.App (postgrest)
|
||||
import PostgREST.Config (AppConfig (..), configPoolTimeout',
|
||||
prettyVersion, readOptions)
|
||||
import PostgREST.DbStructure (getDbStructure, getPgVersion)
|
||||
import PostgREST.Error (encodeError)
|
||||
import PostgREST.Error (errorPayload, checkIsFatal, PgError(PgError))
|
||||
import PostgREST.OpenAPI (isMalformedProxyUri)
|
||||
import PostgREST.Types (DbStructure, Schema, PgVersion(..), minimumPgVersion)
|
||||
import PostgREST.Types (DbStructure, Schema, PgVersion(..), minimumPgVersion, ConnectionStatus(..))
|
||||
import Protolude hiding (hPutStrLn, replace)
|
||||
|
||||
|
||||
@@ -28,7 +28,6 @@ import Data.Text.Encoding (decodeUtf8, encodeUtf8)
|
||||
import Data.Text.IO (hPutStrLn, readFile)
|
||||
import Data.Time.Clock (getCurrentTime)
|
||||
import qualified Hasql.Pool as P
|
||||
import qualified Hasql.Session as H
|
||||
import qualified Hasql.Transaction.Sessions as HT
|
||||
import Network.Wai.Handler.Warp (defaultSettings,
|
||||
runSettings, setHost,
|
||||
@@ -74,25 +73,24 @@ connectionWorker mainTid pool schema refDbStructure refIsWorkerOn = do
|
||||
work = do
|
||||
atomicWriteIORef refDbStructure Nothing
|
||||
putStrLn ("Attempting to connect to the database..." :: Text)
|
||||
connected <- connectingSucceeded pool
|
||||
when connected $ do
|
||||
result <- P.use pool $ do
|
||||
actualPgVersion <- getPgVersion
|
||||
unless (actualPgVersion >= minimumPgVersion) $ liftIO $ do
|
||||
hPutStrLn stderr
|
||||
("Cannot run in this PostgreSQL version, PostgREST needs at least "
|
||||
<> pgvName minimumPgVersion)
|
||||
killThread mainTid
|
||||
dbStructure <- HT.transaction HT.ReadCommitted HT.Read $ getDbStructure schema actualPgVersion
|
||||
liftIO $ atomicWriteIORef refDbStructure $ Just dbStructure
|
||||
case result of
|
||||
Left e -> do
|
||||
putStrLn ("Failed to query the database. Retrying." :: Text)
|
||||
hPutStrLn stderr (toS $ encodeError e)
|
||||
work
|
||||
Right _ -> do
|
||||
atomicWriteIORef refIsWorkerOn False
|
||||
putStrLn ("Connection successful" :: Text)
|
||||
connected <- connectionStatus pool
|
||||
case connected of
|
||||
FatalConnectionError reason -> hPutStrLn stderr reason
|
||||
>> killThread mainTid -- Fatal error when connecting
|
||||
NotConnected -> return () -- Unreachable
|
||||
Connected actualPgVersion -> do -- Procede with initialization
|
||||
result <- P.use pool $ do
|
||||
dbStructure <- HT.transaction HT.ReadCommitted HT.Read $ getDbStructure schema actualPgVersion
|
||||
liftIO $ atomicWriteIORef refDbStructure $ Just dbStructure
|
||||
case result of
|
||||
Left e -> do
|
||||
putStrLn ("Failed to query the database. Retrying." :: Text)
|
||||
hPutStrLn stderr . toS . errorPayload $ PgError False e
|
||||
work
|
||||
|
||||
Right _ -> do
|
||||
atomicWriteIORef refIsWorkerOn False
|
||||
putStrLn ("Connection successful" :: Text)
|
||||
|
||||
{-|
|
||||
Used by 'connectionWorker' to check if the provided db-uri lets
|
||||
@@ -103,26 +101,37 @@ connectionWorker mainTid pool schema refDbStructure refIsWorkerOn = do
|
||||
The connection tries are capped, but if the connection times out no error is
|
||||
thrown, just 'False' is returned.
|
||||
-}
|
||||
connectingSucceeded :: P.Pool -> IO Bool
|
||||
connectingSucceeded pool =
|
||||
connectionStatus :: P.Pool -> IO ConnectionStatus
|
||||
connectionStatus pool =
|
||||
retrying (capDelay 32000000 $ exponentialBackoff 1000000)
|
||||
shouldRetry
|
||||
(const $ P.release pool >> isConnectionSuccessful)
|
||||
(const $ P.release pool >> getConnectionStatus)
|
||||
where
|
||||
isConnectionSuccessful :: IO Bool
|
||||
isConnectionSuccessful = do
|
||||
testConn <- P.use pool $ H.sql "SELECT 1"
|
||||
case testConn of
|
||||
Left e -> hPutStrLn stderr (toS $ encodeError e) >> pure False
|
||||
_ -> pure True
|
||||
shouldRetry :: RetryStatus -> Bool -> IO Bool
|
||||
getConnectionStatus :: IO ConnectionStatus
|
||||
getConnectionStatus = do
|
||||
pgVersion <- P.use pool getPgVersion
|
||||
case pgVersion of
|
||||
Left e -> do
|
||||
let err = PgError False e
|
||||
hPutStrLn stderr . toS $ errorPayload err
|
||||
case checkIsFatal err of
|
||||
Just reason -> return $ FatalConnectionError reason
|
||||
Nothing -> return NotConnected
|
||||
|
||||
Right version ->
|
||||
if version < minimumPgVersion
|
||||
then return . FatalConnectionError $ "Cannot run in this PostgreSQL version, PostgREST needs at least " <> pgvName minimumPgVersion
|
||||
else return . Connected $ version
|
||||
|
||||
shouldRetry :: RetryStatus -> ConnectionStatus -> IO Bool
|
||||
shouldRetry rs isConnSucc = do
|
||||
delay <- pure $ fromMaybe 0 (rsPreviousDelay rs) `div` 1000000
|
||||
itShould <- pure $ not isConnSucc
|
||||
itShould <- pure $ NotConnected == isConnSucc
|
||||
when itShould $
|
||||
putStrLn $ "Attempting to reconnect to the database in " <> (show delay::Text) <> " seconds..."
|
||||
return itShould
|
||||
|
||||
|
||||
{-|
|
||||
This is where everything starts.
|
||||
-}
|
||||
|
||||
Reference in New Issue
Block a user