fix: ensure Listener connections are released
retryingListen function potentially leaks database connections. This patch ensures the connections are released in case of listen/notify errors.
This commit is contained in:
@@ -10,6 +10,10 @@ All notable changes to this project will be documented in this file. From versio
|
|||||||
- Add a `HINT` when the LISTEN channel stops working due to a PostgreSQL bug by @laurenceisla in #4581
|
- Add a `HINT` when the LISTEN channel stops working due to a PostgreSQL bug by @laurenceisla in #4581
|
||||||
- Add string slicing operator for `jwt-role-claim-key` by @taimoorzaeem in #4599
|
- Add string slicing operator for `jwt-role-claim-key` by @taimoorzaeem in #4599
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Ensure Listener connections are released by @mkleczek in #4614
|
||||||
|
|
||||||
## [14.3] - 2026-01-03
|
## [14.3] - 2026-01-03
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|||||||
+26
-16
@@ -1,3 +1,4 @@
|
|||||||
|
{-# LANGUAGE LambdaCase #-}
|
||||||
{-# LANGUAGE MultiWayIf #-}
|
{-# LANGUAGE MultiWayIf #-}
|
||||||
{-# LANGUAGE RecordWildCards #-}
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@ import PostgREST.Version (prettyVersion)
|
|||||||
import qualified PostgREST.AppState as AppState
|
import qualified PostgREST.AppState as AppState
|
||||||
import qualified PostgREST.Config as Config
|
import qualified PostgREST.Config as Config
|
||||||
|
|
||||||
|
import Data.Either.Combinators (whenRight)
|
||||||
import Protolude
|
import Protolude
|
||||||
|
|
||||||
-- | Starts the Listener in a thread
|
-- | Starts the Listener in a thread
|
||||||
@@ -49,25 +51,31 @@ retryingListen appState = do
|
|||||||
|
|
||||||
-- forkFinally allows to detect if the thread dies
|
-- forkFinally allows to detect if the thread dies
|
||||||
void . flip forkFinally handleFinally $ do
|
void . flip forkFinally handleFinally $ do
|
||||||
dbOrError <- SQL.acquire $ toUtf8 (Config.addTargetSessionAttrs $ Config.addFallbackAppName prettyVersion configDbUri)
|
-- Make sure we don't leak connections on errors
|
||||||
case dbOrError of
|
bracket
|
||||||
Right db -> do
|
-- acquire connection
|
||||||
SQL.listen db $ SQL.toPgIdentifier dbChannel
|
(SQL.acquire $ toUtf8 (Config.addTargetSessionAttrs $ Config.addFallbackAppName prettyVersion configDbUri))
|
||||||
AppState.putIsListenerOn appState True
|
-- release connection
|
||||||
|
(`whenRight` releaseConnection) $
|
||||||
|
-- use connection
|
||||||
|
\case
|
||||||
|
Right db -> do
|
||||||
|
SQL.listen db $ SQL.toPgIdentifier dbChannel
|
||||||
|
AppState.putIsListenerOn appState True
|
||||||
|
|
||||||
delay <- AppState.getNextListenerDelay appState
|
delay <- AppState.getNextListenerDelay appState
|
||||||
when (delay > 1) $ do -- if we did a retry
|
when (delay > 1) $ do -- if we did a retry
|
||||||
-- assume we lost notifications, refresh the schema cache
|
-- assume we lost notifications, refresh the schema cache
|
||||||
AppState.schemaCacheLoader appState
|
AppState.schemaCacheLoader appState
|
||||||
-- reset the delay
|
-- reset the delay
|
||||||
AppState.putNextListenerDelay appState 1
|
AppState.putNextListenerDelay appState 1
|
||||||
|
|
||||||
observer $ DBListenStart dbChannel
|
observer $ DBListenStart dbChannel
|
||||||
SQL.waitForNotifications handleNotification db
|
SQL.waitForNotifications handleNotification db
|
||||||
|
|
||||||
Left err -> do
|
Left err -> do
|
||||||
observer $ DBListenFail dbChannel (Left err)
|
observer $ DBListenFail dbChannel (Left err)
|
||||||
exitFailure
|
exitFailure
|
||||||
where
|
where
|
||||||
observer = AppState.getObserver appState
|
observer = AppState.getObserver appState
|
||||||
mainThreadId = AppState.getMainThreadId appState
|
mainThreadId = AppState.getMainThreadId appState
|
||||||
@@ -82,3 +90,5 @@ retryingListen appState = do
|
|||||||
|
|
||||||
cacheReloader =
|
cacheReloader =
|
||||||
AppState.schemaCacheLoader appState
|
AppState.schemaCacheLoader appState
|
||||||
|
|
||||||
|
releaseConnection = void . forkIO . handle (observer . DBListenerConnectionCleanupFail) . SQL.release
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ data Observation
|
|||||||
| DBListenBugHint -- https://github.com/PostgREST/postgrest/issues/3147
|
| DBListenBugHint -- https://github.com/PostgREST/postgrest/issues/3147
|
||||||
| DBListenerGotSCacheMsg ByteString
|
| DBListenerGotSCacheMsg ByteString
|
||||||
| DBListenerGotConfigMsg ByteString
|
| DBListenerGotConfigMsg ByteString
|
||||||
|
| DBListenerConnectionCleanupFail SomeException
|
||||||
| QueryObs MainQuery Status
|
| QueryObs MainQuery Status
|
||||||
| ConfigReadErrorObs SQL.UsageError
|
| ConfigReadErrorObs SQL.UsageError
|
||||||
| ConfigInvalidObs Text
|
| ConfigInvalidObs Text
|
||||||
@@ -122,6 +123,8 @@ observationMessage = \case
|
|||||||
"Received a schema cache reload message on the " <> show channel <> " channel"
|
"Received a schema cache reload message on the " <> show channel <> " channel"
|
||||||
DBListenerGotConfigMsg channel ->
|
DBListenerGotConfigMsg channel ->
|
||||||
"Received a config reload message on the " <> show channel <> " channel"
|
"Received a config reload message on the " <> show channel <> " channel"
|
||||||
|
DBListenerConnectionCleanupFail ex ->
|
||||||
|
"Failed during listener connection cleanup: " <> showOnSingleLine '\t' (show ex)
|
||||||
QueryObs{} ->
|
QueryObs{} ->
|
||||||
mempty -- TODO pending refactor: The logic for printing the query cannot be done here. Join the observationMessage function into observationLogger to avoid this mempty.
|
mempty -- TODO pending refactor: The logic for printing the query cannot be done here. Join the observationMessage function into observationLogger to avoid this mempty.
|
||||||
ConfigReadErrorObs usageErr ->
|
ConfigReadErrorObs usageErr ->
|
||||||
|
|||||||
Reference in New Issue
Block a user